Chinaunix

标题: [找区别] cmd | cmd2 和 cmd > >(cmd2) 的区别 [打印本页]

作者: yjh777    时间: 2015-11-11 14:26
标题: [找区别] cmd | cmd2 和 cmd > >(cmd2) 的区别
如题:

  cmd | cmd2
  cmd > >(cmd2)

这两种写法基本功能一样的,但是主要区别在哪里呢?
我先说一个:
    cmd | cmd2  # 运行结束后 $? 是 cmd2 的返回值
    cmd > >(cmd2)  #运行结束后 $? 是 cmd 的返回值


大家看还有什么区别?
作者: lgfang    时间: 2015-11-11 15:03
本帖最后由 lgfang 于 2015-11-11 15:05 编辑

管道符是个顺序点(应该是这么叫吧。 cmd1 | cmd2 2>/dev/null 是吧cmd2的标准错误重定向。 cmd1 > >(cmd2) 2>/dev/null 重定向的是cmd1的stderr。同理,管道符可以 cmd1 | cmd2 | cmd3 ....,后者就不行了吧。

另外,管道符可以

1. cmd1 |& cmd2 把cmd1的stdout/stderr都重定向到cmd2的stdin
2. set -o pipefail 选项使得管道的返回值是管道中出错的进程的返回值(避免出现管道中的程序明明失败了但整个语句返回的是成功)
3. 管道符看起来简单明了啊。
作者: yjh777    时间: 2015-11-11 16:54
回复 2# lgfang


> 另外,管道符可以

> 1. cmd1 |& cmd2 把cmd1的stdout/stderr都重定向到cmd2的stdin
cmd1 &> >(cmd2)  也可以
> 2. set -o pipefail 选项使得管道的返回值是管道中出错的进程的返回值(避免出现管道中的程序明明失败了但整个语句返回的是成功)
> 3. 管道符看起来简单明了啊。


那么 cmd1 > >(cmd2) 的主要应用场景是什么呢?哪些情况下只能用这个 而 不能用管道呢?
作者: ziyunfei    时间: 2015-11-11 17:23
前者cmd是在子shell里运行的,后者不是,这才是关键的区别
作者: yjh777    时间: 2015-11-11 17:26
加一个: 产生的进程组不同:

cat | cat2   #管道线 所有进程在同一个进程组,同一个父进程 (这里默认讨论bash 其他shell不知道)
$ ps axfo tpgid,pgrp,ppid,pid,command| grep -v grep | grep cat -A 3
6130  6130 12712  6130          |   |   |   \_ cat
6130  6130 12712  6131          |   |   |   \_ cat2

cat > >(cat2)  #不再一个进程组, cat2是 cat的子/孙进程
$ ps axfo tpgid,pgrp,ppid,pid,command| grep -v grep | grep cat -A 3
6136  6136 12712  6136          |   |   |   \_ cat
6136 12712  6136  6137          |   |   |       \_ bash
6136 12712  6137  6138          |   |   |           \_ cat2
作者: yjh777    时间: 2015-11-11 17:53
本帖最后由 yjh777 于 2015-11-11 17:55 编辑
ziyunfei 发表于 2015-11-11 17:23
前者cmd是在子shell里运行的,后者不是,这才是关键的区别


可不可以说的更清楚一些,后者不在子shell里运行的话,是在哪里运行?有什么方法查看?

    (我是参照APUE里 stevens 大爷的方法 拷贝一个cat2 这样很方便查看进程之间的关系 见5楼)
作者: lgfang    时间: 2015-11-12 09:05
回复 4# ziyunfei




   
作者: lgfang    时间: 2015-11-12 09:05
回复 3# yjh777

对。我忘了
作者: reyleon    时间: 2015-11-12 09:49
第二种不好理解啊感觉, 用管子挺好的啊

cmd | cmd2, 这种也可以获取cmd的状态啊, 不是有一个 PIPESTATUS 数组嘛, 保存了每个管道命令的状态啊.  

echo {PIPESTATUS[0]} 就是cmd 的返回状态码, echo {PIPESTATUS[1]} 就是cmd2的返回状态码咯

cmd1 > >(cmd2) 这种没见过, 都没用过 解释下这命令咋回事啊.  

>() 生成一个文件描述符, 然后cmd1的输出重定向到那个文件描述符,然后那个文件描述符作为cmd2的标准输入么? 丢, 好绕.
作者: yjh777    时间: 2015-11-12 10:29
reyleon 发表于 2015-11-12 09:49
第二种不好理解啊感觉, 用管子挺好的啊

cmd | cmd2, 这种也可以获取cmd的状态啊, 不是有一个 PIPESTATUS ...

...
>() 生成一个文件描述符, 然后cmd1的输出重定向到那个文件描述符,然后那个文件描述符作为cmd2的标准输入么? 丢, 好绕.


是的就是这样的 直接就猜出来了 厉害,直接 echo >(sleep 1) 就能看出来 ;其实就是:
cmd > /dev/fd/63  然后在林一个进程组里  cmd2 < /dev/fd/63  
作者: yjh777    时间: 2015-11-12 10:57
本帖最后由 yjh777 于 2015-11-12 10:58 编辑

如果要把 命令行 做参数 eval 执行,
  又想保证 命令行 进程组的一致性 并且想对标准输出给 其他程序处理就  必须用  >  >()   方式了
  1. run() {
  2.         cmdline=$1
  3.         expected=${2:-0}
  4.         desc=${3:-$cmdline}

  5.         echo "[$(date +%T) $USER@ ${PWD}]# $command" | tee -a
  6.         eval "$cmdline" &> >(tee -a $STD_LOG)
  7.         ret=$?
  8.         [ $expected != - ] && {
  9.                 : check_return_code $expected $ret $desc
  10.         }

  11.         return $ret; fi
  12. }
复制代码
如果执行  run "tcpdump .... &"  # 把某个命令行放到后台执行,这时就看到区别了
    这时候如果函数里用的是 | tee -a , 程序就会停在那里
    如果用 &> (tee -a) 就没有问题




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2