每个行业都有一个工具,该行业的大师最常使用它。 对于许多系统管理员来说,这个工具就是他们的 shell。 在大多数 Linux 和其他类 Unix 系统中,默认 shell 是 Bash。
Bash 是一个相当老的程序——它起源于 20 世纪 80 年代后期——但它建立在更古老的 shell 之上,例如 C shell (csh),它至少比 Bash 早 10 年。 由于 shell 的概念如此古老,因此有大量深奥的知识等待被吸收,从而使任何系统管理员的生活都轻松得多。
让我们来看看一些基础知识。
谁曾在某个时候无意中以 root 身份运行命令并导致某种问题? 举手
我很确定我们很多人都曾是那样的人。 非常痛苦。 这里有一些非常简单的技巧可以防止您第二次撞到石头。
使用别名
首先,为 mv
和 rm
等命令设置别名,使其指向 mv -I
和 rm -I
。 这将确保运行 rm -f /boot
至少会要求您确认。 在 Red Hat Enterprise Linux 中,如果您使用 root 帐户,则默认情况下会设置这些别名。
如果您也想为您的普通用户帐户设置这些别名,只需将以下两行放入您主目录中名为 .bashrc 的文件中(这些也适用于 sudo)
alias mv='mv -i'
alias rm='rm -i'
让您的 root 提示符脱颖而出
您可以做的另一件防止意外的事情是确保您知道自己何时正在使用 root 帐户。 我通常通过使 root 提示符与我用于日常工作的普通提示符非常不同来做到这一点。
如果您将以下内容放入 root 用户主目录中的 .bashrc 文件中,您将获得一个红底黑字的 root 提示符,从而清楚地表明您(或任何其他人)应该谨慎行事。
export PS1="\[$(tput bold)$(tput setab 0)$(tput setaf 1)\]\u@\h:\w # \[$(tput sgr0)\]"
事实上,您应该尽可能避免以 root 身份登录,而是通过 sudo 运行大多数系统管理命令,但这又是另一个故事了。
在实施了一些小技巧来帮助防止使用 root 帐户的“意外副作用”之后,让我们看看 Bash 可以帮助您在日常工作中完成的几个好事情。
控制您的历史记录
您可能知道,当您在 Bash 中按下向上箭头键时,您可以看到并重用所有(好吧,很多)以前的命令。 这是因为这些命令已保存到您主目录中名为 .bash_history 的文件中。 该历史记录文件附带了一堆设置和命令,这些设置和命令可能非常有用。
首先,您可以通过键入 history
查看您的整个最近命令历史记录,或者您可以通过键入 history 30
将其限制为您最近的 30 个命令。 但这非常普通。 您可以更好地控制 Bash 保存的内容以及保存方式。
例如,如果您将以下内容添加到您的 .bashrc 中,任何以空格开头的命令都不会保存到历史记录列表中
HISTCONTROL=ignorespace
如果您需要以明文形式将密码传递给命令,这可能很有用。 (是的,这很可怕,但仍然会发生。)
如果您不希望频繁执行的命令出现在您的历史记录中,请使用
HISTCONTROL=ignorespace:erasedups
这样,每次您使用命令时,其之前的所有出现都会从历史记录文件中删除,并且只有上次调用会保存到您的历史记录列表中。
我特别喜欢的一个历史记录设置是 HISTTIMEFORMAT
设置。 这将在您的历史记录文件中为所有条目添加时间戳。 例如,我使用
HISTTIMEFORMAT="%F %T "
当我键入 history 5
时,我会得到漂亮的完整信息,如下所示
1009 2018-06-11 22:34:38 cat /etc/hosts
1010 2018-06-11 22:34:40 echo $foo
1011 2018-06-11 22:34:42 echo $bar
1012 2018-06-11 22:34:44 ssh myhost
1013 2018-06-11 22:34:55 vim .bashrc
这使得浏览我的命令历史记录并找到我两天前用来设置到我的家庭实验室的 SSH 隧道的命令(我一次又一次地忘记它……)变得容易得多。
最佳 Bash 实践
我将用我的最佳 11 条 Bash 脚本编写实践列表来总结本文(或至少是好的;我不声称无所不知)。
-
Bash 脚本可能会变得复杂,而注释很廉价。 如果您想知道是否要添加注释,请添加注释。 如果您在周末后回来,并且不得不花时间弄清楚您上周五试图做什么,那么您忘记添加注释了。
- 将所有变量名称都用花括号括起来,例如
${myvariable}
。 养成这种习惯可以使${variable}_suffix
之类的东西成为可能,并提高整个脚本的一致性。
- 在评估表达式时不要使用反引号; 请改用
$()
语法。 所以使用
for file in $(ls); do
而不是
for file in `ls`; do
前一个选项是可嵌套的,更易于阅读,并让一般的系统管理员群体感到高兴。 不要使用反引号。
- 一致性是好的。 选择一种做事风格并在整个脚本中坚持使用它。 显然,我更希望人们选择
$()
语法而不是反引号,并将他们的变量用花括号括起来。 我更希望人们使用两个或四个空格——而不是制表符——来缩进,但即使您选择做错事,也要始终如一地做错事。
- 为 Bash 脚本使用正确的 shebang。 由于我编写 Bash 脚本的目的只是为了用 Bash 执行它们,所以我最常使用
#!/usr/bin/bash
作为我的 shebang。 不要使用#!/bin/sh
或#!/usr/bin/sh
。 您的脚本将执行,但它将在兼容模式下运行——可能会产生许多意想不到的副作用。 (当然,除非您想要兼容模式。)
- 在比较字符串时,最好在 if 语句中引用您的变量,因为如果您的变量为空,Bash 将为如下行抛出错误
if [ ${myvar} == "foo" ]; then echo "bar" fi
并且对于如下行将评估为 false
if [ "${myvar}" == "foo" ]; then echo "bar" fi
此外,如果您不确定变量的内容(例如,当您解析用户输入时),请引用您的变量以防止解释某些特殊字符,并确保该变量被视为一个单词,即使它包含空格。
- 我想这是一种品味问题,但我更喜欢使用双等号 (
==
),即使在 Bash 中比较字符串时也是如此。 这是一个一致性问题,即使——仅对于字符串比较——单个等号也可以工作,但我的脑海中立即浮现出“单个等号是赋值运算符!”
- 使用正确的退出代码。 确保如果您的脚本未能执行某些操作,您会向用户显示一条书面失败消息(最好提供解决问题的方法)并发送非零退出代码
# we have failed echo "Process has failed to complete, you need to manually restart the whatchamacallit" exit 1
这使得从另一个脚本以编程方式调用您的脚本并验证其成功完成变得更容易。
- 使用 Bash 的内置机制为您的变量提供合理的默认值,或者在您期望定义的变量未定义时抛出错误
# this sets the value of $myvar to redhat, and prints 'redhat' echo ${myvar:=redhat}
# this throws an error reading 'The variable myvar is undefined, dear reader' if $myvar is undefined ${myvar:?The variable myvar is undefined, dear reader}
- 特别是如果您正在编写大型脚本,尤其是如果您与其他人一起处理该大型脚本,请考虑在函数内部定义变量时使用
local
关键字。local
关键字将创建一个局部变量,即仅在该函数内可见的变量。 这限制了变量冲突的可能性。
- 每个系统管理员有时都必须这样做:在控制台上调试某些东西,无论是数据中心中的真实控制台还是通过虚拟化平台的虚拟控制台。 如果您必须以这种方式调试脚本,您会感谢自己记住了这一点:不要让脚本中的行太长!
在许多系统中,控制台的默认宽度仍然是 80 个字符。 如果您需要在控制台上调试脚本,并且该脚本的行非常长,您将成为一只悲伤的熊猫。 此外,具有较短行的脚本——默认仍然是 80 个字符——在普通编辑器中也更容易阅读和理解!
我真的很喜欢 Bash。 我可以花几个小时写关于它的文章,或者与爱好者交流一些不错的技巧。 确保在评论中留下您最喜欢的技巧!
16 条评论