经常使用系统时,往往会陷入设定的使用模式。 有时,您没有养成以最佳方式做事的习惯。 有时,您甚至会采取导致混乱和笨拙的不良做法。 纠正此类不足的最佳方法之一就是认真养成可以抵消这些不足的好习惯。 本文提出了10种值得借鉴的UNIX命令行习惯-良好的习惯,这些习惯可以帮助您打破许多常见的使用习惯,并在此过程中提高命令行的工作效率。 在良好习惯列表之后,将更详细地描述每个习惯。
养成10个好习惯
要采用的十个好习惯是:
- 一次滑动即可制作目录树 。
- 改变路径; 不要移动档案 。
- 将命令与控制运算符结合在一起 。
- 谨慎引用变量 。
- 使用转义序列来管理长输入 。
- 将命令分组到一个列表中 。
- 使用
xargs
外面find
。 - 知道
grep
什么时候应该进行计数-什么时候应该搁置 。 - 匹配输出中的某些字段,而不仅仅是line 。
- 停止管道
cat
小号 。
一次滑动即可制作目录树
清单1说明了最常见的UNIX不良习惯之一:一次定义一个目录树。
清单1.坏习惯1的示例:分别定义目录树
~ $ mkdir tmp
~ $ cd tmp
~/tmp $ mkdir a
~/tmp $ cd a
~/tmp/a $ mkdir b
~/tmp/a $ cd b
~/tmp/a/b/ $ mkdir c
~/tmp/a/b/ $ cd c
~/tmp/a/b/c $
使用-p
选项设置mkdir
并在单个命令中创建所有父目录及其子目录,可以更快得多。 但是,即使是知道此选项的管理员,在命令行上创建子目录时,仍然会遇到逐步进入这些子目录的麻烦。 值得您认真学习好习惯的时间:
清单2.良好习惯1的示例:使用一个命令定义目录树
~ $ mkdir -p tmp/a/b/c
您可以使用此选项来制作整个复杂的目录树,这些目录树非常适合在脚本内部使用。 不只是简单的层次结构。 例如:
清单3.另一个好习惯#1示例:使用一个命令定义复杂的目录树
~ $ mkdir -p project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}
过去,单独定义目录的唯一借口是您的mkdir
实现不支持此选项,但是在大多数系统上不再如此。 IBM AIX? mkdir
,GNU mkdir
,以及其他符合单一UNIX标准,现在有这个选项。
对于仍然缺乏能力的几个系统,使用mkdirhier
脚本(参见相关信息 ),这是一个包装mkdir
,做同样的功能:
~ $ mkdirhier project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}
改变路径; 不要移动档案
另一个错误的使用模式是将.tar存档文件移动到某个目录,因为它恰好是您要在其中解压缩的目录。您无需执行此操作。 您可以将任何.tar归档文件解压缩到所需的任何目录中-这是-C
选项的作用。 解压缩归档文件时,请使用-C
选项,以指定用于解压缩文件的目录:
清单4.习惯2的示例:使用选项-C
解压缩.tar存档文件
~ $ tar xvf -C tmp/a/b/c newarc.tar.gz
养成使用-C
的习惯,比将存档文件移动到您要解压缩文件的位置,更改到该目录然后再提取其内容要好-特别是如果存档文件属于其他位置。
将命令与控制运算符结合
您可能已经知道,在大多数Shell中,您可以通过在它们之间放置分号(;)来组合命令。 分号是一个shell 控制运算符 ,虽然它对于在单个命令行上将多个离散的命令串在一起很有用,但它并不能解决所有问题。 例如,假设您使用分号来组合两个命令,其中第二个命令的正确执行完全取决于第一个命令的成功完成。 如果第一个命令没有按预期退出,则第二个命令仍然运行-并失败。 而是,使用更合适的控制运算符(本文中将介绍其中的一些)。 只要您的外壳支持它们,就值得养成使用它们的习惯。
仅当另一个命令返回零退出状态时才运行命令
使用&&
控件运算符组合两个命令,以便仅在第一个命令返回零退出状态时才运行第二个命令。 换句话说,如果第一个命令成功运行,则第二个命令运行。 如果第一个命令失败,则第二个命令根本不运行。 例如:
清单5.好习惯3的示例:将命令与控件运算符组合
~ $ cd tmp/a/b/c && tar xvf ~/archive.tar
在此示例中,除非该目录不存在,否则存档的内容将提取到〜/ tmp / a / b / c目录中。 如果该目录不存在,则tar
命令不会运行,因此不会提取任何内容。
仅当另一个命令返回非零退出状态时才运行命令
同样, ||
仅当第一个命令返回非零退出状态时,控制操作员才将两个命令分开并运行第二个命令。 换句话说,如果第一个命令成功执行,则第二个命令将不会运行。 如果第一个命令失败,则第二个命令会运行。 在测试给定目录是否存在时,通常使用此运算符;如果不存在,它将创建一个:
清单6.另一个好习惯#3的示例:将命令与控件运算符组合
~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c
您还可以组合本节中描述的控制运算符。 每个都在最后一个命令运行时起作用:
清单7.好习惯的组合示例#3:将命令与控制运算符组合
~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c && tar xvf -C tmp/a/b/c ~/archive.tar
谨慎引用变量
始终谨慎使用Shell扩展名和变量名。 通常最好将变量调用括在双引号中,除非您有充分的理由不这样做。 同样,如果直接在变量名后加上字母数字文本,请确保也将变量名括在大括号({})中,以将其与周围的文本区分开。 否则,shell将尾随文本解释为变量名的一部分-并很可能返回空值。 清单8提供了各种引用和不引用变量及其作用的示例。
清单8.好习惯#4的示例:引用(不引用)变量
~ $ ls tmp/
a b
~ $ VAR="tmp/*"
~ $ echo $VAR
tmp/a tmp/b
~ $ echo "$VAR"
tmp/*
~ $ echo $VARa
~ $ echo "$VARa"
~ $ echo "${VAR}a"
tmp/*a
~ $ echo ${VAR}a
tmp/a
~ $
使用转义序列来管理长输入
您可能已经看过代码示例,其中反斜杠(\)沿长行延续到下一行,并且您知道大多数shell会将在由反斜杠连接的连续行上键入的内容视为一条长行。 但是,您可能不会经常在命令行上利用此功能。 如果您的终端不能正确处理多行换行或命令行比平常小(例如,提示符下的路径很长时),则反斜杠特别方便。 反斜杠在键入长输入行时也很有用,如以下示例所示:
清单9.习惯5的示例:对长输入使用反斜杠
~ $ cd tmp/a/b/c || \
> mkdir -p tmp/a/b/c && \
> tar xvf -C tmp/a/b/c ~/archive.tar
另外,以下配置也可以使用:
清单10.好习惯的替代示例#5:对长输入使用反斜杠
~ $ cd tmp/a/b/c \
> || \
> mkdir -p tmp/a/b/c \
> && \
> tar xvf -C tmp/a/b/c ~/archive.tar
但是,将输入行划分为多行,外壳程序始终将其视为一条连续的行,因为它始终会去除所有反斜杠和多余的空格。
注意:在大多数外壳中,当您按向上箭头键时,整个多行条目将在一条较长的单行上重绘。
将命令分组在一起
大多数外壳程序都有将一组命令组合在一起的方法,以便您可以将它们的总和输出向下传递到管道中,或者将其任何或所有流重定向到同一位置。 通常,您可以通过在子外壳程序中运行命令列表或在当前外壳程序中运行命令列表来执行此操作。
在子shell中运行命令列表
使用括号将命令列表括在一个组中。 这样做可以在新的子Shell中运行命令,并允许您重定向或以其他方式收集整个输出,如以下示例所示:
清单11.良好习惯6的示例:在子shell中运行命令列表
~ $ ( cd tmp/a/b/c/ || mkdir -p tmp/a/b/c && \
> VAR=$PWD; cd ~; tar xvf -C $VAR archive.tar ) \
> | mailx admin -S "Archive contents"
在此示例中,归档文件的内容被提取到tmp / a / b / c /目录中,而包含已提取文件列表的分组命令的输出被邮寄到admin
地址。
如果要在命令列表中重新定义环境变量,并且又不想将这些定义应用于当前shell,则最好使用subshell。
在当前shell中运行命令列表
使用花括号({})括住要在当前 shell中运行的命令列表。 确保在括号和实际命令之间包含空格,否则外壳程序可能无法正确解释括号。 另外,请确保列表中的最终命令以分号结尾,如以下示例所示:
清单12.另一个好习惯的例子#6:在当前shell中运行命令列表
~ $ { cp ${VAR}a . && chown -R guest.guest a && \
> tar cvf newarchive.tar a; } | mailx admin -S "New archive"
在查找之外使用xargs
使用xargs
工具作为过滤器,以充分利用find
命令中剔除的输出。 一般的原则是, find
运行提供符合某些条件的文件列表。 此列表传递给xargs
,然后xargs
运行其他有用的命令,并将该文件列表作为参数,如以下示例所示:
清单13. xargs
工具的经典用法示例
~ $ find | \
> xargs
但是,不要把xargs
仅仅当作find
的助手; 它是那些未得到充分利用的工具之一,当您习惯使用它时,就想尝试所有东西,包括以下用途。
传递以空格分隔的列表
在最简单的调用中, xargs
就像一个过滤器,将一个列表(每个成员都放在一行中)作为输入。 该工具将这些成员放在单个用空格分隔的行上:
清单14. xargs
工具的输出示例
~ $ xargsabc
a b c
~ $
您可以通过xargs
发送任何输出文件名的工具的输出,以获取其他一些以文件名作为参数的工具的参数列表,如以下示例所示:
清单15.使用xargs
工具的示例
~/tmp $ ls -1 | xargs
December_Report.pdf README a archive.tar mkdirhier.sh
~/tmp $ ls -1 | xargs file
December_Report.pdf: PDF document, version 1.3
README: ASCII text
a: directory
archive.tar: POSIX tar archive
mkdirhier.sh: Bourne shell script text executable
~/tmp $
xargs
命令不仅可以传递文件名,还可以使用。 在需要将文本过滤到一行时随时使用:
清单16.习惯7的示例:使用xargs
工具将文本过滤为一行
~/tmp $ ls -l | xargs
-rw-r--r-- 7 joe joe 12043 Jan 27 20:36 December_Report.pdf -rw-r--r-- 1 \
root root 238 Dec 03 08:19 README drwxr-xr-x 38 joe joe 354082 Nov 02 \
16:07 a -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar -rwxr-xr-x 1 \
joe joe 3239 Sep 30 12:40 mkdirhier.sh
~/tmp $
使用xargs
要小心
从技术上讲,发生罕见的情况是您可能会使用xargs
遇到麻烦。 默认情况下,文件结尾字符串是下划线(_); 如果该字符作为单个输入参数发送,则其后面的所有内容都会被忽略。 为了防止这种情况,请使用-e
标志,该标志不带参数会完全关闭文件结尾字符串。
知道grep什么时候应该进行计数-什么时候应该搁置
避免将grep
传递给wc -l
以便计数输出的行数。 grep
的-c
选项提供了与指定模式匹配的行数,并且通常比到wc
的管道快,如以下示例所示:
清单17.好习惯#8的示例:使用和不使用grep计数行
~ $ time grep and tmp/a/longfile.txt | wc -l
2811
real 0m0.097s
user 0m0.006s
sys 0m0.032s
~ $ time grep -c and tmp/a/longfile.txt
2811
real 0m0.013s
user 0m0.006s
sys 0m0.005s
~ $
除了速度因子外, -c
选项也是一种更好的计数方法。 对于多个文件,带有-c
选项的grep
将为每个文件返回一个单独的计数,每行一个,而到wc
的管道则给出了所有合并文件的总数。
但是,无论速度如何,该示例都展示了另一个需要避免的常见错误。 这些计数方法仅对包含匹配模式的行数进行计数-如果您正在寻找的话,那就太好了。 但是在行可以具有特定模式的多个实例的情况下,这些方法无法为您提供匹配实例的实际数目的真实计数。 毕竟,要计算实例数,请使用wc
进行计数。 首先,如果您的版本支持,请运行带有-o
选项的grep
命令。 此选项仅输出匹配的模式,每行一个,而不是行本身。 但是您不能将它与-c
选项一起使用,因此请使用wc -l
来计数行,如以下示例所示:
清单18.好习惯#8的示例:用grep计数模式实例
~ $ grep -o and tmp/a/longfile.txt | wc -l
3402
~ $
在这种情况下,对wc
的调用比第二次对grep
调用稍快,第二次对grep
调用带有插入的虚拟模式以匹配和计数每一行(例如grep -c
)。
匹配输出中的某些字段,而不仅仅是行
当您只想在输出行中的特定字段而不是行中的任何地方匹配模式时,像awk
这样的工具比grep
更可取。
下面的简化示例显示了如何仅列出在12月修改的文件:
清单19.不良习惯9的示例:使用grep在特定字段中查找模式
~/tmp $ ls -l /tmp/a/b/c | grep Dec
-rw-r--r-- 7 joe joe 12043 Jan 27 20:36 December_Report.pdf
-rw-r--r-- 1 root root 238 Dec 03 08:19 README
-rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar
~/tmp $
在此示例中, grep
过滤行,输出所有文件的Dec
修改日期和名称中带有Dec
文件。 因此,即使文件自1月以来未进行修改,也将匹配文件December_Report.pdf。 这可能不是您想要的。 要匹配特定字段中的模式,最好使用awk
,其中关系运算符匹配确切的字段,如以下示例所示:
清单20.良好习惯9的示例:使用awk
在特定字段中查找模式
~/tmp $ ls -l | awk '$6 == "Dec"'
-rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar
-rw-r--r-- 1 root root 238 Dec 03 08:19 README
~/tmp $
请参阅相关主题有关如何使用的详细信息awk
。
停止管道猫
基本但常见的grep
使用错误涉及将cat
的输出传递到grep
以搜索单个文件的内容。 这绝对是不必要的,并且会浪费时间,因为grep
工具grep
文件名作为参数。 在这种情况下,您根本不需要使用cat
,如以下示例所示:
清单21.习惯和不良习惯10的示例:在有和没有cat
情况下使用grep
~ $ time cat tmp/a/longfile.txt | grep and
2811
real 0m0.015s
user 0m0.003s
sys 0m0.013s
~ $ time grep and tmp/a/longfile.txt
2811
real 0m0.010s
user 0m0.006s
sys 0m0.004s
~ $
此错误适用于许多工具。 因为大多数工具都使用连字符(-)将标准输入作为参数,所以即使使用cat
散布带有stdin
多个文件的参数也通常无效。 当您将cat
与它的多个过滤选项之一一起使用时,实际上仅需要在管道之前先进行连接。
结论:养成良好的生活习惯
检查您的命令行习惯是否有任何不良的使用模式是一件好事。 不良习惯会使您减速,并经常导致意外错误。 本文介绍了10个新习惯,可以帮助您摆脱许多最常见的使用错误。 养成这些良好习惯是提高UNIX命令行技能的积极一步。
翻译自: https://www.ibm.com/developerworks/aix/library/au-badunixhabits.html