Shell 的命令

命令的类别

Bash可以使用的命令分成四类。

  • 可执行程序
  • Shell 提供的命令
  • Shell 函数
  • 前三类命令的别名

type, whatis

type命令可以显示命令类型。

  1. $ type command

下面是几个例子。

  1. $ type type
  2. type is a shell builtin
  3. $ type ls
  4. ls is aliased to `ls --color=tty'
  5. $ type cp
  6. cp is /bin/cp

whatis命令显示指定命令的描述。

  1. $ whatis ls
  2. ls (1) - list directory contents

apropos

apropos命令返回符合搜索条件的命令列表。

  1. $ apropos floppy
  2. create_floppy_devices (8) - udev callout to create all possible
  3. fdformat (8) - Low-level formats a floppy disk
  4. floppy (8) - format floppy disks
  5. gfloppy (1) - a simple floppy formatter for the GNOME
  6. mbadblocks (1) - tests a floppy disk, and marks the bad
  7. mformat (1) - add an MSDOS filesystem to a low-level

alias, unalias

alias命令用来为命令起别名。

  1. $ alias foo='cd /usr; ls; cd -'
  2. $ type foo
  3. foo is aliased to `cd /usr; ls ; cd -'

上面命令指定foo为三个命令的别名。以后,执行foo就相当于一起执行这三条命令。

注意,默认情况下,别名只在当前Session有效。当前Session结束时,这些别名就会消失。

alias命令不加参数时,显示所有有效的别名。

  1. $ alias
  2. alias l.='ls -d .* --color=tty'
  3. alias ll='ls -l --color=tty'
  4. alias ls='ls --color=tty'

unalias命令用来取消别名。

  1. $ unalias foo
  2. $ type foo
  3. bash: type: foo: not found

which

which命令显示可执行程序的路径。

  1. $ which ls
  2. /bin/ls

which命令用于Shell内置命令时(比如cd),将没有任何输出。

help,man

help命令用于查看Shell内置命令的帮助信息,man命令用于查看可执行命令的帮助信息。

  1. $ help cd
  2. $ man ls

man里面的文档一共有8类,如果同一个命令,匹配多个文档,man命令总是返回第一个匹配。如果想看指定类型的文档,命令可以采用下面的形式。

  1. $ man 5 passwd

script

script命令会将输入的命令和它的输出,都保存进一个文件。

  1. $ script [file]

如果没有指定文件名,则所有结果会保存进当前目录下typescript文件。结束录制的时候,可以按下Ctrl + d

export

export命令用于将当前进程的变量,输出到所有子进程。

命令的连续执行

多个命令可以写在一起。

Bash 提供三种方式,定义它们如何执行。

  1. # 第一个命令执行完,执行第二个命令
  2. command1; command2
  3. # 只有第一个命令成功执行完(退出码0),才会执行第二个命令
  4. command1 && command2
  5. # 只有第一个命令执行失败(退出码非0),才会执行第二个命令
  6. command1 || command2

上面三种执行方法的退出码,都是最后一条执行的命令的退出码。

bash 允许把命令组合在一起。可以通过两种方式完成;要么用一个 group 命令,要么用一个子 shell。 这里是每种方式的语法示例:

组命令:

  1. { command1; command2; [command3; ...] }

子 shell

  1. (command1; command2; [command3;...])

这两种形式的不同之处在于,组命令用花括号把它的命令包裹起来,而子 shell 用括号。值得注意的是,鉴于 bash 实现组命令的方式, 花括号与命令之间必须有一个空格,并且最后一个命令必须用一个分号或者一个换行符终止。

那么组命令和子 shell 命令对什么有好处呢? 它们都是用来管理重定向的。

  1. { ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt

使用一个子 shell 是相似的。

  1. (ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt

组命令和子 shell 真正闪光的地方是与管道线相结合。 当构建一个管道线命令的时候,通常把几个命令的输出结果合并成一个流是很有用的。 组命令和子 shell 使这种操作变得很简单。

  1. { ls -l; echo "Listing of foo.txt"; cat foo.txt; } | lpr

这里我们已经把我们的三个命令的输出结果合并在一起,并把它们用管道输送给命令 lpr 的输入,以便产生一个打印报告。

虽然组命令和子 shell 看起来相似,并且它们都能用来在重定向中合并流,但是两者之间有一个很重要的不同。 然而,一个组命令在当前 shell 中执行它的所有命令,而一个子 shell(顾名思义)在当前 shell 的一个 子副本中执行它的命令。这意味着运行环境被复制给了一个新的 shell 实例。当这个子 shell 退出时,环境副本会消失, 所以在子 shell 环境(包括变量赋值)中的任何更改也会消失。因此,在大多数情况下,除非脚本要求一个子 shell, 组命令比子 shell 更受欢迎。组命令运行很快并且占用的内存也少。

当我们发现管道线中的一个 read 命令 不按我们所期望的那样工作的时候。为了重现问题,我们构建一个像这样的管道线:

  1. echo "foo" | read
  2. echo $REPLY

该 REPLY 变量的内容总是为空,是因为这个 read 命令在一个子 shell 中执行,所以它的 REPLY 副本会被毁掉, 当该子 shell 终止的时候。因为管道线中的命令总是在子 shell 中执行,任何给变量赋值的命令都会遭遇这样的问题。 幸运地是,shell 提供了一种奇异的展开方式,叫做进程替换,它可以用来解决这种麻烦。进程替换有两种表达方式:

一种适用于产生标准输出的进程:

  1. <(list)

另一种适用于接受标准输入的进程:

  1. >(list)

这里的 list 是一串命令列表:

为了解决我们的 read 命令问题,我们可以雇佣进程替换,像这样。

  1. read < <(echo "foo")
  2. echo $REPLY

进程替换允许我们把一个子 shell 的输出结果当作一个用于重定向的普通文件。事实上,因为它是一种展开形式,我们可以检验它的真实值:

  1. [me@linuxbox ~]$ echo <(echo "foo")
  2. /dev/fd/63

通过使用 echo 命令,查看展开结果,我们看到子 shell 的输出结果,由一个名为 /dev/fd/63 的文件提供。