http://eksliang.iteye.com/blog/2105677
cmd 2>&1 | tee log.1
管道命令在bash的连续处理程序中是相当重要的,尤其在使用到前一个命令的studout(标准输出)作为这次的stdin(标准输入)时,就显得太重要了,某些命令需要用到文件名,例如上篇文档的的切割命令(split)、还有tar(打包)命令等等!这时这个文件就承当studout或者stdin,这个时候这个studout或者stdin就可以用减号(-)来替代
实例一:使用ls -al /将输出的信息中,没3行记录成一个文件
- [root@bogon bash]# ls -al / | split -l 3 - s
- [root@bogon bash]# wc -l s*
- 3 saa
- 3 sab
- 3 sac
- 3 sad
- 3 sae
- 3 saf
- 3 sag
- 2 sah
- 23 total
- #一般来说,如果需要stdout(标准输出)/stdin(标准输入),但偏偏又没有文件,有的只是“-”时,那么那个“-”就会被当做stdout或者stout
实例二:tar -cvf tarName.tar ./bash 本来是这样的,看我怎么用“-”来替他文件的标准输出
- [root@bogon ~]# tar -cvf ./bash
- tar: Cowardly refusing to create an empty archive
- Try `tar --help' or `tar --usage' for more information.
- #这里报错了,没有办法,因为语法错误
- [root@bogon ~]# tar -cvf - ./bash
- ./bash/
- ./bash/sag
- ./bash/saf
- ./bash/sae
- ./bash/aa.txt
- ./bash/sab
- ./bash/saa
- ./bash/sad
- ./bash/cc.txt
- ./bash/bb.txt
- ./bash/test.txt
- .....
- #看到了没有这个时候用”-“替代了本来输出到文件,而标准输出到了屏幕
实例三:综合实例
- [root@bogon ~]# tar -cvf - ./bash | tar -xvf -
- ./bash/
- ./bash/sag
- ./bash/saf
- ......!(省略)
- ./bash/saf
- ./bash/sah
- ./bash/sac
- tar: ./bash: file changed as we read it
- ./bash/sae
- .......!省略)
- ./bash/sah
- ./bash/sac
上面这个例子是说我将./bash这个文件打包,但是打包的文件不是记录到文件,而是传送到标准输出(stdout);经过管道后,将tar -cvf - ./bash传给后面的tar -xvf -。后面这个“-”则是取用前面一个命令的stdout作为stdin,因此这里就不需要使用文件了,这是很常见的例子,因为我们写脚本的时候,就不要去写个临时文件了。
==================================
http://blog.csdn.net/ithomer/article/details/9288353
shell中可能经常能看到:echo log > /dev/null 2>&1
命令的结果可以通过%>的形式来定义输出
/dev/null :代表空设备文件
> :代表重定向到哪里,例如:echo "123" > /home/123.txt
1 :表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于"1>/dev/null"
2 :表示stderr标准错误
& :表示等同于的意思,2>&1,表示2的输出重定向等同于1
1 > /dev/null 2>&1 语句含义:
1 > /dev/null : 首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息。
2>&1 :接着,标准错误输出重定向(等同于)标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。
尖括号后面可以跟文件名,或者是&1, &2,分别表示重定向到标准输出和标准错误。
2> &1
1> &2
2> stderr.log
1> stdout.log
实例解析:
cmd >a 2>a 和 cmd >a 2>&1 为什么不同?
cmd >a 2>a :stdout和stderr都直接送往文件 a ,a文件会被打开两遍,由此导致stdout和stderr互相覆盖。
cmd >a 2>&1 :stdout直接送往文件a ,stderr是继承了FD1的管道之后,再被送往文件a 。a文件只被打开一遍,就是FD1将其打开。
两者的不同点在于:
cmd >a 2>a 相当于使用了FD1、FD2两个互相竞争使用文件 a 的管道;
cmd >a 2>&1 只使用了一个管道FD1,但已经包括了stdout和stderr。
从IO效率上来讲,cmd >a 2>&1的效率更高。
file > /dev/null 2>&1 </dev/null &
The "file" part may be a variable part
representing any command.
The >/dev/null redirects the standard output to /dev/null, throwing it away.
The "2>&1" part redirects the "standard error" output of the command to to the stardard output.
The "</dev/null" unattaches the console keyboard and uses "</dev/null" instead. This will allow the program to run in the background without being blocked.
make 2>&1 | tee log.1
https://blog.csdn.net/geekster/article/details/6657620
先说一下linux重定向:
0、1和2分别表示标准输入、标准输出和标准错误信息输出,可以用来指定需要重定向的标准输入或输出。
在一般使用时,默认的是标准输出,既1.当我们需要特殊用途时,可以使用其他标号。例如,将某个程序的错误信息输出到log文件中:./program 2>log。这样标准输出还是在屏幕上,但是错误信息会输出到log文件中。
另外,也可以实现0,1,2之间的重定向。2>&1:将错误信息重定向到标准输出。
Linux下还有一个特殊的文件/dev/null,它就像一个无底洞,所有重定向到它的信息都会消失得无影无踪。这一点非常有用,当我们不需要回显程序的所有信息时,就可以将输出重定向到/dev/null。
如果想要正常输出和错误信息都不显示,则要把标准输出和标准错误都重定向到/dev/null, 例如:
# ls 1>/dev/null 2>/dev/null
还有一种做法是将错误重定向到标准输出,然后再重定向到 /dev/null,例如:
# ls >/dev/null 2>&1
注意:此处的顺序不能更改,否则达不到想要的效果,此时先将标准输出重定向到 /dev/null,然后将标准错误重定向到标准输出,由于标准输出已经重定向到了/dev/null,因此标准错误也会重定向到/dev/null,于是一切静悄悄:-)
由于使用nohup时,会自动将输出写入nohup.out文件中,如果文件很大的话,nohup.out就会不停的增大,这是我们不希望看到的,因此,可以利用/dev/null来解决这个问题。
nohup ./program >/dev/null 2>log &
如果错误信息也不想要的话:
nohup ./program >/dev/null 2>&1 &
========================
https://blog.csdn.net/oneinmore/article/details/50073443
Linux系统下,使用默认用户root。远程target机器的主目录下有个脚本test.sh,可执行权限,内容只有一条命令:sleep 10
在本地机器上执行 ssh target "nohup ./test.sh &",结果ssh不立即退出,等test.sh执行完毕之后才退出。
一般我们使用nohup命令是为了在断开到某个服务器的ssh连接之后,之前执行的命令仍然正常地在服务器运行。
但是前面的现象其实与nohup命令没有什么关系,只是ssh本身的问题;
nohup其作用的前提是用户使用ssh登录到服务器上。
至于跟nohup扯上关系,我猜是因为在大家的印象中上面这种nohup命令的执行方式应该是立即退出的,结果反差太大,所以当作了一个特别问题。相关的说明可以参见:
https://en.wikipedia.org/wiki/Nohup
http://www.snailbook.com/faq/background-jobs.auto.html
http://www.openssh.com/faq.html#3.10
https://bugzilla.mindrot.org/show_bug.cgi?id=52
解决的方法是,手动在命令里面指定重定向,即上面的命令换成:ssh target "nohup ./test.sh >/dev/null 2>&1 &",然后就OK。
下面的分析表明了nohup命令与“ssh host "cmd"”方式的ssh命令没有任何关系(因为这种方式不会涉及SIGHUP),所以换成ssh target "./test.sh >/dev/null 2>&1 &" 就可以了。
分析:
一般处理ssh远程执行某个命令的任务,在远程目标机器上先建立一个sshd的子进程(父进程是最初始的sshd),然后由这个sshd进程启动一个bash进程(如果使用bash进程)来执行传递过来的命令。
针对这次任务建立的sshd进程和bash进程在文件描述符方面有一定关系:通常bash进程的0 1 2三个文件描述符通过管道与sshd的相应文件描述符联系起来。
这可以通过查找建立的sshd进程和bash进程在/proc文件系统下的相应进程的fd目录的详细情况。
ssh远程执行命令这种建立ssh连接的方式在ps -ef 中显示的sshd进程是有"sshd root@notty"标记。此sshd进程的命令可以通过命令“ps -ef | grep -v grep | grep 'sshd.*notty' | awk '{print $2}'”得到,而相关bash进程的PID可用$$获取。远程执行下面命令可以一步到位,得到比较结果:
ssh target "TMPSPID=\$(ps -ef | grep -v grep | grep -e 'sshd.*notty' | awk '{print \$2}');echo \$TMPSPID;ls -l /proc/\$TMPSPID/fd;echo \$\$;ls -l /proc/\$\$/fd"
如果远程执行的命令是后台执行,那么可以发现新启动的bash进程的父进程成了1,而输入即描述符0重定向到了/dev/null。
nohup是防止进程被SIGHUP信号中断,正常使用的时候也会进行一些重定向操作,即当标准输入/输出/错误等是终端的时候,会对它们进行重定向。
但是ssh远程执行命令时,这些条件都不满足,因为文件描述符0,1,2(正常情况下)都被重定向到管道了。所以远程执行nohup时不会进行相关重定向操作。
而当远程执行后台命令的时候,虽然标准输入被重定向到了/dev/null,但是标准输出和错误还是管道, 所以针对这次任务启动的sshd进程还不会结束。
所以执行远程命令时,还必须自己在命令行上重定向标准输出和标准错误才行。
对于上面的test.sh脚本,下面给出几种命令执行执行方式:
ssh target "./test.sh" # 等待命令完成后退出;本地Ctrl+C中断ssh会话,不会中断test.sh的执行(bash父进程变为1)(与登录终端执行命令而终端连接断开时的行为不一样)
ssh target "./test.sh &" # 等待命令完成后退出;本地Ctrl+C中断ssh会话,不会中断test.sh的执行(bash父进程本来就为1)
ssh target "nohup ./test.sh &" # 等待命令完成后退出;本地Ctrl+C中断ssh会话,不会中断test.sh的执行(bash父进程本来就为1)
ssh target "nohup ./test.sh >/dev/null 2>&1 &" # 启动test.sh执行后就会退出(bash父进程本来就为1)
ssh target "./test.sh >/dev/null 2>&1 &" # 启动test.sh执行后就会退出(bash父进程本来就为1),这也表明ssh不退出与nohup命令本身没有什么关系
实际上如先ssh登录target,执行./test.sh &,然后正常退出ssh(即exit命令),那么./test.sh这个脚本也不会终止,而且会将父进程换成1;如果不正常退出,而是直接关闭连接,那么会导致./test.sh任务终止。
http://singlecool.com/2017/06/11/ssh-nohup/
原因
ssh在执行远程的命令时,首先在远程的机器上建立一个sshd的进程,然后由这个sshd进程启动一个bash进程来执行传过来的命令。
bash进程的0,1,2三个fd通过管道与sshd的相应的文件描述符关联起来。
如果远程命令是后台执行会发现其父进程变为1,输入fd重定向到了/dev/null,但是输出和错误的fd是管道,与sshd进程关联,所以这次启动的sshd进程不会结束,必须重定向输出和错误fd。
对于命令:ssh target "for i in 1 2 3;do sh ./test.sh;done"
在targe机器上执行ps -ef | grep 'test\|sshd'
看到下面结果:
work 14766 1 0 12:42 ? 00:00:00 /bin/bash .test.sh
root 18185 2387 0 16:24 ? 00:00:00 sshd: work [priv]
work 18377 18185 0 16:24 ? 00:00:00 sshd: work@pts/0
work 18406 18377 0 16:24 pts/0 00:00:00 bash -c for i in 1 2 3;do sh ./test.sh;done
示例一:由此可以看到进程的递进关系:sshd –> bash -c –> bash。
所以本机执行ssh target "cmd"
要能够立刻返回要满足:sshd进程没有要等着结束的子进程,这一点靠&
来保证。其次,没有其他正在执行的进程与它有管道关系,这点靠重定向来保证。
因为重定向只对单个简单命令或单个复合命令有效,对于组合的命令需要放到{}中:ssh target "{ ./test.sh && ./test.sh; } >/dev/null 2>&1 &"
。
ssh target "for i in 1 2 3; do ./test.sh >/dev/null 2>&1 0</dev/null; done &"
ssh不会立即退出,test.sh挨个执行,bash-c是后台执行但是1和2 fd跟sshd有管道相连。
示例二:ssh target "for i in 1 2 3; do ./test.sh ; done >/dev/null 2>&1 &"
ssh立即退出,test.sh挨个执行,执行test.sh的bash由bash-c启动,bash-c进行了重定向,所以子bash继承了重定向。
示例三:ssh target "for i in 1 2 3; do ./test.sh & done >/dev/null 2>&1 &"
与上面的示例不同的是这里的test.sh不是顺序执行而是近似的同时执行。
====================================
补充:
感觉上面的分析还不是很到位,因为简单命令还不能够显示出真实情况,比如执行
ssh target "./test.sh"
在远程机器上执行“ps -ef | grep 'test\|notty'”命令,结果如下
root 35929 3306 0 19:20 ? 00:00:00 sshd: root@notty
root 35931 35929 0 19:20 ? 00:00:00 /bin/bash ./test.sh”
好像执行./test.sh的bash进程直接由显示的sshd进程创建,其实情况应该不是这样的。先执行一个稍复杂的命令:
ssh target "for w in a b c; do ./test.sh; done"
同样使用上面的查看命令可以看到如下结果:
root 36219 3306 0 19:29 ? 00:00:00 sshd: root@notty
root 36221 36219 0 19:29 ? 00:00:00 bash -c for w in a b c; do ./test.sh; done
root 36228 36221 0 19:29 ? 00:00:00 /bin/bash ./test.sh
这就表明了其实有两层进程关系,sshd ---- bash -c ----- bash,即sshd 先创建一个bash以bash -c的方式执行传递过来的作为命令的字符串,然后再由这个bash创建执行./test.sh脚本的子bash进程(这个可以创建多个)。而本地执行ssh host "cmd"形式命令要能迅速返回,必须满足的条件是:该命令对象的sshd进程(一般是sshd: root@notty),没有子进程需要等待结束(靠将第一个bash搞成后台进程,或者第一个bash会立即执行完命令自然退出——即它启动一些后台子进程), 而且没有其他进程与它有管道连接关系(靠重定向解决,在第一个bash处或者所有第二层bash处都可以)。简而言之,要ssh host "cmd"形式命令立即返回,在整个命令最后面添加“>/dev/null 2>&1 &”,是有保证的。注意,对于组合的命令, 可能需要放到{}中才行,比如“{ cmd; } >/dev/null 2>&1 &”这样的形式。这是因为重定向只对单个简单命令或单个复合命令有效。
下面通过一些实际例子的情况帮助大家认识(/dev/null也可以是某个本地文件):
ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null; done"
ssh不返回,./test.sh一个一个启动
ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null; done &"
ssh 不返回,./test.sh一个一个启动。 第一个bash(由sshd启动的bash -c)是后台执行的,但是文件描述符1和2还与sshd有管道连接,所以不返回
ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null; done >/dev/null 2>&1 &"
ssh立即返回,./tesh.sh一个接一个启动
ssh target "for w in a b c; do ./test.sh ; done >/dev/null 2>&1 &"
ssh立即返回,./test.sh一个接一个启动;由于执行./test.sh的bash是由第一个bash启动的,而第一个bash执行了重定向,所以该bash也继承了这些重定向,换言之这条命令与上条命令的效果一样,即内层的./test.sh无需重定向了
ssh target "for w in a b c; do ./test.sh & done >/dev/null 2>&1 &"
ssh立即退出,./test.sh全部启动,第一个bash也退出了
ssh target "for w in a b c; do ./test.sh & done >/dev/null 2>&1"
ssh立即退出(为什么?因为第一个bash启动三个./tesh.sh的bash进程后,退出了;而执行./test.sh的bash进程因为继承了父bash的文件描述符,所以没有管道与sshd连接,因此ssh退出
ssh target "for w in a b c; do ./test.sh & done "
ssh不返回,因为执行./test.sh与sshd还有管道连接
ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null & done"
ssh返回,因为第一个bash启动三个子bash之后结束,而子bash与sshd之间又没有管道上连接
ssh target "for w in a b c; do ./test.sh >/dev/null 2>&1 0</dev/null & done &"
同上,因为第一个bash启动所有子进程后会退出,此时将第一个bash作为后台进程已经意义不大
ssh target "./test.sh && ./test.sh >/dev/null 2>&1 &"
ssh不会返回
ssh target "{ ./test.sh && ./test.sh; } >/dev/null 2>&1 &"
ssh会返回