Bash编程:避免常见错误与掌握关键特性
避免函数调用时“command not found”错误
在编程中,不同的编程语言对于函数调用和定义有不同的规则。比如在Perl等语言中,你可以在函数实际定义之前的代码部分调用函数。然而,在Shell脚本中,情况有所不同。
问题:习惯了像Perl这样允许在函数定义前调用函数的语言,在Shell脚本中沿用这种方式可能会导致“command not found”错误。
解决方案:Shell脚本是按从上到下的线性方式读取和执行的,所以在使用函数之前必须先定义它们。
原因分析:一些语言(如Perl)在执行过程中会有中间步骤,将整个脚本作为一个单元进行解析,这使得你可以将main()函数放在代码顶部,而将其他函数或子例程放在后面定义。但Shell脚本是先读入内存,然后逐行执行,因此在定义函数之前无法使用它。
区分Shell通配符和正则表达式
在Shell编程中,通配符和正则表达式容易让人混淆。有时候会看到.*、*、[a - z]*等符号,但它们的含义可能和你预期的不同,而且在grep和sed中使用正则表达式,在bash的某些地方却不适用,这让人难以理清。
问题:难以区分Shell通配符和正则表达式的使用场景和含义。
解决方案:放松心态,可能是因为学习的内容太多或者使用频率太低而导致混淆,多练习就会逐渐掌握。在bash中,正则表达式语法仅在=~比较运算符中使用,其他表达式都使用Shell模式匹配。
详细说明:bash使用的模式匹配和正则表达式有一些相同的符号,但含义不同。而且在Shell脚本中,经常会调用使用正则表达式的命令,如grep和sed。经过询问专家得知,=~是bash中唯一使用正则表达式的地方。以下是bash中使用Shell模式匹配的部分语法:
- 文件名通配(路径名扩展)
-[[中的==和!=运算符
-case语句
-$GLOBIGNORE处理
-$HISTIGNORE处理
-${parameter#[#]word}
-${parameter%[%]word}
-${parameter/pattern/string}
- 几个可绑定的readline命令(如glob-expand-word、glob-complete-word等)
-complete -G和compgen -G
-complete -X和compgen -X
-help内置命令的pattern参数
Bash调用选项
在调用当前版本的bash时,可以使用以下选项。多字符选项必须在单字符选项之前出现在命令行上。登录Shell通常使用-i(交互式)、-s(从标准输入读取)和-m(启用作业控制)选项。除了表中列出的选项外,任何set选项都可以在命令行上使用。
| Option | Meaning |
|---|---|
| -c string | 如果存在string,则从string中读取命令。string之后的任何参数都将被解释为位置参数,从$0开始。 |
| -D | 将所有以$开头的双引号字符串列表打印到标准输出。当当前区域设置不是C或POSIX时,这些字符串会进行语言翻译。同时开启-n选项。 |
| -i | 交互式Shell。忽略TERM、INT和QUIT信号。如果启用作业控制,也会忽略TTIN、TTOU和TSTP信号。 |
| -l | 使bash表现得像作为登录Shell被调用一样。 |
| -o option | 接受与set -o相同的参数。 |
| -O, +O shopt-option | shopt-option是shopt内置命令接受的Shell选项之一。如果存在shopt-option,-O设置该选项的值;+O取消设置。如果未提供shopt-option,则将shopt接受的Shell选项的名称和值打印到标准输出。如果调用选项是+O,输出将以可作为输入重用的格式显示。 |
| -s | 从标准输入读取命令。如果给bash提供了参数,此标志优先(即参数不会被视为脚本名称,而是从标准输入读取)。 |
| -r | 受限Shell。 |
| -v | 读取Shell输入行时将其打印出来。 |
| - | 表示选项结束,禁用进一步的选项处理。此后的任何选项都将被视为文件名和参数。--与-同义。 |
| –debugger | 安排在Shell启动前执行调试器配置文件。在bash 3.0或更高版本中开启扩展调试模式和Shell函数跟踪。 |
| –dump-strings | 与-D作用相同。 |
| –dump-po-strings | 与-D作用相同,但输出采用GNU gettext可移植对象(po)文件格式。 |
| –help | 显示使用消息并退出。 |
| –login | 使bash表现得像作为登录Shell被调用一样,与-l相同。 |
| –noediting | 如果是交互式Shell,不使用GNU readline库读取命令行。 |
| –noprofile | 不读取启动文件/etc/profile或任何个人初始化文件。 |
| –norc | 如果是交互式Shell,不读取初始化文件~/.bashrc。如果以sh调用Shell,此选项默认开启。 |
| –posix | 在bash的默认操作与POSIX标准不同的地方,使bash的行为更符合POSIX标准。 |
| –quiet | 启动Shell时不显示任何信息,这是默认行为。 |
| –rcfile file, –init-file file | 如果是交互式Shell,执行从file读取的命令,而不是初始化文件~/.bashrc。 |
| –verbose | 等同于-v。 |
| –version | 显示此bash实例的版本号,然后退出。 |
提示字符串自定义
以下是可用的提示字符串自定义格式代码的总结,不同的bash版本对这些代码的支持有所不同。
| Command | Meaning | Added |
|---|---|---|
| \a | ASCII铃字符(007) | bash - 1.14.7 |
| \A | 当前时间,24小时制的HH:MM格式 | bash - 2.05 |
| \d | “星期 月份 日期”格式的日期 | |
| \D {format} | 将format传递给strftime(3),并将结果插入提示字符串;空格式会得到特定于区域设置的时间表示;需要使用花括号 | bash - 2.05b |
| \e | ASCII转义字符(033) | bash - 1.14.7 |
| \H | 主机名 | bash - 1.14.7 |
| \h | 主机名,直到第一个“.” | |
| \j | 当前Shell管理的作业数量 | bash - 2.03 |
| \l | Shell终端设备名称的基本名称 | bash - 2.03 |
| \n | 回车换行符 | |
| \r | 回车符 | bash - 2.01.1 |
| \s | Shell的名称 | |
| \T | 当前时间,12小时制的HH:MM:SS格式 | bash - 1.14.7 |
| \t | 当前时间,HH:MM:SS格式 | |
| \@ | 当前时间,12小时制的a.m./p.m.格式 | bash - 1.14.7 |
| \u | 当前用户的用户名 | |
| \v | bash的版本号(如2.00) | bash - 1.14.7 |
| \V | bash的发行号;版本和补丁级别(如3.00.0) | bash - 1.14.7 |
| \w | 当前工作目录 | |
| \W | 当前工作目录的基本名称 | |
| # | 当前命令的命令编号 | |
| ! | 当前命令的历史编号 | |
| \$ | 如果有效用户ID为0,打印#,否则打印$ | |
| \nnn | 八进制字符代码 | |
| \ | 打印反斜杠 | |
| [ | 开始非打印字符序列,如终端控制序列 | |
| ] | 结束非打印字符序列 |
ANSI颜色转义序列
以下是ANSI颜色转义序列的相关信息,可用于设置终端输出的颜色和字符属性。
| Code | Character attribute | FG code | Foreground color | BG code | Background color |
|---|---|---|---|---|---|
| 0 | 重置所有属性 | 30 | 黑色 | 40 | 黑色 |
| 1 | 明亮 | 31 | 红色 | 41 | 红色 |
| 2 | 暗淡 | 32 | 绿色 | 42 | 绿色 |
| 4 | 下划线 | 33 | 黄色 | 43 | 黄色 |
| 5 | 闪烁 | 34 | 蓝色 | 44 | 蓝色 |
| 7 | 反转 | 35 | 品红色 | 45 | 品红色 |
| 8 | 隐藏 | 36 | 青色 | 46 | 青色 |
| 37 | 白色 | 47 | 白色 |
Bash编程:避免常见错误与掌握关键特性
内置命令和保留字
以下是所有内置命令和保留字的总结,表格中“Type”列的字母含义为:R = 保留字,空白 = 内置命令。
| Command | Type | Summary |
|---|---|---|
| ! | R | 命令退出状态的逻辑非。 |
| : | 不执行任何操作(仅对参数进行扩展)。 | |
| . | 读取文件并在当前Shell中执行其内容。 | |
| alias | 为命令或命令行设置简写。 | |
| bg | 将作业放入后台。 | |
| bind | 将键序列绑定到readline函数或宏。 | |
| break | 从周围的for、select、while或until循环中退出。 | |
| builtin | 执行指定的Shell内置命令。 | |
| case | R | 保留字,多路条件构造。 |
| cd | 更改工作目录。 | |
| command | 绕过Shell函数查找来运行命令。 | |
| compgen | 生成可能的补全匹配项。 | |
| complete | 指定补全的执行方式。 | |
| continue | 跳过for、select、while或until循环的下一次迭代。 | |
| declare | 声明变量并赋予其属性,与typeset相同。 | |
| dirs | 显示当前记住的目录列表。 | |
| disown | 从作业表中移除作业。 | |
| do | R | for、select、while或until循环构造的一部分。 |
| done | R | for、select、while或until循环构造的一部分。 |
| echo | 输出参数。 | |
| elif | R | if构造的一部分。 |
| else | R | if构造的一部分。 |
| enable | 启用和禁用内置Shell命令。 | |
| esac | R | case构造的结束。 |
| eval | 通过命令行处理运行给定的参数。 | |
| exec | 用给定的程序替换Shell。 | |
| exit | 从Shell退出。 | |
| export | 创建环境变量。 | |
| fc | 修复命令(编辑历史文件)。 | |
| fg | 将后台作业置于前台。 | |
| fi | R | if构造的一部分。 |
| for | R | 循环构造。 |
| function | R | 定义函数。 |
| getopts | 处理命令行选项。 | |
| hash | 确定并记住全路径名。 | |
| help | 显示内置命令的帮助信息。 | |
| history | 显示命令历史。 | |
| if | R | 条件构造。 |
| in | R | case构造的一部分。 |
| jobs | 列出任何后台作业。 | |
| kill | 向进程发送信号。 | |
| let | 算术变量赋值。 | |
| local | 创建局部变量。 | |
| logout | 退出登录Shell。 | |
| popd | 从目录栈中移除目录。 | |
| pushd | 向目录栈中添加目录。 | |
| pwd | 打印工作目录。 | |
| read | 从标准输入读取一行。 | |
| readonly | 使变量只读(不可赋值)。 | |
| return | 从周围的函数或脚本返回。 | |
| select | R | 菜单生成构造。 |
| set | 设置选项。 | |
| shift | 移动命令行参数。 | |
| suspend | 暂停Shell的执行。 | |
| test | 计算条件表达式。 | |
| then | R | if构造的一部分。 |
| time | R | 运行命令管道并打印执行时间,输出格式可通过TIMEFORMAT控制。 |
| times | 打印从Shell运行的进程的累积用户和系统时间。 | |
| trap | 设置信号捕获例程。 | |
| type | 识别命令的来源。 | |
| typeset | 声明变量并赋予其属性,与declare相同。 | |
| ulimit | 设置/显示进程资源限制。 | |
| umask | 设置/显示文件权限掩码。 | |
| unalias | 移除别名定义。 | |
| unset | 移除变量或函数的定义。 | |
| until | R | 循环构造。 |
| wait | 等待后台作业完成。 | |
| while | R | 循环构造。 |
内置Shell变量
以下是bash 3.0中可用的环境变量的完整列表,表格中“Type”列的字母含义为:A = 数组,L = 冒号分隔的列表,R = 只读,U = 取消设置会使其失去特殊含义。
| Variable | Type | Description |
|---|---|---|
| * | R | 一个字符串,包含当前脚本或函数的位置参数,由$IFS的第一个字符分隔(例如arg1 arg2 arg3)。 |
| @ | R | 当前脚本或函数的每个位置参数,作为双引号字符串列表给出(例如"arg1" "arg2" "arg3")。 |
| # | R | 当前脚本或函数的参数数量。 |
| - | R | 调用Shell时提供的选项。 |
| ? | R | 上一个命令的退出状态。 |
| _ | R | 上一个命令的最后一个参数。 |
| $ | R | Shell进程的进程ID。 |
| ! | R | 最后一个后台命令的进程ID。 |
| 0 | R | Shell或Shell脚本的名称。 |
| BASH | 用于调用此bash实例的完整路径名。 | |
| BASH_ARGC | A | 一个数组,其值是当前bash执行调用栈中每个帧的参数数量。当前子例程(使用.或source执行的Shell函数或脚本)的参数数量位于栈顶。 |
| BASH_ARGV | A | 当前bash执行调用栈中的所有参数。最后一个子例程调用的最后一个参数位于栈顶;初始调用的第一个参数位于栈底。 |
| BASH_COMMAND | 当前正在执行或即将执行的命令,除非Shell正在作为陷阱的结果执行命令,在这种情况下,它是陷阱发生时正在执行的命令。 | |
| BASH_EXECUTION_STRING | -c调用选项的命令参数。 | |
| BASH_ENV | 当Shell被调用时作为环境文件运行的文件名。 | |
| BASH_LINENO | A | 一个数组,其成员是源文件中与@var{FUNCNAME}的每个成员对应的行号。${BASHLINENO[$i]}是源文件中调用${FUNCNAME[$i + 1]}的行号,对应的源文件名是${BASHSOURCE[$i + 1]}。 |
| BASH_REMATCH | AR | 一个数组,其成员由[[条件命令的=~二元运算符赋值。索引为0的元素是与整个正则表达式匹配的字符串部分,索引为n的元素是与第n个带括号的子表达式匹配的字符串部分。 |
| BASH_SOURCE | A | 一个数组,包含与$FUNCNAME数组变量中的元素对应的源文件名。 |
| BASH_SUBSHELL | 每次生成子Shell或子Shell环境时递增 1,初始值为 0。子Shell是父Shell的派生副本,共享其环境。 | |
| BASH_VERSION | 此bash实例的版本号。 | |
| BASH_VERSINFO | AR | 此bash实例的版本信息,数组的每个元素保存版本号的一部分。 |
| CDPATH | L | cd命令搜索的目录列表。 |
| COMP_CWORD | COMPWORDS中包含当前光标位置的单词的索引,此变量仅在可编程补全设施调用的Shell函数中可用。 | |
| COMP_LINE | 当前命令行,此变量仅在可编程补全设施调用的Shell函数和外部命令中可用。 | |
| COMP_POINT | 当前光标位置相对于当前命令开头的索引。如果当前光标位置在当前命令的末尾,此变量的值等于${#COMPLINE},此变量仅在可编程补全设施调用的Shell函数和外部命令中可用。 | |
| COMP_WORDBREAKS | U | Readline库在执行单词补全时视为单词分隔符的字符集。如果取消设置COMP_WORDBREAKS,它将失去其特殊属性,即使随后重新设置也是如此。 |
| COMP_WORDS | A | 当前命令行中各个单词的数组,此变量仅在可编程补全设施调用的Shell函数中可用。 |
| COMPREPLY | A | 可编程补全设施调用的Shell函数生成的可能补全项。 |
| DIRSTACK | ARU | 目录栈的当前内容。 |
| EUID | R | 当前用户的有效用户ID。 |
| FUNCNAME | ARU | 一个数组,包含当前执行调用栈中所有Shell函数的名称。索引为0的元素是当前正在执行的任何Shell函数的名称,最底部的元素是“main”,此变量仅在Shell函数执行时存在。 |
| FCEDIT | fc命令的默认编辑器。 | |
| FIGNORE | L | 执行文件名补全时要忽略的名称列表。 |
| GLOBIGNORE | L | 路径名扩展期间要忽略的文件名模式列表。 |
| GROUPS | AR | 一个数组,包含当前用户所属的组列表。 |
| IFS | 内部字段分隔符,作为单词分隔符的字符列表,通常设置为空格、制表符和换行符。 | |
| HISTCMD | U | 当前命令的历史编号。 |
| HISTCONTROL | 由冒号(:)分隔的模式列表,可具有以下值:ignorespace:以空格开头的行不进入历史列表;ignoredups:与最后一个历史行匹配的行不进入;erasedups:在保存当前行之前,从历史列表中移除所有与当前行匹配的先前行;ignoreboth:同时启用ignorespace和ignoredups。 | |
| HISTFILE | 命令历史文件的名称。 | |
| HISTIGNORE | 决定历史列表中应保留哪些内容的模式列表。 | |
| HISTSIZE | 命令历史中保留的行数。 | |
| HISTFILESIZE | 历史文件中保留的最大行数。 | |
| HISTTIMEFORMAT | 如果设置且不为空,其值用作strftime(3)的格式字符串,用于打印history内置命令显示的每个历史条目的时间戳。如果设置此变量,时间戳将写入历史文件,以便在Shell会话之间保留。 | |
| HOME | 主(登录)目录。 | |
| HOSTFILE | 用于主机名补全的文件。 | |
| HOSTNAME | 当前主机的名称。 | |
| HOSTTYPE | bash运行的机器类型。 | |
| IGNOREEOF | 退出交互式Shell之前接收的EOF字符数量。 | |
| INPUTRC | readline启动文件。 | |
| LANG | 用于确定未由以LC_开头的变量专门选择的任何类别(如LC_COLLATE、LC_CTYPE等)的区域类别。 | |
| LC_ALL | 覆盖$LANG和任何其他指定区域类别的LC_变量的值。 | |
| LC_COLLATE | 确定路径名扩展结果排序时使用的排序顺序。 | |
| LC_CTYPE | 确定路径名扩展和模式匹配中字符的解释和字符类的行为。 | |
| LC_MESSAGES | 此变量确定用于翻译以$开头的双引号字符串的区域设置。 | |
| LC_NUMERIC | 确定用于数字格式化的区域类别。 | |
| LINENO | U | 脚本或函数中刚刚运行的行号。 |
| MACHTYPE | 描述bash执行所在系统的字符串。 | |
| 检查新邮件的文件名。 | ||
| MAILCHECK | 检查新邮件的频率(以秒为单位)。 | |
| MAILPATH | L | 如果未设置$MAIL,则检查新邮件的文件名列表。 |
| OLDPWD | 上一个工作目录。 | |
| OPTARG | getopts处理的最后一个选项参数的值。 | |
| OPTERR | 如果设置为 1,显示getopts的错误消息。 | |
| OPTIND | 选项之后的第一个参数的编号。 | |
| OSTYPE | bash执行的操作系统。 | |
| PATH | L | 命令的搜索路径。 |
| PIPESTATUS | A | 一个数组变量,包含最近执行的前台管道中进程的退出状态值列表。 |
| POSIXLY_CORRECT | 如果在bash启动时存在于环境中,Shell在读取启动文件之前进入POSIX模式,就像提供了--posix调用选项一样。如果在Shell运行时设置,bash启用POSIX模式,就像执行了set -o posix命令一样。 | |
| PROMPT_COMMAND | 在发出主提示之前,将其值作为命令执行。 | |
| PS1 | 主命令提示字符串。 | |
| PS2 | 行延续的提示字符串。 | |
| PS3 | select命令的提示字符串。 | |
| PS4 | xtrace选项的提示字符串。 | |
| PPID | R | 父进程的进程ID。 |
| PWD | 当前工作目录。 | |
| RANDOM | U | 0到32767(2^15 - 1)之间的随机数。 |
| REPLY | 用户对select命令的响应;如果未给出变量名,则是read命令的结果。 | |
| SECONDS | U | 自Shell被调用以来的秒数。 |
| SHELL | Shell的完整路径名。 | |
| SHELLOPTS | LR | 已启用的Shell选项列表。 |
| SHLVL | 每次调用新的bash实例(不是子Shell)时递增 1,用于计算bashShell的嵌套深度。 | |
| TIMEFORMAT | 指定在命令管道上使用time保留字时输出的格式。 | |
| TMOUT | 如果设置为正整数,则在没有收到输入的情况下,Shell自动终止的秒数。 | |
| UID | R | 当前用户的用户ID。 |
| auto_resume | 控制作业控制的工作方式(值为exact、substring或其他非这些关键字的值)。 | |
| histchars | 指定用作历史控制字符的内容,通常设置为字符串!^#。 |
通过掌握这些Bash的关键特性和避免常见错误,能够让你在使用Bash进行编程和脚本编写时更加得心应手,提高工作效率和代码的稳定性。希望这些内容对你有所帮助,让你在Bash的世界中探索更多的可能性。