[ian@echidna ~]$ echo Word
Word
[ian@echidna ~]$ echo A phrase
A phrase
[ian@echidna ~]$ echo Where are my spaces?
Where are my spaces?
[ian@echidna ~]$ echo "Here are my spaces." # plus comment
Here are my spaces.
在清单 3 的第三个示例中,所有额外的空间都被压缩到输出的单个空间中。为了避免这种情况,需要使用双引号(")或单引号(')将字符串括起。Bash 使用空格,比如空白、制表符和换行符,来将输入行分离到标记(token)中,后者随后被传递给命令。使用引号引用字符串将保留多余的空格并将完整的字符串作为一个单一标记。在上面的示例中,命令名称之后的每一个标记都是一个参数,因此我们具有的参数分别为 1、2、4 和 1。
echo 命令包含两个选项。echo 通常会在输出的末尾加一个拖尾换行符。使用 -n 选项可以禁用这个行为。使用 -e 选项可以使某些反斜杠转义字符具有特殊的含义。其中一些如表 1 所示。
表 1. Echo 和转义字符
[ian@echidna ~]$ echo -n No new line
No new line[ian@echidna ~]$ echo -e "No new line\c"
No new line[ian@echidna ~]$ echo "A line with a typed
> return"
A line with a typed
return
[ian@echidna ~]$ echo -e "A line with an escaped\nreturn"
A line with an escaped
return
[ian@echidna ~]$ echo "A line with an escaped\nreturn but no -e option"
A line with an escaped\nreturn but no -e option
[ian@echidna ~]$ echo -e Doubly escaped\\n\\tmetacharacters
Doubly escaped
metacharacters
[ian@echidna ~]$ echo Backslash \
> followed by newline \
> serves as line continuation.
Backslash followed by newline serves as line continuation.
注意,bash 在您输入包含不匹配引号的行时显示了一个特殊的 提示 (>)。您的输入字符串继续输入到下一行并包含一个换行符。
Bash shell 元字符和控制操作符(control operator)
Bash 具有多个元字符,这些元字符在未使用引号括起时,可以用来将输入分成多个单词。除了空格以外,这些元字符还包括:
|
&
;
(
)
<
>
我们将在本文的其他部分更详细地讨论其中一些元字符。现在要注意的是,如果您希望包含一个元字符作为文本的一部分,那么必须使用引号括起,或是使用反斜杠 (\) 进行转义,如清单 4 所示。
换行和某些元字符或元字符对也可以用作控制操作符。它们包括:
||
&&
&
;
;;
|
(
)
其中一些控制操作符允许您创建命令序列 或列表。
最简单的命令序列就是由两个命令组成的、用分号 (;) 分隔的序列。所有命令将按顺序执行。在任何可编程的环境中,命令返回成功或失败的指示;Linux 命令通常返回一个零值表示成功,并返回一个非零值表示失败。可以使用 && 和 || 控制操作符来将某些条件处理引入到列表中。如果使用控制操作符 && 来分隔两个命令,那么只有在第一个命令返回 0 表示退出时,才会执行第二个命令。如果使用 || 分隔命令,那么只有在第一个命令返回一个非零的退出代码时,才会执行第二个命令。清单 5 展示了一些使用 echo 命令的命令序列。这些例子并不怎么令人兴奋,因为 echo 返回了 0,但是当我们使用更多的命令时,您会看到更多例子。
清单 5. 命令序列
[ian@echidna ~]$ echo line 1;echo line 2; echo line 3
line 1
line 2
line 3
[ian@echidna ~]$ echo line 1&&echo line 2&&echo line 3
line 1
line 2
line 3
[ian@echidna ~]$ echo line 1||echo line 2; echo line 3
line 1
line 3
退出
您可以使用 exit 命令终止一个 shell。或者可以提供一个 exit 代码作为参数。如果您在图形桌面上的终端窗口中运行 shell,那么窗口将关闭。类似地,如果使用 ssh 或 telnet(举例来说)连接到一个远程系统,那么您的连接将中断。在 bash shell 中,可以同时按下 Ctrl键和 d 键来退出。
让我们查看另一个控制操作符。如果您使用圆括号括起命令或命令列表,那么命令或序列将在一个 sub shell 中执行,因此 exit 命令将退出 sub shell,而不是退出您所在的 shell。清单 6 展示了结合了 && 和 || 以及两个不同的退出代码的示例。
清单 6. Subshell 和序列
[ian@echidna ~]$ (echo In subshell; exit 0) && echo OK || echo Bad exit
In subshell
OK
[ian@echidna ~]$ (echo In subshell; exit 4) && echo OK || echo Bad exit
In subshell
Bad exit
本文后面将介绍更多命令序列。
环境变量
当您在 bash shell 中运行时,有许多东西构成了您的环境,比如提示表单、主目录、工作目录、shell 名称、打开的文件、定义的函数,等等。您的环境包括许多由 bash 或您设置的变量。bash shell 还允许您拥有 shell 变量,可以将其导出 到您的环境,以供运行在 shell 中的其他进程或衍生自当前 shell 的其他 shell 使用。
环境变量和 shell 变量都具有一个名称。可以通过在名称前面加一个 “$” 前缀来引用变量的值。表 2 显示了您将经常遇到的一些 bash 环境变量。
表 2. 一些常见 bash 环境变量
如果喜欢用 vi 类的编辑模式操作历史,那么使用 set -o vi 命令来切换到 vi 模式。使用 set -o emacs 切换回 emacs 模式。当在 vi 模式下检索到一个命令后,您最初将位于 vi 的 insert 模式。vi 编辑器将在本系列另一篇文章中介绍(参见 参考资料 获得系列路线图)。
路径 - 我的命令在哪里?
一些 bash 命令都是内置的,而另一些则来自外部。让我们探讨一下外部命令以及如何运行它们,以及如何判断某个命令是否是内部的。
shell 如何查找命令?
外部命令就是指文件系统中的文件。基本命令管理将在本系列另一篇文章中介绍(参见 参考资料 获得系列路线图)。在 Linux 和 UNIX 系统上,所有文件都作为一个大型树的其中一部分访问,这个树的根为 /。在我们的示例中,我们的当前目录目前为止一直都是用户的主目录。非根用户常常在 /home 目录中有一个主目录,比如这里的主目录为 /home/ian。根用户的主目录通常为 /root。如果输入一个命令名,那么 bash 将在您的路径(path)中查找该命令,路径就是指 PATH 环境变量中以冒号分隔的目录列表。
如果您希望知道在输入特定字符串后将执行哪个命令,那么使用 which 或 type 命令。清单 17 展示了我的默认路径以及多个命令的位置。
清单 17. 找到命令位置
[ian@echidna ~]$ echo $PATH
/usr/lib/qt-3.3/bin:/usr/kerberos/bin:/usr/lib/ccache:/usr/local/bin:/bin:/usr/b
in:/home/ian/bin
[ian@echidna ~]$ which bash env zip xclock echo set ls
alias ls='ls --color=auto'
/bin/ls
/bin/bash
/bin/env
/usr/bin/zip
/usr/bin/xclock
/bin/echo
/usr/bin/which: no set in (/usr/lib/qt-3.3/bin:/usr/kerberos/bin:/usr/lib/ccache
:/usr/local/bin:/bin:/usr/bin:/home/ian/bin)
[ian@echidna ~]$ type bash env zip xclock echo set ls
bash is hashed (/bin/bash)
env is hashed (/bin/env)
zip is /usr/bin/zip
xclock is /usr/bin/xclock
echo is a shell builtin
set is a shell builtin
ls is aliased to `ls --color=auto'
注意,路径中的目录大部分以 /bin 结束。这是一个常见的习惯,但是正如 /usr/lib/ccache 一样,不是必须要求。which 命令报告指出,ls 命令是一个别名(alias)并且无法找到 set 命令。在这种情况下,我们将其解释为不存在或是一个内置命令。type 命令报告指出,ls 命令是一个别名,但是它将 set 命令标识为一个 shell 内置命令。它还报告说有一个内置的 echo 命令以及由 which 发现的位于 /bin 的命令。命令还将按照不同的顺序生成输出。
我们看到,用于列出目录内容的 ls 命令是一个别名。别名可以方便地配置一些命令以使用不同的默认值,或为某个命令提供可替换的名称。在我们的示例中,--color=tty 选项可以根据文件或目录的类型用不同的颜色编码目录清单。尝试运行 dircolors --print-database,看看如何控制不同颜色的编码以及将哪些颜色用于哪类文件中。
每个命令都提供有额外的选项。根据您的具体需求,可以使用任何一个命令。当我确信我将找到一个可执行文件并且只需要它的完整路径说明时,我将使用 which。我发现 type 为我提供了更准确的信息,我需要在某个 shell 脚本中使用到。
运行其他命令
我们在清单 17 中看到,可执行文件拥有一个以 /根目录开头的完整路径。例如,xclock 程序的路径为 /usr/bin/xclock,即位于 /usr/bin 目录下的一个文件。在较旧的系统上,您可能会发现该文件位于 /usr/X11R6/bin 目录。如果命令没有出现在 PATH 说明中,那么可能仍然需要通过指定一个路径和一个命令名来运行它。您可以使用两种类型的路径:
绝对 路径指那些以 / 开头的路径,比如我们在清单 17 中看到的路径(/bin/bash、/bin/env 等等)。
相对 路径指的是相对于您的当前工作目录 的路径,正如 pwd 命令报告的那样。这些命令不是以 / 开头,但是至少包含了一个 /。
不管您的当前工作路径是什么,您都可以使用绝对路径,但是只有在一个命令接近当前目录时,才有可能使用相对路径。假设您在名为 mytestbin 的主目录的子目录中开发经典的 “Hello World!” 程序的新版本。您可能需要使用相对路径来以 mytestbin/hello 形式运行命令。您可以在路径中使用两种特殊名称;使用一个圆点 (.) 表示当前目录,使用一对圆点 (..) 表示当前目录的子目录。由于您的主目录通常并不在(通常来讲也不应该在)PATH 中,因此需要显式地为您希望从主目录中运行的任何可执行文件提供一个路径。例如,如果在主目录中有一个 hello 程序的副本,那么可以使用 ./hello 命令运行它。可以使用 . 和 .. 作为绝对路径的一部分,尽管单个 . 在这种情形下并不是特别有用。您还可以使用一个波浪符号 (~) 来表示自己的主目录,并使用 ~用户名 表示名为 username 的用户的主目录。清单 18 展示了一些示例。
清单 18. 绝对和相对路径
[ian@echidna ~]$ /bin/echo Use echo command rather than builtin
Use echo command rather than builtin
[ian@echidna ~]$ /usr/../bin/echo Include parent dir in path
Include parent dir in path
[ian@echidna ~]$ /bin/././echo Add a couple of useless path components
Add a couple of useless path components
[ian@echidna ~]$ pwd # See where we are
/home/ian
[ian@echidna ~]$ ../../bin/echo Use a relative path to echo
Use a relative path to echo
[ian@echidna ~]$ myprogs/hello # Use a relative path with no dots
-bash: myprogs/hello: No such file or Directory
[ian@echidna ~]$ mytestbin/hello # Use a relative path with no dots
Hello world!
[ian@echidna ~]$ ./hello
Hello world!
[ian@echidna ~]$ ~/mytestbin/hello # run hello using ~
Hello world!
[ian@echidna ~]$ ../hello # Try running hello from parent
-bash: ../hello: No such file or directory
切换工作目录
正如您可以从系统的不同目录中执行程序一样,您也可以使用 cd 命令来切换当前工作目录。cd 的参数必须是一个目录的绝对或相对路径。对于命令,您可以在路径中使用 .、..、~ 和 ~username。如果 cd 不使用任何参数,那么将切换到您的主目录。将一个连字符 (-) 作为参数意味着切换到前一个工作目录。您的主目录存储在 HOME 环境变量,而前一个目录存储在 OLDPWD 变量,因此 cd 相当于 cd $HOME,而 cd - 相当于 cd $OLDPWD。通常我们使用 change directory 指代 change current working directory。
对于命令,还包括一个环境变量 CDPATH,它包含在解析相对路径时执行搜索的以冒号分隔的目录集合(除了当前工作目录外)。如果解析使用了来自 CDPATH 的路径,那么 cd 将输出结果目录的完整路径。通常,成功的目录切换将不会产生输出,只会产生一个新的、很可能更改过的提示。清单 19 显示了一些示例。
清单 19. 切换目录
[ian@echidna ~]$ cd /;pwd
/
[ian@echidna /]$ cd /usr/local;pwd
/usr/local
[ian@echidna local]$ cd ;pwd
/home/ian
[ian@echidna ~]$ cd -;pwd
/usr/local
/usr/local
[ian@echidna local]$ cd ~ian/..;pwd
/home
[ian@echidna home]$ cd ~;pwd
/home/ian
[ian@echidna ~]$ export CDPATH=~
[ian@echidna ~]$ cd /;pwd
/
[ian@echidna /]$ cd mytestbin
/home/ian/mytestbin
手册页
本文讨论的最后一个话题就是教您如何通过手册页以及其他来源获得有关 Linux 命令的文档。
手册页和章节
文档的主要(和传统)来源就是手册页,您可以通过 man 命令访问。图 1 解释了 man 命令的手册页。使用命令 man man 显示这些信息。
图 1. man 命令的手册页
图 1 展示了手册页中的典型内容项:
一个标题,命令名后是使用圆括号括起的章节号
命令名以及同一手册页中描述的相关命令的名称
可用于该命令的选项和参数的摘要
命令的简短描述
有关每个选项的详细信息
您可能会找到有关使用、如何报告 bug、作者信息以及相关命令列表的章节。例如,man 的手册页告诉我们,相关的命令(以及手册页章节)为:
apropos(1)、whatis(1)、less(1)、groff(1) 和 man.conf(5)。
共有 8 个常见手册页章节。手册页通常在您安装包时进行安装,因此如果您没有安装包的话,您很可能并不具备手册页。某些手册页章节可能是空的,或几乎是空的。包含示例内容的常见手册页章节包括:
用户命令(env、ls、echo、mkdir、tty)
系统调用或内核函数(link、sethostname、mkdir)
库例程(acosh、asctime、btree、locale、XML:arser)
与设备相关的信息(isdn_audio、mouse、tty、zero)
文件格式描述(keymaps、motd、wvdial.conf) 游戏(注意,许多游戏现在都是图形化的,并且在手册页系统之外提供了图形化帮助)
杂项(arp、boot、regex、unix utf8)
系统管理(debugfs、fdisk、fsck、mount、renice、rpm)
其他章节可能包括:9 表示 Linux 内核文档、n 表示新文档、o 表示旧文档,l 表示本地文档。
某些条目出现在多个章节中。在我们的例子中,mkdir 出现在章节 1 和 2 中,而 tty 出现在章节 1 和 4 中。您可以指定一个特定的章节,例如,man 4 tty 或 man 2 mkdir,或者可以指定 -a 选项来列出所有合适的手册章节。
您可能已经注意到,图中的 man 为您提供了许多选项。现在,让我们快速了解一下 “另外参见” 部分与 man 有关的一些命令。
另外参见
与 man 有关的两个重要的命令分别为 whatis 和 apropos。whatis 命令在手册页中寻找您指定的名称,并从合适的手册页中显示名称信息。apropos 命令对手册页执行关键字搜索,并列出包含您的关键字的命令。清单 20 解释了这些命令。
清单 20. Whatis 和 apropos 示例
[ian@echidna ~]$ whatis man
man [] (1) - format and display the on-line manual pages
man [] (1p) - display system documentation
man [] (7) - macros to format man pages
man [] (7) - pages - conventions for writing Linux man pages
man.config [] (5) - configuration data for man
man-pages (rpm) - Man (manual) pages from the Linux Documentation Project
man (rpm) - A set of documentation tools: man, apropos and whatis
[ian@echidna ~]$ whatis mkdir
mkdir [] (1) - make directories
mkdir [] (1p) - make directories
mkdir [] (2) - create a directory
mkdir [] (3p) - make a directory
[ian@echidna ~]$ apropos mkdir
mkdir [] (1) - make directories
mkdir [] (1p) - make directories
mkdir [] (2) - create a directory
mkdir [] (3p) - make a directory
mkdirat [] (2) - create a directory relative to a directory file descriptor
mkdirhier [] (1) - makes a directory hierarchy
顺便提一下,如果无法在手册页中找到 man.conf,那么尝试运行 man man.config。
man 命令通过一个分页程序将输出分页到显示中。在大多数 Linux 系统中,很可能使用的是 less 程序。另一个选择是使用较旧的 more 程序。如果希望输出页面,那么指定 -t o选项来格式化页面,以使用 groff 或 troff 程序执行输出。
less 分页器包含多个命令,可以帮助您在显示的输出中搜索字符串。使用 man less 查找更多关于 /(向前搜索)、?(向后搜索)和 n(重复最后一次搜索)以及其他许多命令的信息。
其他文档来源
除了从命令行访问手册页外,Free Software Foundation 提供了大量使用 info 程序处理过的 info 文件。它们提供了大量导航功能,包括跳到其他章节的功能。尝试 man info 或 info info 获得更多信息。并非所有的命令都使用 info 归档,因此如果您是一名 info 用户的话,那么可以同时使用 man 和 info。
手册页还包含许多图形界面,比如 xman(来自 XFree86 Project)和 yelp(Gnome 2.0 帮助浏览器)。
如果无法获得有关某个命令的帮助,尝试用 --help 选项运行命令。这将提供命令的帮助,或者它将告诉您如何获得所需的帮助。