
_START_ARTICLE_
Bash
_START_SECTION_
历史
_START_PARAGRAPH_
由于理查德·斯托曼对于之前一位开发者的进度不满，布莱恩·福克斯从1988年1月10日开始开发Bash。斯托曼和自由软件基金会希望到一个能够运行已有的shell脚本的自由软件。他们把这看作是建成一个基于BSD和GNU的完全自由的操作系统的战略的重要部分。这是他们自己注资的几个项目之一。福克斯作为自由软件基金会的雇员承担这项工作。1989年6月8日，福克斯发布Bash的beta版本，版本号为.99。在福克斯离开于1992年中期到1994年中期的某个时候离开自由软件基金会之前，他一直担任Bash的主要维护者。之后，他的工作被传递给另一个早期贡献者，切特·雷米（Chet Ramey）。_NEWLINE_从那时起，在Linux用户当中sh在很大度上成为最流行的shell，并成为许多Linux发行版默认的交互式shell（不过Almquist shell可能是默认的脚本shell）。在苹果公司的OS X 操作系统上也是如此。Bash 也被移植到Microsoft Windows（通过Cygwin和MinGW）。通过DJGPP项目，Bash被移植到了DOS。通过许多终端模拟软件，Bash被移植到Novell NetWare和Android。微软在2016年的Build大会上宣布，Windows 10 添加一个Linux子系统，完全支持Bash和其他Ubuntu下的二进制程序。_NEWLINE_2014年9月24日，Stephane Chazelas，一位工作于英国，致力于Unix/Linux和网络通信方面的专家，发现Bash的一个安全漏洞。这个漏洞被命名为Shellshock，并被分配编号CVE-2014-6271、CVE-2014-6277、CVE-2014-7169。这个漏洞非常严重，因为使用Bash的CGI脚本会变得脆弱，使得攻击者可以执行任意的代码。这个漏洞与Bash通过环境变量把函数定义传递给shell子进程的方式有关。
_START_SECTION_
语法与特性
_START_PARAGRAPH_
bash的命令语法是Bourne shell命令语法的超集。数量庞大的Bourne shell脚本大多不经修改即可以在bash中执行，只有那些引用了Bourne特殊变量或使用了Bourne的内置命令的脚本才需要修改。bash的命令语法很多来自Korn shell（ksh）和C shell（csh），例如命令行编辑，命令历史，目录栈，$RANDOM和$PPID变量，以及POSIX的命令置换语法：$(...)。作为一个交互式的shell，按下TAB键即可自动补全已部分输入的程序名，文件名，变量名等等。_NEWLINE_使用'function'关键字时，Bash的函数声明与Bourne/Korn/POSIX脚本不兼容（Korn shell 有同样的问题）。不过Bash也接受Bourne/Korn/POSIX的函数声明语法。因为许多不同，Bash脚本很少能在Bourne或Korn解释器中运行，除非编写脚本时刻意保持兼容性。然而，随着Linux的普及，这种方式正变得越来越少。不过在POSIX模式下，Bash更加符合POSIX。_NEWLINE_bash的语法针对Bourne shell的不足做了很多扩展。其中的一些列举在这里。
_START_SECTION_
花括号扩展
_START_PARAGRAPH_
花括号扩展是一个从C shell借鉴而来的特性，它产生一系列指定的字符串（按照原先从左到右的顺序）。这些字符串不需要是已经存在的文件。_NEWLINE_$ echo a{p,c,d,b}e_NEWLINE_ape ace ade abe_NEWLINE_$ echo {a,b,c}{d,e,f}_NEWLINE_ad ae af bd be bf cd ce cf_NEWLINE_花括号扩展不应该被用在可移植的shell脚本中，因为Bourne shell产生的结果不同。_NEWLINE_#! /bin/sh# 传统的shell并不产生相同结果echo a{p,c,d,b}e # a{p,c,d,b}e_NEWLINE_当花括号扩展和通配符一起使用时，花括号扩展首先被解析，然后正常解析通配符。因此，可以用这种方法获得当前目录的一系列JPEG和PEG文件。_NEWLINE_ls *.{jpg,jpeg,png}    # 首先扩展为*.jpg *.jpeg *.png，然后解析通配符echo *.{png,jp{e,}g}   # echo显示扩展结果；花括号扩展可以嵌套。_NEWLINE_除了列举备选项，还可以用“..”在花括号扩展中指定字符或数字范围。较新的Bash版本接受一个整数作为第三个参数，指定增量。_NEWLINE_$ echo {1..10}1 2 3 4 5 6 7 8 9 10_NEWLINE_$ echo file{1..4}.txt_NEWLINE_file1.txt file2.txt file3.txt file4.txt_NEWLINE_$ echo {a..e}_NEWLINE_a b c d e_NEWLINE_$ echo {1..10..3}1 4 7 10_NEWLINE_$ echo {a..j..3}_NEWLINE_a d g j_NEWLINE_当花括号扩展和变量扩展一起使用时，变量扩展解析于花括号扩展之后。有时有必要使用内置的eval函数_NEWLINE_$ start=1; end=10_NEWLINE_$ echo {$start..$end}  # 由于解析顺序，无法得到想要的结果{1..10}_NEWLINE_$ eval echo {$start..$end} # 首先进行变量扩展的解析1 2 3 4 5 6 7 8 9 10
_START_SECTION_
使用整数
_START_PARAGRAPH_
与Bourne shell不同的是bash不用另外生成进程即能进行整数运算。bash使用((...))命令和$[...]变量语法来达到这个目的：_NEWLINE_ VAR=55             # 将整数55赋值给变量VAR_NEWLINE_ ((VAR = VAR + 1))  # 变量VAR加1。注意这里没有'$'_NEWLINE_ ((++VAR))          # 另一种方法给VAR加1。使用C语言风格的前缀自增_NEWLINE_ ((VAR++))          # 另一种方法给VAR加1。使用C语言风格的后缀自增_NEWLINE_ echo $((VAR * 22)) # VAR乘以22并将结果送入命令_NEWLINE_ echo $[VAR * 22]   # 同上，但为过时用法_NEWLINE_((...))命令可以用于条件语句，因为它的退出状态是0或者非0（大多数情况下是1），可以用于是与非的条件判断：_NEWLINE_ if((VAR == Y * 3 + X * 2))_NEWLINE_ then_NEWLINE_         echo Yes_NEWLINE_ fi_NEWLINE__NEWLINE_ ((Z > 23)) && echo Yes_NEWLINE_((...))命令支持下列比较操作符：'==', '!=', '>', '<', '>='，和'<='。_NEWLINE_bash不能在自身进程内进行浮点数运算。当前有这个能力的unix shell只有Korn shell和Z shell。
_START_SECTION_
输入输出重定向
_START_PARAGRAPH_
bash拥有传统Bourne shell缺乏的I/O重定向语法。bash可以同时重定向标准输出和标准错误，这需要使用下面的语法：_NEWLINE_ command &> file_NEWLINE_这比等价的Bourne shell语法"command > file 2>&1"来的简单。2.05b版本以后，bash可以用下列语法重定向标准输入至字符串（称为here string）：_NEWLINE_ command <<< "string to be read as standard input"_NEWLINE_如果字符串包括空格就需要用引号包裹字符串。_NEWLINE_例子:_NEWLINE_重定向标准输出至文件，写数据，关闭文件，重置标准输出。_NEWLINE_ # 生成标准输出（文件描述符1）的拷贝文件描述符6_NEWLINE_ exec 6>&1_NEWLINE_ # 打开文件"test.data"以供写入_NEWLINE_ exec 1>test.data_NEWLINE_ # 产生一些内容_NEWLINE_ echo "data:data:data"_NEWLINE_ # 关闭文件"test.data"_NEWLINE_ exec 1>&-_NEWLINE_ # 使标准输出指向FD 6（重置标准输出）_NEWLINE_ exec 1>&6_NEWLINE_ # 关闭FD6_NEWLINE_ exec 6>&-_NEWLINE_打开及关闭文件_NEWLINE_ # 打开文件test.data以供读取_NEWLINE_ exec 6<test.data_NEWLINE_ # 读文件直到文件尾_NEWLINE_ while read -u 6 dta_NEWLINE_ do_NEWLINE_   echo "$dta"_NEWLINE_ done_NEWLINE_ # 关闭文件test.data_NEWLINE_ exec 6<&-_NEWLINE_抓取外部命令的输出_NEWLINE_  # 运行'find'并且将结果存于VAR_NEWLINE_  # 搜索以"h"结尾的文件名_NEWLINE_  VAR=$(find . -name "*h")
_START_SECTION_
进程内的正则表达式
_START_PARAGRAPH_
bash 3.0支持进程内的正则表达式，使用下面的语法：_NEWLINE_ [[ string =~ regex ]]_NEWLINE_正则表达式语法同regex(7) man page所描述的一致。正则表达式匹配字符串时上述命令的退出状态为0，不匹配为1。正则表达式中用圆括号括起的子表达式可以访问shell变量BASH_REMATCH，如下：_NEWLINE_ if [[ abcfoobarbletch =~ 'foo(bar)bl(.*)' ]]_NEWLINE_ then_NEWLINE_         echo The regex matches!_NEWLINE_         echo $BASH_REMATCH      -- outputs: foobarbletch_NEWLINE_         echo ${BASH_REMATCH[1]} -- outputs: bar_NEWLINE_         echo ${BASH_REMATCH[2]} -- outputs: etch_NEWLINE_ fi_NEWLINE_使用这个语法的性能要比生成一个新的进程来运行grep命令优越，因为正则表达式匹配在bash进程内完成。如果正则表达式或者字符串包括空格或者shell 关键字，（诸如'*'或者'?'），就需要用引号包裹。Bash 4 开始的版本已经不需要这么做了。
_START_SECTION_
关联数组
_START_PARAGRAPH_
Bash 4.0 开始支持关联数组，通过类似AWK的方式，对于多维数组提供了伪支持。_NEWLINE_$ declare -A a         # 声明一个名为a的伪二位数组_NEWLINE_$ i=1; j=2_NEWLINE_$ a[$i,$j]=5           # 将键"$i,$j" 与值5 对应_NEWLINE_$ echo ${a[$i,$j]}
_START_SECTION_
键盘快捷键
_START_PARAGRAPH_
Bash默认使用Emacs的快捷键，可以通过set -o vi 让它使用Vi的快捷键
_START_SECTION_
启动脚本
_START_PARAGRAPH_
bash启动的时候会运行各种不同的脚本。_NEWLINE_当bash作为一个登录的交互shell被调用，或者作为非交互shell但带有--login参数被调用时，它首先读入并执行文件/etc/profile。然后它会依次寻找~/.bash_profile，~/.bash_login，和~/.profile，读入并执行第一个存在且可读的文件。--noprofile参数可以阻止bash启动时的这种行为。_NEWLINE_当一个登录shell退出时，bash读取并执行~/.bash_logout文件，如果此文件存在。_NEWLINE_当一个交互的非登录shell启动后，bash读取并执行~/.bashrc文件。这个行为可以用--norc参数阻止。--rcfile file参数强制bash读取并执行指定的file而不是默认的~/.bashrc。_NEWLINE_如果用sh来调用bash，bash在启动后进入posix模式，它会尽可能模仿sh历史版本的启动行为，以便遵守POSIX标准。用sh名字调用的非交互shell不会去读取其他启动脚本，--rcfile参数无效。_NEWLINE_当bash以POSIX模式启动时（例如带有--posix参数）它使用POSIX标准来读取启动文件。在此模式下，交互shells扩展变量ENV，从以此为文件名的文件中读取命令并执行。_NEWLINE_bash会探测自己是不是被远程shell守护程序运行（通常是rshd）。如果是，它会读取并执行~/.bashrc中的命令。但是rshd一般不会用rc相关参数调用shell，也不会允许指定这些参数。
_START_SECTION_
Bash与Bourne shell和csh启动脚本的比较
_START_PARAGRAPH_
Bash的特性是从Bourne shell和csh发展而来，因此一定程度上允许同Bourne shell的启动文件共享，并提供一些csh用户熟悉的启动特性。
_START_SECTION_
设置可继承的环境变量
_START_PARAGRAPH_
Bourne shell登陆时使用~/.profile 来设置环境变量，这些环境变量可以被子进程继承。Bash可以以兼容的方式使用~/.profile ，只需在Bash自有的脚本中显式执行下面这行代码。通过在~/.profile 中避免使用Bash特有的语法，就可以和Bourne shell保持兼容性。_NEWLINE_. ~/.profile
_START_SECTION_
别名和函数
_START_PARAGRAPH_
更通用的函数以及借鉴自csh的“别名(alias)”很大程度上取代了Bourne shell的别名(alias)和函数。然而这两个特性一般不能从登录式shell中继承，在该登录式shell的子shell中，它们必须被重新定义。尽管有个环境变量ENV可以被用于这个问题，不过 csh 和 Bash 都可以用子shell的启动脚本直接处理。在Bash当中，~/.bashrc 是交互式子shell启动时执行的脚本。如果想要在登录式shell中使用~/.bashrc 定义的函数，可以在~/.bash_login 的环境变量后面加上这样一行：_NEWLINE_. ~/.bashrc
_START_SECTION_
登录与退出时执行的命令
_START_PARAGRAPH_
最初登录时，csh 执行 ~/.login ，可以执行一些只在登录时执行的操作，例如显示系统负载、硬盘状态、是否收到新邮件、在日志中记录登录时间，等等。Bourne shell 可以在~/.profile 文件中模拟这种行为，但并没有预先定义文件名。可以在~/.bash_profile 文件的环境变量设置和函数定义的后面添加这样一行：_NEWLINE_. ~/.bash_login_NEWLINE_与之类似，csh还有一个文件~/.logout ，这个文件只在登录式shell退出时执行。Bash与之对应的文件是~/.bash_logout ，并且不需要专门的设置。在 Bourne shell 中，trap 这个内置工具可以实现类似的效果。
_START_SECTION_
兼容旧环境的Bash启动脚本示例
_START_PARAGRAPH_
下面这个~/.bash_profile 的框架与Bourne shell 兼容，并且为~/.bashrc 和 ~/.bash_login 提供类似于csh 的语义。[ -r 文件名] 测试指定文件是否存在，如果不存在，跳过&& 后面的部分_NEWLINE_[ -r ~/.profile ] && . ~/.profile             # 只使用Bourne shell的语法设置环境变量if [ -n "$PS1" ] ; then                       # 判断是否是交互式shell_NEWLINE_   [ -r ~/.bashrc     ] && . ~/.bashrc        # 加载~/.bashrc（tty、prompt、函数设置等）_NEWLINE_   [ -r ~/.bash_login ] && . ~/.bash_login    # 执行登录式shell登录时的任务fi                                            # if块的结束标志
_START_SECTION_
Bash 启动脚本与操作系统相关的问题
_START_PARAGRAPH_
一些 Unix 和 Linux 版本常在/etc 放置 Bash 的系统级启动脚本。Bash在其标准的初始化过程中执行它们，不过其他启动脚本可以按照不同于Bash启动序列文档所述的顺序来读取这些文件。root 用户的文件默认内容，以及新用户被创建时系统提供的默认文件可能有问题。启动 X窗口系统 的启动脚本可能使用用户的Bash 启动脚本尝试在窗口管理器 启动之前设置用户的环境变量。这些问题常常可以通过使用~/.xsession 或者 ~/.xprofile 来读取~/.profile 而解决。