Bash里的那些坑・pipe的坑
大家觉得这个代码会显示什么:
$ foo="Hello" | echo $foo
会显示Hello
吗?,答案是:不会。
情景如下图所示:
为什么会这样?因为管道操作符会开启一个新的Process。
我们再看看上面的命令,首先,我们设定foo
的值是Hello
,这个命令在它自己的process里面生效,因此这个foo
在它自己的process 里面被赋值。
然后我们使用 管道操作符 ,也就是|
,把前一个命令的stdout
交给后一个命令,前一个命令此时执行完成了,而|
后面的命令会在自己的新的process里面执行,这时bash
负责做的事情,就是给命令创建process。
后面这个命令是新的process ,并不继承前面已经结束掉的process的环境变量(因为和前一个已经结束的process不存在parent-child关系),所以自然不存在foo
这个变量。
所以我们学到的很重要一点:
- 管道操作符是把前一个命令的stdout交给后一个命令,并不继承环境变量
接下来看这个代码,大家觉得输出是什么?
$ foo="Hello, "; echo $foo | foo="World"; echo $foo
会是 Hello, world!
吗?执行结果如下:
为什么输出是Hello,
?我们仔细分析上面的代码,首先我们要知道,分号,也就是;
,就是用来把多行代码写到一行用的,代替「回车」的功能。
于是上面的代码就等于是:
foo="Hello, "
echo $foo | foo="World"
echo $foo
第一行,给foo
赋值。
第二行,向stdout输出Hello,
,通过 管道操作符 传给一个新的 process ,这个 process 执行的命令是给foo
赋值,然后这个命令的 process 退出,等于什么也没干。
第三行,向stdout打印foo
的值,Bash默认的stdout是屏幕输出,所以我们看到屏幕打印了Hello,
。
所以说,我们要给|
两边的命令加括号,才可以正确结合顺序。
下面是代码:
(foo="Hello, "; echo $foo) | (foo="World"; echo $foo)
大家觉得这回会正确输出Hello, world
吗?以下是命令执行结果:
为什么这回只输出World
?我们还是分析上面的代码:首先,前一条指令向stdout输出Hello,
,通过|
传给后面的命令,后面的命令没有管前一个命令给的Hello,
,向屏幕输出World
。
所以我们得让后一个命令接受并处理前一个命令的输出才可以,能够做这件事情的,是xargs
这个命令,它可以接受管道操作符给过来的前一个命令的输出。
下面是我们想要的命令:
(foo="Hello, "; echo $foo) | (xargs printf; foo="World"; echo $foo)
如上所示,我们把前一个命令的stdout用xargs
接收,交给printf
命令,打印。 printf
和echo
的区别就是默认不会换行,这样保证字符串在一行。下面是命令执行结果:
正确!
然后我们如果想明确地把前一个命令的输出放到一个变量里面使用,也可以使用xargs
来实现,下面是例子:
$ foo=hello; echo $foo | xargs -I OUTPUT echo OUTPUT
hello
如上所示,我们把之前的输出明确声明为 OUTPUT 。注意,在前面的变量加了引号,这其实不是变量,而是直接的「文本替换」,就是把所有OUTPUT
变成之前命令的输出内容。所以应该叫做「replace string」。
此外,还有坑:
$ foo=Hello; echo "foo"
foo
如果我们执行上面的命令,我们已经把bash shell的环境变量给污染了:
$ echo $foo
Hello
我们可能只想让这个foo
变量在我们的命令里面生效,而不想让它在 bash shell 里面生效。
一个原则:
- 如果你不想污染bash shell环境,就把你的命令和变量统统放进一个脚本文件里面再执行。
以上是一些使用bash时需要注意的一些地方。
- 上一篇 使用sdkman安装GraalVM
- 下一篇 HTTPS的双向认证(五)