输出的传递
shell脚本最棒的特性之一就是可以将多个命令组合起来生成输出
一个命令的输出可以作为另一个命令的输入,而这个命令的输出又会传递至下一个命令,以此类推。
这种命令组合的输出可以被存储在变量中。
1 管道
1.1 命令的组合
$ ls | cat -n > out.txt
ls (列出当前目录内容)的输出被传给 cat -n ,后者为通过 stdin 所接收到的输入内容加上行号,然后将输出重定向到文件out.txt
2 子shell
子shell一般有两种实现方式
- $()
- ``
2.1 利用子shell生成独立的进程
子shell本身就是独立的进程。可以使用 () 操作符来定义一个子shell
$> pwd
$> (cd /bin; ls)
$> pwd
当命令在子shell中执行时,不会对当前shell造成任何影响;所有的改变仅限于该子shell内。
2.2 引用子shell保留空格和换行符
为了保留输出的空格和换行符( \n ),必须使用双引号
$ cat text.txt
1
2
3
$ out=$(cat text.txt)
$ echo $out
1 2 3 #丢失了换行符
$ out="$(cat text.txt)"
$ echo $out
1
2
3 #没有丢失换行符
2.2.1 依然不保留换行符的思考
先用set | grep out 你会看到两个out是这样保存的:
out=$'1\n2\n3'
$'...'表示里面反斜杠开头是转义字符,这种字符串也叫ANSI C like strings,和下面这个是有区别的:
another='1\n2\n3'
echo $'1\n2\n3' 就能得到你想要的结果。
也就是说,变量里实际是有换行符的,只是传给echo的时候做了些变换。
bash把变量展开之后,会根据IFS(Internal Field Separator)把输入划分成多个单独的单词(Word splitting),除了在双引号里,原输入里的IFS都换成空格,连续空格再缩成1个空格。默认的IFS里有空格、换行和Tab。
所以输入echo $out,bash处理是这样的:
echo<空格>$out
--展开out-->
echo<空格>1<换行>2<换行>3
--根据IFS分词-->
echo<空格>1<空格>2<空格>3
所以用1、2、3作为参数调用echo。
这时候只要加上双引号阻止分词就可以了:
echo<空格>"$out"
--展开out-->
echo<空格>"1<换行>2<换行>3"
--根据IFS分词,双引号里不分词-->
echo<空格>"1<换行>2<换行>3"
1<换行>2<换行>3 被当作参数传给echo,然后就是你想要的结果了。
也可以把IFS设成其他字符,例如IFS=,,然后echo out"的结果是一样的,因为输入里没有IFS,不会被分词。