bash 和 Shell 艺术
操作系统环境
-
Ubuntu 20.04.3 LTS -
Kernel: 6.5.0-060500-generic -
GNU bash,版本 5.1.16(1)-release (x86_64-pc-linux-gnu)
管道的语法
┌──(linuxmi㉿linuxmi)-[~]
└─$ seq 10 | wc -l
10
因此,通过这样的方式, seq 10
的标准输出被连接到wc -l
的标准输入,因此seq 10
输出的1到10的10行将被读取到wc -l
中,并wc -l
输出的10将显示在终端上。command |& command2
使用 |&
时,command
的标准错误输出也通过管道连接到command2
的标准输入。这是2>&1 |
的简写形式。这个标准错误输出的隐式重定向是在命令指定的所有重定向之后执行的。如果使用 |
作为管道的分隔符,则仅将左侧命令的标准输出连接到右侧命令的标准输入。例如: 这样做的话, seq
会将1到10的10行写入标准输出,dd
读取这10行并将其直接写入标准输出,tail -n 5
从读取的10行中读取最后的5行并将其写入标准输出。在这种情况下, dd
会将关于读写的数据量等统计信息写入标准错误输出,并且这些信息会直接显示在终端上。如果使用 |&
作为管道分隔符,那么左侧命令的标准错误输出也会连接到右侧命令的标准输入。例如: ┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
seq 10 | dd |& tail -n 5
9
10
0+1 块记录
0+1 块记录
21 字节已复制,4.2766e-05 s,491 kB/s┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
seq 10 | dd |& tail -n 5
这样做的话, dd
写入标准错误输出的统计信息也会连接到tail -n 5
的标准输入。tail -n 5
会从标准输入中读取1到10的10行以及统计信息的全部内容,并将其中的最后5行写入标准输出。这个过程是 ┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
seq 10 | dd 2>&1 | tail -n 5
9
10
0+1 块记录
0+1 块记录
21 字节已复制,0.00715583 s,2.9 kB/s
这等效于: ! command | command2
除非启用了 pipefail
选项,否则管道的返回状态将成为最后一个命令的退出状态。如果在管道之前有感叹号!
,那么该管道的退出状态将是上述退出状态的逻辑否定。Unix系列操作系统的进程可以通过 exit(2)
系统调用等方式返回退出状态。Bash可以利用执行命令时的退出状态进行各种控制。此外,对于管道和复合命令,也定义了退出状态,并可以同样使用。管道的退出状态取决于shell选项 pipefail
的设置,但在默认设置下(pipefail
未启用),管道的最后(最右边)命令的退出状态将成为整个管道的退出状态。例如: ┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ seq 5 | grep 7; echo $?
1
这样做的话, seq 5
会写出1到5的5行,grep 7
会在其中搜索7,但由于没有匹配的行,它以退出状态1结束。整个管道的退出状态也是1。echo $?
用于显示刚刚执行的命令的退出状态,因此终端上将显示1。在这里, ┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ ! seq 5 | grep 7; echo $?
0
这样做的话,整个管道的退出状态将被逻辑否定,变为0。 复合命令的语法
在bash中,提供了用于组合多个命令以执行的语法,例如管道、列表、复合命令等。 列表(list)是指使用操作符 ;
、&
、&&
、||
分隔的一个或多个管道。这里所说的管道是指由 |
分隔的一个或多个命令的序列。因此,简单地写一个命令也是管道的一种。 例如: ┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ seq 5 | grep 3
3
┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ echo www.linuxmi.com
www.linuxmi.com
考虑到这两个管道,可以使用 seq 5 | grep 3, echo hoge
这个顺序,用四种不同的列表操作符将这两个管道排列成四种不同的列表。如果列表运算符是 &
,则seq 5 | grep 3
将在后台运行,而不等待其完成,然后立即执行echo linuxmi
。因此,两个管道的输出可能会交错。如果列表运算符是 ;
,则首先执行seq 5 | grep 3
,等待其完成,然后 (必然) 执行echo linuxmi
。如果列表运算符是 &&
,则首先执行seq 5 | grep 3
,只有在其退出状态为0时才执行echo linuxmi
。如果列表运算符是 ||
,则首先执行seq 5 | grep 3
,只有在其退出状态不为0时才执行echo linuxmi
。{ list; }
{ list; } list
仅在当前shell环境中执行。list
的结尾必须是换行符或分号。这称为组合命令(group command)。 返回状态是 list
的退出状态。请注意,与元字符(例如 (
和)
)不同,{
和}
是保留字,并且必须出现在被视为保留字的地方。由于它们不会被划分为单词,因此在{
和}
与列表之间必须用空格或shell元字符分隔。如前所述 管道是由使用 |
分隔的一个或多个命令组成的序列, 而列表是由使用;
,&
,&&
,||
之一的运算符分隔的一个或多个管道的组合。如果您想要将整个列表视为单个命令,将其嵌入到管道的元素中或使用重定向,您可以使用两种类型的复合命令来实现。 ┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ (seq 5 | grep 3 && echo linuxmi) | wc -l
2
┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ { seq 5 | grep 3 && echo linuxmi; } | wc -l
2
(list)
的形式的复合命令会在子shell(子进程)中执行列表。而{ list; }
的形式的复合命令会在当前shell进程中直接执行列表(没有fork的开销)。由于 ( )
是shell的元字符,所以前后不加空格也会被解释为单词分隔。而{ }
是shell的保留字,如果前后的单词无法分隔,则不加空格会导致语法错误。
┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ {seq 5 | grep 3 && echo linuxmi; } | wc -l
bash: 未预期的记号 "}" 附近有语法错误
# { 和 seq 无法进行单词分隔。┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com] └─$ { seq 5 | grep 3 && echo linuxmi;}| wc -l 2 # ; 和 | 都是用于单词分隔的元字符,所以即使紧挨着也没关系
引号的语法 |
、&
、;
、(
、)
、<
、>
、$
以及空格等字符在Shell中具有特殊意义,因此在表示原始字符本身时,可以使用引号来禁用特殊含义和功能。bash 中有三种引号:
在想要引号的字符前加上反斜杠 。
用单引号括起来(所有字符都失去特殊含义)。 用双引号括起来(除了 $
、`、!
之外的字符都失去特殊含义)。$'str' 如果在用 ASCII 文本编写的 Shell 脚本中表示换行符、退格符、转义字符等控制字符或 ASCII 范围之外的 Unicode 字符,仅仅使用引号来禁用特殊含义是不够的,还需要使用某种 ASCII 表示方法来表示这些字符。 可以使用 bash 的内建命令或外部命令,如 echo 或 printf,以及 bash 的命令替换功能,例如: ┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ touch "$(echo -e 'U1F363')"; ls
🍣 1 93139 linuxmi--- linuxmi.com linuxmi.py linuxmi.sh
┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ touch "$(printf 'U1F363')"; ls
🍣 1 93139 linuxmi--- linuxmi.com linuxmi.py linuxmi.sh
虽然也有类似下面这样的方法,但是由于命令在子shell中执行会带来一些额外的开销,并且代码会变得冗长。在这里,可以使用 $'str'
这种引号的语法:┌──(linuxmi㉿linuxmi)-[~/www.linuxmi.com]
└─$ touch $'U1F363'; ls
🍣 1 93139 linuxmi--- linuxmi.com linuxmi.py linuxmi.sh
这样的表达方式更为简洁,且无需执行子shell导致的额外开销。 总结
介绍的语法仅仅是bash语法的冰山一角。Bash还有许多有趣且实用的语法,如果你有兴趣的话,不妨阅读一下 man bash
。如果你对本文还有什么疑问与建议,请在Linux迷 www.linuxmi.com 的评论给我们留言,如果你觉得本文对你有所启发与帮助,请分享给你的朋友与同事。 LINUX迷公众号
感谢 Linux迷 www.linuxmi.com 的精彩分享。 更多精彩请点击关注以下视频号。 需要 Linux 精美艺术壁纸和免费编程及Linux 教程(PDF版)的朋友请加小编微信linuxgs(口令linuxmi)。 需要邀请码的也请加小编微信linuxgs(口令:邀请码)。 来自:Linux迷
链接:https://www.linuxmi.com/bash-shell.html关注我们 长按或扫描下面二维码关注 Linux公社
关注 Linux公社,添加“ 星标 ”
每天 获取 技术干货,让我们一起成长
合作联系:root@linuxidc.net
发表评论