博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

linux shell 重定向 减号-的作用; nohup

Posted on 2014-10-08 16:50  bw_0927  阅读(1086)  评论(0编辑  收藏  举报

http://eksliang.iteye.com/blog/2105677

 

cmd 2>&1 | tee log.1

 

管道命令在bash的连续处理程序中是相当重要的,尤其在使用到前一个命令的studout(标准输出)作为这次的stdin(标准输入)时,就显得太重要了,某些命令需要用到文件名,例如上篇文档的的切割命令(split)、还有tar(打包)命令等等!这时这个文件就承当studout或者stdin,这个时候这个studout或者stdin就可以用减号(-)来替代

 实例一:使用ls -al /将输出的信息中,没3行记录成一个文件

 
  1. [root@bogon bash]# ls -al / | split -l 3 - s  
  2. [root@bogon bash]# wc -l s*  
  3.    3 saa  
  4.    3 sab  
  5.    3 sac  
  6.    3 sad  
  7.    3 sae  
  8.    3 saf  
  9.    3 sag  
  10.    2 sah  
  11.   23 total  
  12. #一般来说,如果需要stdout(标准输出)/stdin(标准输入),但偏偏又没有文件,有的只是“-”时,那么那个“-”就会被当做stdout或者stout  

 

实例二:tar -cvf  tarName.tar ./bash 本来是这样的,看我怎么用“-”来替他文件的标准输出

 
  1. [root@bogon ~]# tar -cvf   ./bash  
  2. tar: Cowardly refusing to create an empty archive  
  3. Try `tar --help' or `tar --usage' for more information.  
  4. #这里报错了,没有办法,因为语法错误  
  5. [root@bogon ~]# tar -cvf  - ./bash   
  6. ./bash/  
  7. ./bash/sag  
  8. ./bash/saf  
  9. ./bash/sae  
  10. ./bash/aa.txt  
  11. ./bash/sab  
  12. ./bash/saa  
  13. ./bash/sad  
  14. ./bash/cc.txt  
  15. ./bash/bb.txt  
  16. ./bash/test.txt  
  17. .....  
  18. #看到了没有这个时候用”-“替代了本来输出到文件,而标准输出到了屏幕  

 

    实例三:综合实例

 
  1. [root@bogon ~]# tar -cvf - ./bash | tar -xvf -  
  2. ./bash/  
  3. ./bash/sag  
  4. ./bash/saf  
  5. ......!(省略)  
  6. ./bash/saf  
  7. ./bash/sah  
  8. ./bash/sac  
  9. tar: ./bash: file changed as we read it  
  10. ./bash/sae  
  11. .......!省略)  
  12. ./bash/sah  
  13. ./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会返回