Bash 是一个shell,或者说是命令行语言解释器,对于GNU操作系统。Bash是的Bourne-Again SHell的缩写。
它的基础,一个shell就是一个简单的宏处理器用于执行命令。词汇宏处理器意味着表示扩展文本和符号以创建更大表达式的功能。
一个unix shell不仅是一个命令行解释器也是一个编程语言。作为一个命令行解释器,一个shell提供了用户界面给丰富的GNU实用程序集。编程语言特性允许这些实用集去结合。文件包含创建的命令行,然后变成命令本身。
这些新命令与/bin等目录中的系统命令具有相同的状态,允许用户或组建立自定义环境来自动化他们的公共任务。
外壳可以交互使用,也可以不交互使用。在交互模式下,它们接受键盘输入。在非交互执行时,shell执行从文件中读取的命令。
shell允许同步和异步地执行GNU命令。shell在接受更多输入之前等待同步命令完成;当shell读取和执行其他命令时,异步命令继续与shell并行执行。重定向构造允许对这些命令的输入和输出进行细粒度控制。此外,shell允许控制命令环境的内容。
shell还提供了一小组内置命令(内置命令),这些命令实现了不可能或不方便通过单独的实用程序获得的功能。例如,cd、break、continue和exec不能在shell之外实现,因为它们直接操作shell本身。历史记录、getopt、kill或pwd内置项等可以在单独的实用程序中实现,但是作为内置命令使用它们更方便。所有shell内建都将在后面的部分中描述。
虽然执行命令是必要的,但是shell的大部分功能(和复杂性)都归功于它们的嵌入式编程语言。与任何高级语言一样,shell提供变量、流控制结构、引用和函数。
shell提供了专门用于交互使用的特性,而不是增强编程语言。这些交互特性包括作业控制、命令行编辑、命令历史记录和别名。本手册描述了这些特性。
这些定义将在本手册的其余部分中使用。
POSIX
一组基于Unix的开放系统标准。Bash主要关注POSIX 1003.1标准的Shell和实用程序部分。
blank
空白或者tab。
builtin
是由shell本身自己实现的,而不是存在于文件系统的某个地方的程序实现。
control operator
一个标志能够执行控制函数,这是一个新行或者以下的符号:‘||’, ‘&&’, ‘&’, ‘;’, ‘;;’, ‘;&’, ‘;;&’, ‘|’, ‘|&’, ‘(’, or ‘)’。
exit status
命令返回给调用者的值。该值被限制为8位,因此最大值为255。
field
一种文本单元,它是shell扩展的结果之一。展开之后,当执行命令时,结果字段用作命令名和参数。
filename
用于标识文件的字符串。
job
包含管道的一组流程,以及从管道派生的所有流程,它们都位于同一个流程组中。
job control
用户可以有选择地停止(挂起)和重新启动(恢复)进程执行的一种机制。
metacharacter
一种字符,当不加引号时,它分隔单词。元字符是空格、制表符、换行符或以下字符之一:’ | ‘、’ & ‘、’;’、’(’,’)’、’ < ‘或’ > '。
name
由字母、数字和下划线组成的单词,以字母或下划线开头。名称用作外壳变量和函数名称。也称为标识符。
operator
控制操作符或重定向操作符。有关重定向操作符的列表,请参见重定向。运算符包含至少一个未引用的元字符。
process group
具有相同进程组ID的相关进程的集合。
process group ID
表示进程组在其生存期内的唯一标识符。
reserved word
对外壳有特殊意义的词。大多数保留字引入了shell流控制结构,比如for和while。
return status
退出状态的同义词。
signal
一种机制,通过这种机制,内核可以通知进程系统中发生的事件。
special builtin
被POSIX标准分类为特殊的shell内置命令。
token
一串字符,被外壳看作一个单元。它不是一个单词就是一个操作符。
word
一串字符,被外壳当作一个单位。单词可能不包括未引用的元字符。
Bash中提供了所有Bourne shell内置命令,用于计算和引用的规则取自“标准”Unix shell的POSIX规范。
当shell读取输入时,它将执行一系列操作。如果输入指示了注释的开头,那么shell将忽略注释符号(’ # ')和该行的其余部分。
否则,粗略地说,shell读取输入并将输入划分为单词和操作符,使用引用规则来选择要分配各种单词和字符的含义。
然后,shell将这些令牌解析为命令和其他构造,删除特定单词或字符的特殊含义,扩展其他的,根据需要重定向输入和输出,执行指定的命令,等待命令的退出状态,并使该退出状态可供进一步检查或处理。
这是一个Shell操作的简述。它读取然后执行一个命令,基本上,shell干了以下这些事情。
引用是用来删除外壳中某些字符或单词的特殊含义。引用可用于禁用特殊字符的特殊处理,防止保留字被识别为保留字,并防止参数展开。
非引号反斜杠’ \ '是Bash转义字符。它保留下一个字符的文字值,换行符除外。如果出现\newline对,而反斜杠本身没有被引用,则\newline被视为行延续(即从输入流中删除并有效地忽略)。
用单引号括住字符(" ')可以保留引号内每个字符的文字值。单引号可能不会出现在单引号之间,即使前面有反斜杠。
将字符括在双引号中(’ ")可以保留引号中所有字符的文字值,除了’ $ ‘、’ "、’ \ ‘和在启用历史扩展时’ ! ‘之外。当shell处于POSIX模式(参见Bash POSIX模式)时,’ !在双引号中没有特殊含义,即使启用了历史扩展。字符“$”和“”在双引号中保留它们的特殊含义(参见Shell展开)。反斜杠只有在后跟以下字符之一时才保留其特殊含义:’ $ ‘、’ ‘、’ ‘、’ \ '或换行。在双引号中,将删除后跟其中一个字符的反斜杠。没有特殊含义的前一个字符的反斜杠保留不修改。双引号可以在双引号内加上反斜杠。如果启用,将执行历史记录扩展,除非“!”出现在双引号中是使用反斜杠转义的。‘前面的反斜杠!并没有移除。
特殊参数’ * ‘和’ @ '在双引号中有特殊意义(请参阅Shell参数展开)。
对$'string’形式的单词进行特殊处理。单词扩展为string,用反斜杠转义的字符替换为ANSI C标准指定的字符。反斜杠转义序列,如果存在,解码如下:
\a
alert (bell)
\b
backspace
在双引号的字符串前面加上美元符号(’ $ '),将根据当前的语言环境转换该字符串。如果当前语言环境是C或POSIX,则忽略美元符号。如果字符串被翻译和替换,则替换将被双引号括起。
一些系统使用LC_MESSAGES shell变量选择的消息目录。其他人则从TEXTDOMAIN shell变量的值创建消息目录的名称,可能添加后缀“.mo”。如果使用TEXTDOMAIN变量,可能需要将TEXTDOMAINDIR变量设置为消息目录文件的位置。还有一些以这种方式使用这两个变量:TEXTDOMAINDIR/LC_MESSAGES/LC_MESSAGES/TEXTDOMAIN.mo。
使用#。
一个简单的shell命令,比如echo ab c,由命令本身和参数组成,参数之间用空格分隔。
更复杂的shell命令由简单的命令组成,这些命令以各种方式排列在一起:在管道中,一个命令的输出成为第二个命令的输入,在循环或条件结构中,或者在其他一些分组中。
简单的命令是最常遇到的命令。它只是由空格分隔的单词序列,由shell的一个控制操作符终止(参见定义)。第一个单词通常指定要执行的命令,其余的单词是该命令的参数。
简单命令的返回状态(参见退出状态)是POSIX 1003.1 waitpid函数提供的退出状态,如果命令被信号n终止,则返回状态为128+n。
管道是由一个或多个命令组成的序列,这些命令由一个控制操作符’ | ‘或’ |& '分隔。
管道的格式是
[time [-p]] [!] command1 [ | or |& command2 ] …
**管道中的每个命令的输出都通过管道连接到下一个命令的输入。**也就是说,每个命令读取前一个命令的输出。此连接在命令指定的任何重定向之前执行。
如果使用’ |& ',command1的标准错误,除了它的标准输出,还通过管道连接到command2的标准输入;它是2>和1 |的简写。将标准错误隐式重定向到标准输出是在命令指定任何重定向之后执行的。
保留字time会在管道完成后打印计时统计信息。当前的统计数据包括运行时间(壁钟)以及命令执行所消耗的用户和系统时间。p选项将输出格式更改为POSIX指定的格式。当shell处于POSIX模式(请参阅Bash POSIX模式)时,如果下一个令牌以“-”开头,则它不将时间识别为保留字。可以将TIMEFORMAT变量设置为指定如何显示计时信息的格式字符串。有关可用格式的描述,请参见Bash变量。使用时间作为保留字允许对shell内建、shell函数和管道进行计时。外部时间命令不能很容易地对这些时间进行计时。
当shell处于POSIX模式(参见Bash POSIX模式)时,时间后面可能会跟着一个换行。在本例中,shell显示shell及其子程序所消耗的总用户和系统时间。时间格式变量可用于指定时间信息的格式。
如果管道不是异步执行的(参见列表),则shell将等待管道中的所有命令完成。
管道中的每个命令都在自己的子shell中执行,这是一个独立的进程(请参阅命令执行环境)。如果使用shopt内建启用了lastpipe选项(请参阅shopt内建),则管道的最后一个元素可能由shell进程运行。
管道的退出状态是管道中最后一个命令的退出状态,除非启用了pipefail选项(请参阅Set Builtin)。如果启用了pipefail,则管道的返回状态是最后一个(最右边的)以非零状态退出的命令的值,如果所有命令都成功退出,则返回零。如果保留字’ !在管道之前,退出状态是上面描述的对退出状态的逻辑否定。shell等待管道中的所有命令终止,然后返回一个值。
列表是由一个或多个管道组成的序列,这些管道由一个操作符’;’、’ & ‘、’ && ‘或’ || ‘分隔,并可选地由’;’、’ & '或换行符之一终止。
在这些列表操作符中,’ && ‘和’ || ‘具有相同的优先级,然后是’;‘和’ & ',它们具有相同的优先级。
列表中可能出现一个或多个换行序列来分隔命令,相当于分号。
如果一个命令被控制操作符’ & '终止,shell将在子shell中异步执行该命令。这称为在后台执行命令,这些命令称为异步命令。shell不等待命令完成,返回状态为0 (true)。当作业控制不活动时(请参阅作业控制),在没有任何显式重定向的情况下,异步命令的标准输入将从/dev/null重定向
以“;”分隔的命令按顺序执行;shell等待每个命令依次终止。返回状态是最后执行的命令的退出状态。
和和或列表分别是由控制操作符’ && ‘和’ || '分隔的一个或多个管道的序列。和或列表是用左联想性来执行的。
and列表具有这种形式
command1 && command2
如果命令1执行返回成功的话,那么执行命令2.
or列表有下面的形式
command1 || command2
命令1执行失败的时候,那么执行命令2.
命令列表的返回值是最后一个执行命令的返回值。
Bash支持以下循环结构。
注意,在命令语法描述中出现’;'时,可以用一行或多行换行。
until
until命令的语法是:
until test-commands; do consequent-commands; done
执行consequent-commands,只要test-commands的退出状态不为零。返回状态是在consequent-commands中执行的最后一个命令的退出状态,如果没有执行任何命令,则为零。
while
while命令的语法为:
while test-commands; do consequent-commands; done
执行结果命令,只要test-commands的退出状态为零。返回状态是在consequent-commands中执行的最后一个命令的退出状态,如果没有执行任何命令,则为零。
for
for命令的语法为:
for name [ [in [words …] ] ; ] do commands; done
展开words(请参阅Shell展开),并为结果列表中的每个成员执行一次命令,并将名称绑定到当前成员。如果没有“in words”,for命令对设置的每个位置参数执行一次命令,就好像已经指定了“$@”中的“in”(请参阅特殊参数)。
返回状态是执行的最后一个命令的退出状态。如果单词展开中没有项,则不执行任何命令,返回状态为零。
还支持for命令的另一种形式:
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done
首先,根据下面描述的规则计算算术表达式expr1(参见Shell算术)。然后反复计算算术表达式expr2,直到它的值为零。每次expr2计算为非零值时,都会执行命令并计算算术表达式expr3。如果省略任何表达式,它的行为就好像它的值是1一样。返回值是正在执行的命令中的最后一个命令的退出状态,如果表达式无效,返回值为false。
break和continue内建函数(参见Bourne Shell内建函数)可用于控制循环执行。
if
if test-commands; then
consequent-commands;
[elif more-test-commands; then
more-consequents;]
[else alternate-consequents;]
fi
执行test-commands列表,如果返回状态为零,则执行consequent-commands列表。如果test-commands返回非零状态,则依次执行每个elif列表,如果其退出状态为零,则执行相应的更多结果,并完成命令。如果’ else alternative - resulents '存在,并且final If或elif子句中的final命令具有非零退出状态,则执行alternative - resulents。返回状态是执行的最后一个命令的退出状态,如果没有测试为真,则为零。
case
语法:
case word in
[ [(] pattern [| pattern]…) command-list ;;]…
esac
case将有选择地执行与匹配word的第一个模式对应的命令列表。匹配根据模式匹配中描述的规则执行。如果启用了nocasematch shell选项(请参阅shopt内置项中的shopt描述),则执行匹配时不考虑字母字符的大小写。’ | ‘用于分隔多个模式,’)'操作符终止模式列表。模式列表和相关联的命令列表称为子句。
每个从句必须以“;;”、“;&”或“;;&”结尾。在尝试匹配之前,单词将经历波浪展开、参数展开、命令替换、算术展开和引号删除(请参阅Shell参数展开)。每个模式都经历tilde展开、参数展开、命令替换和算术展开。
可能有任意数量的case子句,每个子句都以“;;”、“;&”或“;;&”结尾。匹配的第一个模式确定要执行的命令列表。使用’ * '作为定义默认情况的最终模式是一个常见的习惯用法,因为该模式总是匹配的。
下面是一个在脚本中使用case的例子,可以用来描述动物的一个有趣的特征:
echo -n "Enter the name of an animal: "
read ANIMAL
echo -n "The $ANIMAL has "
case $ANIMAL in
horse | dog | cat) echo -n "four";;
man | kangaroo ) echo -n "two";;
*) echo -n "an unknown number of";;
esac
echo " legs."
如果使用’;;‘操作符,则在第一个模式匹配之后不会尝试后续匹配。使用’;& ‘代替’;;'会导致执行继续执行与下一个子句关联的命令列表(如果有的话)。使用“;;&”代替“;;”将使shell测试下一个子句中的模式(如果有的话),并在匹配成功时执行任何关联的命令列表。
如果没有匹配模式,返回状态为零。否则,返回状态就是执行的命令列表的退出状态。
select
select构造允许简单地生成菜单。它的语法几乎与for命令相同:
select name [in words …]; do commands; done
将展开后面的单词列表,生成项目列表。扩展后的单词集打印在标准错误输出流上,每个单词前面都有一个数字。如果省略了“in words”,则打印位置参数,就像指定了“$@”中的“in”一样。然后显示PS3提示符,并从标准输入中读取一行。如果行由与显示的单词之一对应的数字组成,则name的值设置为该单词。如果行为空,则再次显示单词和提示符。如果读取EOF, select命令将完成。任何其他读取值都会将name设置为null。读取的行保存在变量REPLY中。
每次选择之后都会执行这些命令,直到执行break命令,此时select命令就完成了。
下面的示例允许用户从当前目录中选择文件名,并显示所选文件的名称和索引。
select fname in *;
do
echo you picked $fname \($REPLY\)
break;
done
(( expression ))
(( expression ))
算术表达式根据下面描述的规则求值(参见Shell算术)。如果表达式的值非零,则返回状态为0;否则返回状态为1。等同于
let "expression"
有关let内置函数的完整描述,请参见Bash内置函数。
[[…]]
[[ expression ]]
根据条件表达式表达式的计算结果返回0或1的状态。表达式由Bash条件表达式中描述的基本形式组成。[[和]]之间的单词不执行分词和扩展文件名;执行tilde展开、参数和变量展开、算术展开、命令替换、流程替换和删除引用。条件运算符如’ -f ‘必须不加引号才能被识别为初等运算符。
当与[[一起使用时,’ < ‘和’ > ‘操作符使用当前区域设置按字典顺序排序。
当’ == ‘和’ != ‘操作符,操作符右边的字符串被认为是一个模式,并根据模式匹配中描述的规则进行匹配,就像启用了extglob shell选项一样。’ =’ 操作符与’ == ‘相同。如果启用了nocasematch shell选项(请参阅shopt内置项中的shopt描述),则执行匹配时不考虑字母字符的大小写。如果字符串匹配(’ == ‘)或不匹配(’ != ‘)模式,返回值为0,否则返回值为1。可以引用模式的任何部分,以强制将引用部分匹配为字符串。
可以使用另一个二进制操作符’ =~ ‘,其优先级与’ == ‘和’ != '相同。当使用它时,操作符右边的字符串被认为是POSIX扩展正则表达式,并相应地进行匹配(如regex3中所示)。如果字符串与模式匹配,返回值为0,否则返回值为1。如果正则表达式在语法上不正确,则条件表达式的返回值为2。如果启用了nocasematch shell选项(请参阅shopt内置项中的shopt描述),则执行匹配时不考虑字母字符的大小写。可以引用模式的任何部分,以强制将引用部分匹配为字符串。正则表达式中的括号表达式必须小心处理,因为普通的引号字符在括号之间失去了它们的含义。如果模式存储在shell变量中,引用变量展开将强制将整个模式匹配为字符串。正则表达式中的圆括号子表达式匹配的子字符串保存在数组变量BASH_REMATCH中。索引为0的BASH_REMATCH元素是匹配整个正则表达式的字符串的一部分。BASH_REMATCH with index n的元素是匹配第n个子表达式的字符串的一部分。
例如,如果值中有一个字符序列,其中包括0个空格字符,0个或1个“a”实例,那么a“b”将匹配一行(存储在shell变量行中):
[[ $line =~ [[:space:]]*?(a)b ]]
这意味着像’ aab ‘和’ aaaaaab ‘这样的值将匹配,在其值的任何位置包含’ b '的行也将匹配.
将正则表达式存储在shell变量中通常是一种有用的方法,可以避免引用shell特有的字符时出现的问题。有时很难在不使用引号的情况下按字面意思指定正则表达式,或者在注意shell的引号删除的同时跟踪正则表达式使用的引号。使用shell变量来存储模式可以减少这些问题。例如,下面的内容等价于上面的内容:
pattern=’[[:space:]]*?(a)b’
[[ $line =~ $pattern ]]
如果要匹配正则表达式语法中特殊的字符,必须引用该字符以删除其特殊含义。这意味着在模式’ xxx中。三种“,”.匹配字符串中的任何字符(其通常的正则表达式含义),但模式是“xxx.txt"‘它只能匹配一个文字’ . '。Shell程序员应该特别注意反斜杠,因为Shell和正则表达式都使用反斜杠来删除下面字符中的特殊含义。以下两组命令并不等同:
pattern='\.'
[[ . =~ $pattern ]]
[[ . =~ \. ]]
[[ . =~ "$pattern" ]]
[[ . =~ '\.' ]]
前两项匹配将成功,但后两项不成功,因为在后两项中,反斜杠将是要匹配的模式的一部分。在前两个例子中,反斜杠去掉了’中的特殊含义。如果第一个例子中的字符串不是’,例如“a”,则该模式将不匹配,因为引用了“。在图案中失去了与任何单个字符匹配的特殊含义。
表达式可以使用以下操作符组合,按优先级递减排列:(比较简单)
( expression )
! expression
expression1 && expression2
expression1 || expression2
如果expression1的值足以确定整个条件表达式的返回值,那么&&和||操作符不计算expression2。
Bash提供了两种方法来将要作为一个单元执行的命令列表分组。当对命令进行分组时,可以对整个命令列表应用重定向。例如,列表中所有命令的输出可以重定向到一个流。
()
(list)
在括号之间放置命令列表将创建子shell环境(请参阅命令执行环境),列表中的每个命令将在该子shell中执行。由于列表是在子shell中执行的,所以变量赋值在子shell完成后不会保持有效。
{}
{ list; }
在花括号之间放置命令列表会导致该列表在当前shell上下文中执行。没有创建子shell。下面的列表需要分号(或换行)。
除了创建子shell之外,由于历史原因,这两个构造之间还有细微的差别。大括号是保留字,因此必须用空格或其他shell元字符将它们与列表分隔开。圆括号是操作符,并且被shell识别为单独的标记,即使它们没有被空格从列表中分隔出来。
这两个构造的退出状态都是list的退出状态。
coprocess是一个shell命令,前面有coproc保留字。协进程在子shell中异步执行,就像命令已经用“&”控制操作符终止一样,在执行的shell和协进程之间建立
了一个双向管道。
格式:
coproc [NAME] command [redirections]
这将创建一个名为NAME的协进程。如果没有提供名称,默认名称是COPROC。如果命令是简单命令,则不能提供名称(参见简单命令);否则,它被解释为简单命令的第一个单词。
当执行协进程时,shell在执行shell的上下文中创建一个名为NAME的数组变量(参见数组)。命令的标准输出通过管道连接到执行shell中的文件描述符,并将该文件描述符指定为 NAME[0]。命令的标准输入通过管道连接到执行shell中的文件描述符,并将该文件描述符指定为 NAME[1]。此管道是在命令指定的任何重定向之前建立的(参见重定向)。文件描述符可以用作shell命令的参数,并使用标准的word扩展重定向。除了用于执行命令和处理替换的文件之外,文件描述符在子shell中不可用。
派生用于执行协进程的shell的进程ID作为变量NAME_PID的值可用。wait builtin命令可用于等待协进程终止。
由于coprocess是作为异步命令创建的,所以coproc命令总是返回成功。协进程的返回状态是命令的退出状态。
有一些方法可以并行运行Bash中没有内置的命令。GNU Parallel就是这样一个工具。
GNU Parallel,顾名思义,可以用来并行地构建和运行命令。您可以使用不同的参数运行相同的命令,无论是文件名、用户名、主机名还是从文件中读取的行。GNU Parallel提供了对许多最常见操作(输入行、输入行的不同部分、指定输入源的不同方法,等等)的简写引用。Parallel可以将xargs或输入源中的命令替换为Bash的几个不同实例。
有关完整的描述,请参阅GNU并行文档。几个例子应该对它的使用做一个简单的介绍。
例如,很容易替换xargs来gzip当前目录及其子目录中的所有html文件:
find . -type f -name '*.html' -print | parallel gzip
如果需要保护文件名中的特殊字符,如换行符,请使用find的-print0 选项和parallel的-0选项。
当文件数量过大,无法通过一次mv调用处理时,可以使用Parallel从当前目录移动文件: