本文是Bash用户手册的一个简要总结。

目录

基础概念

  • control operator:用于控制方法(分割命令):newline || && & ; ;; ;& ;;& | |& ( )
  • metacharacter:用于分割单词(word):| & ; ( ) < >
  • Shell执行命令的过程如下:
    1. 从文件或终端读入命令;
    2. 将输入分割成单词(word)和操作符(operator),这个步骤会应用转义规则(Qouting)。单词通过 metacharacter划分。同义展开(alias expansion)在这一步执行;
    3. 将词(token)解析为简单(simple)和复合(compound)命令;
    4. 执行各种展开(expansion)(展开顺序见下),将展开的词放入命令和参数中;
    5. 执行必要的重定向(redirection),从参数列表中去掉重定向操作符;
    6. 执行命令;
    7. 选择性的等待命令完成,获取退出状态。
  • 转义规则:
    • \转义下一个单词,\newline被认为行拼接,除非在'转义中,否则转义结果中不会出现换行(即\n);
    • '转义出现在引号内的所有字符;
    • "转义转义除$ ` \ !外所有字符。$ `保持全有展开含义,\仅当后跟$ ` " \ newline这些字符时才有转义语义,否则仍然输出反斜杠。若启用了历史展开,则!会展开,若!\转义,则不会展开,并且\不会移除(仍然输出\!);
    • ANSI-C转义,形如$'\n'通过ANSI C语义转义,如:
      • \nnn:8进制转义(最多3位);
      • \xHH:16进制转义(最多2位);
      • \uHHHH:Unicode转义(最多4位);
      • \UHHHHHHHH:Unicode转义(最多8位);
      • \cxcontrol-x转义。
  • ||&组成管道(pipeline),若在管道前加入!,则最终的exitcode被取反,如! ls(空格不能省略,否则被解析为历史展开),|&同时重定向stdoutstderr
  • ; & && ||组成列表(list),并由; & newline表示结束,其中&& ||优先级最高,; &其次。由&表示的命令在后台运行,若没有启用任务控制(job control),则进程的stdin会被重定向到/dev/null
  • ( list )会创建一个subshell,然后执行命令,{ list; }会在当前shell执行命令,注意空格和;不能省略;
  • coproc会创建一个coprocess;
  • parallelxargs类似,但是会并行执行命令;

基础语法

语法中的;大多可以被newline替换。

循环

until test-commands; do      # 循环直到test-commands为0
	consequent-commands;
done # exitcode是最后执行的命令的exitcode,如果没有任何语句执行则为0
while test-commands; do     # 循环直到test-commands不为0
	consequent-commands;
done # exitcode是最后执行的命令的exitcode,如果没有任何语句执行则为0
for name [ [in [words …] ] ; ] do  # in语句省略时,相当于in "$@"
	commands $name; 
done # exitcode是最后执行的命令的exitcode,如果没有任何语句执行则为0
for (( expr1 ; expr2 ; expr3 )) ; do  # 应该是bash特有语法
	commands; 
done # exitcode是最后执行的命令的exitcode,如果任何expr非法,返回false

条件

if test-commands; then
	consequent-commands;
elif more-test-commands; then
	more-consequents;
else
	alternate-consequents;
fi # exitcode是最后执行的命令的exitcode,如没有任何条件为真返回0
case word in 
	pattern1 | pattern2) command-list1 ;;  # ;;结尾若匹配直接返回
	pattern3 | pattern4) command-list2 ;&  # ;&结尾若匹配继续执行下一个command-list
	pattern5 | pattern6) command-list3 ;;& # ;;&结尾若匹配则继续匹配下一项,匹配成功则执行
	*) command-list ;;
esac # exitcode是执行的command-list的exitcode,若没有任何匹配则返回0
select name [in words …]; do # 构造一个menu供用户选择
	commands $name $REPLY; # $REPLY表示序号
	break;                 # break退出select环境,否则会循环执行select
done
(( expression ))  # bash算术表达式,若表达式非0,exitcode为0,否则exitcode为1
[[ expression ]]  # bash条件表达式

Bash条件表达式(Bash扩展)

若没有特殊指定,则文件会跟踪符号连接,在目标文件上操作,而不是符号链接本身。

语法含义
-a file若文件存在,返回真
-b file若文件存在并且是块设备,返回真
-c file若文件存在并且是字符设备,返回真
-d file若文件存在并且是目录,返回真
-e file若文件存在,返回真
-f file若文件存在并且是普通文件,返回真
-g file若文件存在并且set-group-id被设置,返回真
-h file若文件存在并且是符号链接,返回真
-k file若文件存在并且sticky被设置,返回真
-p file若文件存在并且是命名管道(FIFO),返回真
-r file若文件存在并且可读,返回真
-s file若文件存在并且大小大于0,返回真
-t fd若文件描述符打开并且指向终端,返回真
-u file若文件存在并且set-user-id被设置,返回真
-w file若文件存在并且可写,返回真
-x file若文件存在并且可执行,返回真
-G file若文件存在并且被当前egid拥有,返回真
-L file若文件存在并且是符号链接,返回真
-N file若文件存在并且自它上次被读取以来被修改过,返回真
-O file若文件存在并且被当前euid拥有,返回真
-S file若文件存在并且是socket,返回真
file1 -ef file2file1file2指向相同的设备和inode号,返回真
file1 -nt file2file1file2新(根据mtime)或者file1存在而file2不存在,返回真
file1 -ot file2file1file2老(根据mtime)或者file1不存在而file2存在,返回真
-o optname若shell选项optname打开,返回真
-v varname若变量存在,返回真
-R varname若变量存在,并且是一个引用(name reference),返回真
-z string若字符串长度为0,返回真
-n string string若字符串长度不为0,返回真
string1 == string2 string1 = string2若字符串相等,返回真。若在[[中使用,还可以进行模式匹配
string1 != string2若字符串不等,返回真
string1 < string2string1在字典序上排序比string2靠前,返回真
string1 > string2string1在字典序上排序比string2靠后,返回真
arg1 OP arg2OP可以是-eq -ne -lt -le -gt -ge。分别表示数字arg1arg2相等、不等、小于、小于等于、大于、大于等于

函数

在函数中使用local语句定义局部变量

name () compound-command [ redirections ]
# 命令必须以; & newline结尾
function name [()] compound-command [ redirections ]

特殊参数

参数含义
$*从第一个到最后一个参数,$*会展开为$1 $2 ..."$*"会展开$1c$2c...,其中c$IFS$
$@从第一个到最后一个参数,$@会展开为$1 $2 ..."$@"会展开为"$1" "$2" ...,若没有参数则$@会被移除
$#参数数量
$?最近执行的前台管道的exitcode
$-当前shell的选项标志(通过set设置)
$$当前shell的pid,在subshell中,$$展开为调用shell的pid
$!最近放入后台运行的job的pid
$0程序运行的名字(有疑点)
$_上一个执行的命令的最后一个参数(有疑点)

展开

Shell展开通过以下顺序进行:

  1. 括号展开(brace expansion);
  2. 波浪号展开(tilde expansion),参数和变量展开(parameter and variable expansion),算数展开(arithmetic expansion),命令替换(command substitution),进程替换(process substitution),展开从左到右进行;
  3. 单词分割(word splitting);
  4. 文件名展开(filename expansion,即wildcard);
  5. 转义移除(quote removal)。

各种展开语法:

展开类型
语法
括号展开a{b,c,d}e {x..y[..incr]} 注意,前后不能有空格,x y可以是数字或者单个字符
波浪号展开~展开为$HOME~+展开为$PWD~-展开为$OLDPWD~N ~+N ~-N展开为`dirs +/-N`
参数展开${parameter}
算数展开$(( expression ))
命令替换$(command) `command` 使用$()语法时,所有字符保持原有语义,没有任何特殊字符,使用反引号语法时,$ ` \有特殊语义,可以被\转义
进程替换<(list) >(list),注意尖括号与圆括号之间不能有空格
文件名展开* ? [...]

参数展开的格式:

  • ${parameter:-word}:若paramter没有设置,则展开word,否则展开parameter
  • ${parameter:=word}:若paramter没有设置,则将word展开后的值赋给parameter,然后展开;
  • ${parameter:?word}:若parameter没有设置,则将word展开后输出到stderr,若shell不是交互式的则退出;
  • ${parameter:+word}:若parameter没有设置,不替换任何值,否则替换为word的展开;
  • ${parameter:offset} ${parameter:offset:length}:取parameter的子字符串。offsetlength为负数时,表示从字符串末端向前数的第n个字符,offset为负数时,:-之间必须有空格;
  • ${!prefix*} ${!prefix@}:将变量名以prefix开头的变量展开,并通过$IFS连结;
  • ${!name[@]} ${!name[*]}:若name是数组(或字典),则展开为下标(或键值),如果不是,若name已定义,则展开为0,否则为null,如果使用@,则每个键值被展开为单独的"转义的词;
  • ${#parameter}:展开为数组长度;
  • ${parameter#word}:将parameter从左边开始最短匹配word的部分删除后展开;
  • ${parameter##word}:将parameter从左边开始最长匹配word的部分删除后展开;
  • ${parameter%word}:将parameter从右边开始最短匹配word的部分删除后展开;
  • ${parameter%%word}:将parameter从右边开始最长匹配word的部分删除后展开;
  • ${parameter/pattern/string}:将parameter匹配pattern的部分替换为string后展开。pattern可以以以下字符开始:
    • /:表示所有的匹配都要替换,一般情况下只替换一次;
    • #:匹配必须从开始位置匹配;
    • %:必须匹配结束位置。
  • ${parameter^pattern} ${parameter^^pattern} ${parameter,pattern} ${parameter,,pattern}:查找parameter中匹配pattern的部分,并转换大小写,其中^ ^^将小写转换为大写,, ,,将大写转换为小写,^ ,仅转换一次,^^ ,,转换全部。若pattern省略,则相当于匹配全部内容。

文件名展开时,若extglob选项打开(通过shopt命令),则还支持以下命令语法:

  • ?(pattern-list) 匹配0个或1个
  • *(pattern-list) 匹配0个或多个
  • +(pattern-list) 匹配1个或多个
  • @(pattern-list) 匹配1个
  • !(pattern-list) 匹配除pattern外的任何字符串

重定向

Bash使用一些在重定向中的特殊文件,如果操作系统支持这些文件,Bash会使用操作系统提供的文件,如果不支持,则Bash会模拟相应的文件:

文件含义
/dev/fd/fd复制fd
/dev/stdin标准输入
/dev/stdout标准输出
/dev/stderr标准错误
/dev/tcp/host/port重定向到tcp://host:port
/dev/udp/host/port重定向到udp://host:port

重定向语法

语法
含义
[n]<word重定向输入
[n]>[|]word重定向输出,如果有|,则无视noclobber设置
[n]>>word追加输出
&>word >&word >word 2>&1同时重定向stdoutstderr
&>>word >>word 2>&1同时追加stdoutstderr
[n]<<[-]word
    here-document
 delimiter
here document,如果word被转义(被'括起),delimiter是转义后的word,并且不会展开here-document中的任何内容,如果word没有被转义,则here-document会进行参数和变量展开、命令替换和算数展开,\必须用于转义\ $ `。如果使用<<-,则here-document中的前导tab会被移除。
[n]<<< wordhere stringword会经过括号展开、波浪号展开、参数和变量展开、命令替换、算数展开和转义移除,文件名展开和单词分割不会进行,结果中会追加一个新行。
[n]<&word [n]>&word复制文件描述符,如果word指定的描述符没有打开,则报错,如果word-,则文件描述符n被关闭。
[n]<&digit- [n]>&digit-移动文件描述符,即复制后关闭digit
[n]<>word打开word为文件描述符n用于读写,若n省略默认为0,若文件不存在,则会创建。

数组和字典

定义数组:

  • name[subscript]=value
  • declare -a name
  • name=(value1 value2 ... )

定义字典:

  • declare -A name

引用数组或字典中的值:

  • ${name[subscript]}

Shell builtin

  • 使用help cmd获取Shell builtin的帮助;
  • 使用type cmd可以查询cmd是否是Shell builtin;
  • trap:设置信号处理程序。例子:
trap "echo SIGINT" 2    # 为SIGINT设置处理程序
trap - 2                # 清除处理程序

Line Editing

Bash使用readline控制行编辑。readline会尝试读取shell变量INPUTRC中指定的配置文件,若变量不存在,读取~/.inputrc,若该文件不存在,读取/etc/inputrc

使用bind -P可以列出所有当前绑定的按键和操作,其中

  • \C-x表示ctrl-x
  • \M-x表示Meta-x,通常Meta键是alt键;
  • \C-\M-x表示同时按下ctrlalt
  • \ex表示Esc-x

使用bind -V列出当前设置。例如,使用bind "set completion-disabled on"可以禁用自动补全。