Linux 命令行大全 (2)

常见任务和主要工具

重定向

I/O 是输入/输出的缩写。这个功能可以把命令行的输入重定向为从文件中获取内容,也可以把命令行的输出结果重定向到文件中。如果我们将多个命令行关联起来,将形成非常强大的命令——管道。

标准输入、标准输出和标准错误

到目前为止,我们使用过的很多程序生成了不同种类的输出。这些输出常包含两种类型。一种是程序运行的结果,即该程序生成的数据;另一种是状态和错误信息,表示程序当前的运行情况。比如输入 ls 命令,屏幕上将显示它的运行结果以及它的相关错误信息。

UNIX “一切都是文件”的思想一致,类似 ls 的程序实际上把他们的运行结果发送到了一个称为标准输出(standard output,通常表示为 stdout)的特殊文件中,他们的状态信息发送到了另一个称为标准错误(standard error,表示为 stderr)的文件中。默认情况下,标准输出和标准错误都将被来年街道屏幕上并且不会保存在磁盘文件中。

另外,许多程序从一个被称为标准输入(standard input,表示为 stdin)的设备来得到输入。默认情况下,标准输入连接到键盘。

I/O 重定向功能可以改变输出内容发送到目的地,也可以改变输入内容的来源地。通常来说,输出的内容显示在屏幕,输入内容来自键盘。但是用 I/O 重定向功能可以改变这一惯例。

标准输出重定向

使用重定向操作符 > 后面接文件名可以把标准输出重定向到另一个文件中,而不是显示在屏幕中。比如我们可以按照下面的形式把 ls 命令的输出保存到 ls-output.txt 文件中

ls -l /usr/bin > ls-output.txt

此时我们把目录名称换为一个不存在的目录

ls -l /bin/usr > ls-output.txt

此时会显示错误信息,这个错误信息会直接显示在屏幕上而不会保存在 ls-output.txt 文件中,因为 ls 程序并不会把错误信息发送到标准输出文件中,而是把错误信息发送到标准错误文件中。而这个输出文件内容会被清空,因为 ls 命令没有输出任何命令。

如果我们要删除一个文件内容或创建一个空文件,可以采用这种方式

> ls-output.txt

我们如何能够不从文件的首位置覆盖文件,而是从文件的尾部开始添加输出内容呢?我们可以使用重定向符 >> 来实现,比如

ls -l /usr/bin >> ls-output.txt

如果这个文件不存在,它会与 > 一样创建这个文件。

标准错误重定向

标准错误的重定向并不能简单实用要给专用的重定向符来实现,要实现标准错误的重定向,不得不提到它的文件描述符。一个程序可以把生成的输出内容发送到任意文件流中,如果把这些文件流中的前三个分别对应标准输入文件、标准输出文件和标准错误文件,那么 shell 将在内部用文件描述符分别索引他们为 0、1 和 2。由于标准错误等同于文件描述符 2,所以可以使用这种表示法来重定向标准错误。

ls -l /bin/user 2> ls-error.txt

将标准输出和标准错误重定向到一个文件中

很多情况下,我们会希望把一个命令的所有输出内容都放在同一个独立文件中。为此,我们必须同时重定向标准输出和标准错误。有两种方法可以满足要求,第一种是传统的方法,在旧版本的 shell 中使用

ls -l /bin/usr > ls-output.txt 2>&1

使用这个方法,将执行两个重定向操作,第首先重定向到标准输出到 ls-output.txt 中,然后使用标记符 2>&1 把文件描述符 2(标准错误)重定向到文件符 1(标准输出)中。

这种重定向操作的顺序非常重要。标准错误的重定向操作通常发生在标准输出重定向操作之后,否则他将不起作用。

最近的 bash 版本提供了效率更高的第二种方法。

ls -l /bin/usr &> ls-output.txt

这个例子中,使用 &> 就把标准输出和标准错误都重定向到了 ls-output.txt 文件中。

处理不想要的输出

有时命令执行后我们并不希望得到输出,而是想把这个输出丢弃,尤其是在输出错误和状态信息的情况下更为需要。系统提供了一种方法,即把输出重定向到一个成为 /dev/null 的特殊文件中来实现它。这个文件被称为位桶(bit bucket)

ls -l /bin/usr 2> /dev/null

标准输入重定向

cat——合并文件

cat 命令读取一个或多个文件,并把它们复制到标准输出文件中,格式如下

cat [file...]

大多数情况下,你可以认为 cat 命令和 DOS 中的 TYPE 命令类似。使用它显示文件而不需要分页,例如

cat ls-output.txt

上面的命令将显示 ls-output.txt 文件中的内容。 cat 经常用来显示短的文本文件,由于 cat 可以接收多个文件作为输入参数,所以它也可以用来把文件连接在一起。假设我们下载了一个很大的文件,它已被拆分成多个部分,现在我们想要把各部分连接在一起,并还原为原来的文件,如果这些文件命名为:movie.mpeg.001 movie.mpeg.002...movie.mpeg.099

我们可以使用下面的命令来将它们重新连接在一起

cat movie.mpeg.0* > movie.mpeg

通配符一般是按顺序来扩展的,因此这些参数将按正确的顺序来排列。假如只输入 cat 没有给定任何参数,踏进从标准输入读取内容,由于标准输入默认是连接到键盘,所以它实际上是等待着从键盘输入到内容。输入 Ctrl-D 告知 cat 命令他已经达到了标准输入的文件尾。

如果我们想要创建短的文本文件内容可以像下面这样用

cat > lazy_dog.txt

之后按下 Ctrl-D 结束输入,我们还可以尝试下标准输入的重定向

cat < lazy_doy.txt

此时命令行可以打印出文件内容。

管道

命令从输入到读取数据,并将数据发送到标准输出的能力,是使用了名为管道的 shell 特性。使用管道操作符 | 可以把一个命令的标准输入传送到另一个命令的标准输入中。

command1 | command2

过滤器

管道功能经常用来对数据执行复杂的操作。也可以把多条命令合在一起构成一个管道。这种方式中用到的命令通常被称为过滤器 filter ,过滤器接受输入,按照某种方式对输入进行改变,然后再输出它。第一个要用到的命令是 sort 。假设要把 /binusr/bin 目录下的所有可执行程序合并成一个列表,并按照顺序排列,最后再查看这个列表。

ls /bin /usr/bin | sort | less

uniq——报告或忽略文件中重复的行

uniq 命令和 sort 命令结合使用。 uniq 可以接受来自于标准输入或一个单一文件名参数对应的已排好序的数据列表(可以查看 uniq 命令的 man 页面获取详细的信息)。默认情况下,该命令删除列表中的所有重复行,因此,在管道中添加 uniq 命令,可以确保所有列表都没有重复行(即在 /bin/usr/bin 目录下都出现的相同名字的任意程序)。

ls /bin /usr/bin | sort | uniq | less

如果想要查看重复行的列表,可以在 uniq 命令后添加 -d 命令。

wc——打印行数、字数和字节数

wc (word count)命令用来显示文件中包含的行数、字数和字节数。比如:

wc ls-output.txt

-l 参数限制命令只报告行数。

grep——打印匹配行

grep 是一个功能强大的程序,它用来在文件中查找匹配文本,其使用方法如下

grep pattern [file...]

grep 能够匹配的模式内容是非常复杂的。

-i 表示忽略大小写, -v 使 grep 只输出和模式不匹配的行。

head/tail——打印文件的开头部分/结尾部分

默认情况下, head 命令只输出开头十行, tail 命令则输出文件的最后十行。 -n 选项可以调节输出的行数

head -n 5 ls-output.txt

tail 中有一个选项用来查看文件,该选项在观察正在被写入的日志文件的进展状态时很有用,下面的例子中,我们将观察 /var/log 目录下的 messages 文件。因为 /var/log/messages 文件可能包含安全信息,在一些 Linux 发行版中需要超级用户的权限才能执行该操作

tail -f /var/log/messages

tee——从 stdin 读取数据,并同时输出到 stdout 和文件

tee 程序读取标准输入,再把读到的内容复制到标准输出和一个或更多的文件中去。当在某个中间处理阶段来捕获一个管道中的内容时会很有用。

ls /usr/bin | tee ls.txt | grep zip

透过 shell 看世界

在本章,我们将会介绍 shell 的几个有趣而复杂的特性,但是我们只是用一条新命令来处理

echo: 显示一行文本

扩展

每次输入命令行按下 Enter 键时,bash 搜会在执行命令之前对文本进行多重处理。前面已经见过一个简单的字符序列(比如 *)在 shell 中被识别为多种意思的几个例子。产生这个结果的处理过程称为扩展(expansion)。有了扩展功能,在输入内容后,这些内容将会在 shell 对其执行之前被扩展成其他内容。

echo 是 shell 的一个内置命令,他执行的任务非常简单,即把文本参数内容打印到标准输出。

echo this is a test

这个例子相当简单,传递给 echo 的任何参数都将显示出来

echo *

它不会返回 * ,而是会被 shell 扩展成其他内容,在这个例子中扩展为当前工作目录下的所有文件名,因此,只能看到 * 扩展后的结果。

路径名扩展

通过通配符来实现扩展到机制称为路径名扩展,试试在前面章节使用过的一些技术,将会发现它们实际上就是扩展

echo D* , echo *s , echo [[:upper:]]* , echo /usr/*/share

隐藏文件的路径名扩展

文件名以一个 . 开头的文件都将被隐藏。所以类似 echo * 这样的扩展并不能显示隐藏的文件。

echo .* 似乎是可行的,但是 ... 也将出现在结果中,它们包括当前工作目录以及父目录

在这种情况下,可以使用 ls -d .[!.]?* 来进行更精确的匹配

波浪线扩展

如果把 ~ 用在一个单词的开头,那么他警备扩展为指定用户的主目录名,如果没有指定用户命名,则扩展为当前用户的主目录

echo ~ => /home/me

如果有用户 foo 这个用户

echo ~foo => /home/foo

算数扩展

shell 支持通过扩展来运行算术表达式,这允许我们把 shell 提示符当作计算器使用

echo $((2 + 2)) => 4

算术扩展符使用如下格式

$((expression))

其中, expression 是指包含树枝和算术操作符的算术表达式。

算术扩展只支持整数(全是数字,没有小数),但是可以执行很多不同的运算。下面列出了支持的操作符

+ , - , * , / , % , ** (取幂)

空格在算术表达式中是没有意义的,而且表达式是可以嵌套的。

echo $(($((5**2)) * 3)) => 75

可以使用一对括号来组合多个子表达式

echo $(((5 ** 2) * 3)) => 75

花括号扩展

你可以按照花括号里的模式创建多种文本字符串

echo Front-{A,B,C}-Back => Front-A-Back Front-B-Back Front-C-Back

花括号扩展到模式信息可以包含一个称为前导字符开头部分和一个称为附言的结尾部分。花括号表达式本身可包含一系列逗号分隔的字符串,也可以包含一系列整数或单个字符。这里的模式信息不能包含内嵌的空白

echo _{1..5} => _1 _2 _3 _4 _5

花括号支持嵌套

echo a{A{1, 2}, B{3, 4}}b => aA1b aA2b aB3b aB4b

花括号一般用于创建一系列的文本或目录,比如一个很大的图片集,需要通过以年月格式命名的目录

参数扩展

参数扩展用在 shell 脚本中比直接用在命令行中更为有用,它的许多特性于系统存储小块数据以及给每个小块数据命名性能有关

echo $USER => me

命令替换

命令替换可以把一个命令的输出作为一个扩展模式使用

echo $(ls)

我最喜欢的一种用法如下

ls -l $(which cp)

这里,把 which cp 命令的运行结果作为 ls 命令的一个参数,因此我们无需知道 cp 程序所在的完整路径就能获得 cp 程序对应的列表。这个功能也可用于整个管道中

flie $(ls /usr/bin/* | grep zip)

在早期的 shell 中存在命令替换的另一种语法格式,bash 也支持这种格式,它用反引号代替美元符号和括号

ls -l 'which cp'

@##引用

我们已经知道 shell 有多种方式可以执行扩展,现在我们来学习如何控制扩展

echo The total is $100.00 => The total is 00.00

因为 $1 是一个未定义的变量,所以参数扩展把 $1 的值替换为空字符串。shell 提供了一种称为引用的机制,用来有选择性地避免不想要的扩展

双引号

如果把文本放在双引号中,那么 shell 使用的所有特殊字符都将失去它们的特殊含义,而被看成普通字符。字符 $ , \ , , 除外。这就意味着单数扩展、算术扩展和命令替换依旧有效。比如有一个以空格分隔的文件名,使用引用就可以避免被当成连个独立的参数使用。

ls -l "two words.txt"

请记住,算术扩展,参数扩展和命令替换在双引号中依然有效。

echo "$USER $((2+2)) $(cal)"

单引号

如果我们希望一直所有扩展,那么应该使用单引号。

转义字符

有时候我们只是想要引用单个字符,这种情况我们可以通过在该字符前加上反斜杠 \ 来实现,转义字符也常用来消除文件名中某个字符的特殊含义,包括 $!& ,空格等

如果想要显示反斜杠字符串,可以通过使用两个反斜杠 \\ 来实现。

高级键盘技巧

事实上,省事是命令行最希望达到的目标之一。命令行的另一个目标是,用户在执行任务时无需离开键盘使用鼠标。

编辑命令行

bash 使用了一个名为 Readline 的库来实现命令行的编辑。

光标移动

  • Ctrl-A移动光标到行首
  • Ctrl-E移动光标到行尾
  • Ctrl-F光标向前移动一个字符;和右箭头键作用一样
  • Ctrl-B光标向后移动一格字符;和左箭头键作用一样
  • Alt-F光标向前移动一个字
  • Alt-B光标向后移动一个字
  • Ctrl-L清屏并把光标移到左上角;clear可以完成相同工作

修改文本

  • Ctrl-D删除光标处的字符
  • Ctrl-T使光标处的字符和它面前的字符对调位置
  • Alt-T使光标处的子和它前面的字对调位置
  • Alt-L把光标到字尾的字符转成小写形式
  • Alt-U把光标到字尾的字符转成大写形式

剪切和粘贴文本

Readline 文档中使用术语 killingyanking 来指代通常所说的剪切和粘贴。

  • Ctrl-K剪切从光标到行尾的文本
  • Ctrl-U剪切从光标到行首的文本
  • Alt-D剪切从光标到当前词尾的文本
  • Alt-Backspace剪切从光标到词头的文本。如果光标在一个单词的开头,则剪切前一个单词
  • Ctrl-Ykill-ring缓冲区中的文本粘贴到光标位置

在 bash 帮助文档的“READLINE”部分可以查看 Readline 文档,在这里你可以看到元键(meta key)这个术语。它对应于现代键盘的 Alt 键,不过也并不总是这样。如果你仍然在使用终端,则按下和释放 Esc 键和长按住 Alt 键的效果是相同的。

自动补齐功能

shell 的一种称为“自动补齐”的机制为用户提供了很大的帮助。在输入命令时,按 Tab 键将触发自动补齐功能。

要让自动补齐功能生效,要保证输入的内容不摸棱两可,即必须是确定性的。自动补齐可以针对变量(如果单词以 $ 开头)、用户名(如果单词以 ~ 开头)、命令(如果单词是命令行的第一个单词)和主机名(如果单词以 @ 开头)起作用。注意到自动补齐只对 /etc/hosts 目录下的主机名生效

有一些控制和元键序列与自动补齐功能相关联

  • Alt-$显示所有可能的补齐列表。在大鼠系统中,通过按两次tab键也可实现
  • Alt-*插入所有可能的匹配项。当要用到一个以上的匹配项时,将比较有用

除了这些,其他的组合键可以在 bash man 页面的 READLINE 部分获取更多内容列表

通过 set | less 可以查看当前系统支持的自动补齐脚本。

使用历史命令

bash 会保存使用过命令的历史记录。这些命令的历史记录列表保存在用户主目录的 .bash_history 文件中

搜索历史命令

任何情况下,我们都可以通过如下命令查看历史记录的内容列表

history | less

bash 会默认保存用户最经使用过的 500 个命令,假设我们想找到用来列出 /usr/bin 目录下内容的命令,我们可以这样做

history | grep /usr/bin

假设得到的搜索结果中有一行包含如下内容

88 ls -l /usr/bin > ls-output.txt

其中 88 是这个命令行在历史纪录列表中所处的行号我们可以通过历史记录扩展来立即使用它

!88

bash 将把 !88 扩展为历史列表中第 88 行的内容。bash 也支持以递增方式搜索历史记录。随着输入字符数的增加,bash 会相应地改变搜索范围。按下 Ctrl-R 键,接着输入你要查早的内容,可以开始递增式的搜索。当找到要炸找到的内容时,按下 Enter 键白澳式执行此命令,而按 Ctrl-J 将把搜索的呢日共从历史记录中复制到当前命令行。当要查找下一个匹配项时,再次按下 Ctrl-R 键。

列出了一些用来手动操作历史记录的组合键

  • Ctrl-P移动到前一条历史记录。相当于向上箭头键。

  • Ctrl-N移动到前一条历史记录。相当于向下箭头键。

  • Alt-<移动到历史纪录列表的开始处

  • Alt->移动到历史纪录列表的结尾处

  • Ctrl-R逆向递增式搜索。送当前命令行向前递增搜索

  • Alt-P逆向非递增式搜索。按下这个组合键,接着输入待搜索的字符串,在按Enter键后,搜索才真正开始执行

    Alt-N`向前非递增式搜索

  • Ctrl-O执行历史记录列表的当前项,执行完跳到下一项。若要把历史纪录的一系列命令重新执行一遍,使用该组合键就很方便

历史记录扩展

除了使用 ! 意外,还有其他的扩展特性,当使用 ! string!? string 时,请务必小心谨慎,除非对命令列表非常确信。

还可以查阅 bash 帮助页面的 HISTORY EXPANSION 部分获取更多细节。

  • !!重复执行最后一个执行的命令。按向上箭头键和Enter键可实现相同功能
  • !numbernumber行的命令
  • ! string重复最近的以string开头的命令
  • !? string重复最近的包含string的历史记录