====== bash ====== GNU/Linux支持多种版本的shell. 但默认的是bash. /etc/shells文件列出了本机linux系统支持的shell类型. /etc/passwd中列出了用户默认的登录shell. 从当前shell转换到另一种shell: 直接在命令行输入对应的shell程序名.如:\\ $ sh\\ sh-3.00$\\ 提示符变了, 要退回到之前的bash, 可输入exit或使用C-d组合键. ===== bash的特色 ===== - 命令行参数: sh只能传送单字符参数(可通过内置的set命令配置), bash还可以传送字符串参数. - 启动配置文件: bash启动时, 依次读取启动文件, 这样可以在bash启动时完成一些默认的配置工作. (关于bash配置文件, 后面再讨论). - bash是交互性shell. - 条件表达式. - shell算术. - 别名. - 数组 - 目录栈. - 受限版的bash: rbash. ===== bash命令的执行 ===== bash能够识别要执行内容的类型: 是系统中的可执行程序, 还是bash的内置命令, 或者是一个bash脚本? 根据这些类型, bash的执行方式有所不同: - 可执行程序: 遵循fork-exec形式, 它fork出一个子bash进程, 父bash和子bash仅进程号不同. 注意这种情况下, 父进程等待子进程执行, 子bash执行完毕退出后, 父bash重新执行. 程序对子bash的环境做出的更改不会影响父bash. - bash内嵌命令: bash直接执行命令, 不新生成一个进程. - bash脚本: 分两种情况: * (1) 如果直接执行脚本, 比如: $ ./foo.sh , 此情况与执行可执行程序相同, 生成的子bash进程一次从脚本读取一行命令来执行, 如同用户从键盘输入命令. * (2) 如果执行 $ source ./foo.sh, 这相当于执行bash的内嵌命令, 也可以起到执行脚本命令的作用, 但不生成新的bash. 所以如果需要在脚本中更改bash环境变量的话, 要用source命令! 直接运行脚本只能更改fork出的子bash的环境变量, 运行结束后, 子bash退出, 父bash的环境变量不受影响. ===== 编写bash脚本 ===== 1, 推荐使用能够显示bash语法高亮的编辑器, 比如emacs, gedit, vim... 2, 自己编写的脚本怎样命名, 放在什么地方? 命名: 为了避免自己写的脚本与系统命令重名, 建议shell脚本以.sh结尾. 但自己写的脚本还是可能与系统自带的脚本重名, 建议命名前通过$ which scriptname.sh (或者whereis, locate)来查看. 位置: 可以将自己写的脚本放在一个合适的目录中, 比如~/script.  Unix/Linux不利用文件扩展名来判断文件类型. 要想知道文件类型, 可使用file命令. 3, 如何执行脚本? (1) 脚本可以像命令一样被执行:\\ $ /bin/bash file.sh (不需要file.sh的可执行权限) 如果你懒得输入/bin/bash, 可以采取下面的两种方法:\\ 如果脚本被集中放置在~/script目录, 可以将该目录添加到PATH中再直接调用\\ $ export PATH="$PATH:~/script"\\ 由于脚本所在的目录在PATH环境变量中, 所以可直接运行: $ file.sh (需要file.sh有可执行权限) 如果想执行当前目录的脚本, 可以运行$ ./scriptname.sh (需要file.sh有可执行权限)\\ 方法(1)使bash新生成一个子bash来执行, 在此情况下, 脚本对环境变量的更改只对子bash有效.并且在执行脚本文件之前必须更改权限(指明shell路径时不用), 让脚本具有相应的执行权限.   (2) 可使用source命令来直接执行脚本(可用'.'来代替source). 它与上面的方法有两点区别:\\ 1, 脚本文件不需要执行权限.\\ 2, 就在当前bash中执行, bash不会新fork一个子bash来执行它, 所以脚本对环境变量的更改会影响到当前bash. 推荐使用./file.sh在当前目录执行脚本, 这样可以避免误调系统其他位置的重名脚本. 如果需要更改当前shell的环境变量, 则使用source命令. 不推荐修改环境变量: 这会造成系统的安全隐患. 当你确认脚本运行无误, 而且需要经常执行它时, 可将它copy到合适的系统执行目录, 比如/usr/bin, /usr/local/bin 或者~/bin(需修改PATH)中. 然后向调用命令一样执行它. ==== 脚本结构 ==== 脚本开头第一行指定运行脚本的shell, 一般指定为bash:\\ #!/bin/bash bash脚本中的注释以'#'开头, #之后到行尾的内容为注释, 在执行时会被bash忽略. (脚本中不能再在同一行的注释之后添加命令, #之后到行尾的内容都会被bash忽略!) 但脚本开头的#!是个例外! 在脚本结尾的最后一行, 一般包含一个 "exit 0" 语句(0表示执行成功). 它返回一个值. 注意: 在交互式bash中, 这条命令没什么用处. 当它所在的脚本被被的脚本调用时, 就能确定被调用的脚本是否正确执行了. 你的脚本在将来可能被别的脚本所调用, 所以最好在脚本末尾包含这条语句. 这也是编写可重用脚本的一个好习惯. ==== 调试脚本 ==== ==== 调试整个脚本 ==== 最通用的方法是新调用一个bash, 以调试选项"-x"来执行脚本: $ bash -x scriptname.sh 这种情况下, 新bash在显示每个语句生成的结果之前打印出该语句(以+开头), 这样易于我们对比语句和执行结果. ==== 调试脚本的部分内容 ==== 只需在脚本中你想调试的语句之前添加: set -x, 然后在结尾添加: set +x. 可以把这两条语句当作"调试开关", 在脚本中多次调用. 调试选项归纳如下: Short notation Long notation Result set -f      set -o noglob Disable file name generation using metacharacters (globbing). set -v      set -o verbose Prints shell input lines as they are read. set -x      set -o xtrace Print command traces before executing command. 注意, -为打开启用调试,+为关闭调试,这容易混淆. 这些选项不光可以添加到脚本文件中, 还可以直接在命令行中指定.  e.g: $ set -x $ ls 看看结果吧 :) ===== bash环境 ===== ==== 启动配置文件的读取 ==== 首先要理解: 交互式bash和非交互式bash(interactive& non-interactive) 交互式shell: 用户输入命令给shell执行, shell将执行的结果通过输出反馈给用户.  非交互式shell: 命令(脚本)在后台执行, 执行过程中不读取用户输入, 也不反馈执行信息.(执行完毕后可能会显示一些信息). 如何判断某个shell是 不是交互式的呢? 可以运行 $ echo &-, 若输出中还有小写字母'i', 则是交互式shell. 一般而言, terminal或console都是交互式shell, 而shell脚本在执行时执行它的shell就是非交互式的. 交互式bash还有两种调用方式: login和non-login. login: 我们在文本模式下登录console时, bash提示输入用户名和密码, 此时的bash就是login的. non-login: 在图形模式下, 打开一个 terminal, 不需要输入用户明或密码, 此时调用的bash就是non-login的. 根据login和non-login的区别, bash在启动时读取不同的配置文件: === login: === 1, 读取/etc/profile: 它是所有用户, 所有shell的启动配置文件. /etc/profile还会读取/etc/bash.bashrc, 它是所有用户的bash启动配置文件. 2, 读取~/.bash_profile, 若它不存在, 则读取~/.bash_login 同样, 若前两者不存在, 读取~/.profile.  3, 在logout时读取~/.bash_logout === non-login: === 读取~/.bashrc. 可以自行在上述的配置文件的末尾添加echo语句, 这样在console中登录或者打开一个终端时就能显示配置文件的读取信息. 上面的总结基于Ubuntu, 各发行版可能情况不同. non-interactive:\\ 读取的文件由BASH_ENV定义. 这些文件要用全路径, 因为无法用PATH变量来搜寻这些文件. ==== bash的初始化文件 ==== === 一, 针对整个系统所有用户的login配置文件. === 1, 针对整个系统的所有用户, 所有shell的配置文件 /etc/profile\\ # System wide environment and startup programs, for login setup 注意: 该配置文件被交互式login shell读取, 以bash为例, 在图形模式下打开terminal时是交互式non-login的, 所以不会读取/etc/profile: 运行 $ bash, 不会读取/etc/profile. 但如果以 $ bash --login, 则会读取. 在字符界面下, 最初登录时会读取/etc/profile. 登录后的情形等同于图形界面的terminal. 2, 针对整个系统所有用户, bash的配置文件 /etc/bash.bashrc\\ 它一般会被/etc/profile读取以配置bash环境. === 二, 针对单个用户的login配置文件 === 这些配置文件默认位于用户的home目录, 以.开头, 是隐藏文件. 如果它们不存在, 可以创建它们. 1, ~/.bash_profile\\ This is the preferred configuration file for configuring user environments individually. In this file, users can add extra configuration options or change default settings. 与/etc/profile相同, ~/.bash_profile在交互式, login情况下被读取.\\ 如果~/.bash_profile不存在, 则读取~/.bash_login, 若~/.bash_login也不存在, 则读取~/.profile. === 三, 针对单个用户的login-out配置文件 === 在logout时候, bash读取~/.bash_logout文件. === 四, 针对单个用户的non-login配置文件 === ~/.bashrc\\ 在图形模式下, non-login bash更为广泛地存在: 打开一个terminal一般不会读取/etc/profile或~/.bash_profile文件, 因为图形模式下打开终端时是non-login的. 这种情况下通过~/.bashrc来配置.\\ ~/.bashrc是否会被读取的情况等同于/etc/profile和~/.bash_profile. 这样, 我们就知道在定制bash环境时, 如何针对自己的需要修改这些配置文件了:\\ login情形下, 针对所有用户的定制: /etc/bash.bashrc; 针对单个单个用户的定制: 修改~/.bash_profile\\ non-login情形下: 针对所有用户的定制: /etc/bash.bashrc; 针对单个用户的定制: 修改~/.bashrc\\ 注意: /etc/bash.bashrc会被login或non-login的bash读取! 对这出配置文件进行修改后, 可以重登录, 或者新开一个bash, 或者使用source命令来使它们生效. 有时候可能记不清到底是哪些配置文件被读取, 可在上述的文件末尾添加一条echo语句, 显示它是否被调用. 然后打开一个terminal或者进入console看看, 就知道哪些配置文件被调用, 以及它们的调用顺序.