IBM的LPI复习资料之LPI101-Topic103:GNU和Unix命令(1)Linux命令行

0 摘要

GUI图形界面是友好的,但是却不能发挥Linux系统的真正威力,没有什么能够替代命令行。本文中,作者介绍了bash这个shell的主要特征,着重强调了对LPI考试非常重要的那些特征。学习了本文以后,你将能够轻松地使用echo、exit等命令,设置环境变量,查看系统信息。

1 概述

本文对bash的主要特征进行了简要介绍,包含如下几个话题:
  • 通过命令行与shell和各种工具程序进行交互;
  • 使用命令以及命令序列
  • 定义,修改,引用,导出环境变量
  • 查看历史命令与编辑
  • 启动路径中的的命令以及路径外的命令
  • 使用手册页来获取命令的帮助
本文帮助读者为LPI-101的103.1目标做好准备。本目标的权重是4.

2 bash 入门

bash是Linux中可用的众多shell之一,也叫做Bourne-again shell,以Stephen Bourne(此人是另一个早期shell-/bin/sh的发明人)命名。bash大体上与sh兼容,但在功能和可编程能力上大大提高了。bash综合了ksh和csh的特征,并致力于成为一个POSIX兼容的shell。

在深入学习bash之前,先回忆一下shell是一个接收并执行命令的程序。它也支持编程结构,允许使用简单的语法来构建复杂的命令组合。这些复杂的命令组合(就是脚本)也可以存储为文件从而可以整体作为一个命令。在一个典型的Linux系统中,很多的命令其实就是脚本。

shell拥有一些内建的命令,例如cd,break,exec。其他的命令则是外部的程序。

shell也使用三种标准的I/O流:
  • stdin 是标准的输入流,提供到命令的输入;
  • stdout是标准的输出流,显示命令的输出;
  • stderr是标准的错误输出流,显示命令的错误输出。
输入流提供程序的输入,通常来自终端键盘。输出流打印文本字符,通常流向终端显示器。终端曾经是一台ASCII打字机和显示器,但是现在通常是一个图形桌面的窗口。如何重定向这些输入输出流会在另一篇文章中单独介绍。

文章接下来,我们将假设读者知道如何获取一个shell提示符。如果你还不知道,另一篇文章“Linux新手的基本任务”会告诉你如何做到。

如果你使用一个没有图形桌面的Linux系统,或者在一个图形桌面上打开一个终端窗口,一个提示符将会出现,表示欢迎你的意思。可能如下图所示:
[db2inst1@echidna db2inst1]$
ian@lyrebird:~>
$
如果是使用root用户登录,提示符可能会看起来如下:
[root@echidna ~]#
lyrebird:~ #
#

本系列文章中的代码示例都是从使用默认提示符的实际Linux系统中剪切拷贝出来的。root用户提示符以#结尾,一般用户的提示符则以$结尾,所以可以非常清楚的区分两者。这种惯例在很多书籍中都是一致的。如果发现那里不对劲了,检查一下提示符(从而确认当前的用户身份)。

3 命令与命令序列

既然你已经有了一个提示符,来看看可以使用它做些什么。shell主要的功能就是解释你的命令来让你和Linux系统进行交互。在Linux系统(以及Unix)系统中,命令包括一个名字、然后是选项和参数。有些命令既没有选项也没有参数,有些命令则只有其中的一个。

如果一行中有个#,那么#后面的同行字符都会被忽略。这样看来,#就是一个注释符号,同时上面我们提到#还是root用户提示符的一部分。这并不冲突,因为从上下文中可以清楚地知道#到底代表了什么。

3.1 echo命令

echo命令打印它的参数到终端上显示,如下:

[smstong@localhost test]$ echo WordWord
[smstong@localhost test]$ echo A phraseA phrase
[smstong@localhost test]$ echo Where        are      my          spaces?Where are my spaces?
[smstong@localhost test]$ echo "Here         are     my          spaces." # plus commentHere         are     my          spaces.
[smstong@localhost test]$ 

在第三行,所有的连续空格在输出时都被压缩成了一个空格。为了避免这样,你需要使用引号,可以是单引号也可以是双引号。bash使用空白(如空格、Tab、换行)来分割单词,然后把分割后的单词作为参数传递给你的命令。引号包围的字符串会保留空白字符,并且将整个字符串作为单一参数。在上面的例子中,命令后面的每一个单词都是参数,所以上述命令分别有1,2,4,1个参数。


echo这个命令有很多选项。正常情况下,echo会在输出最后增加一个换行。使用-n选项可以压制这个特性。使用-e选项来使得特定的转义字符有特殊的意义。这些转义字符如下:


Escape
sequence
Function
\a Alert (bell)
\b Backspace
\c Suppress trailing newline (same function as -n option)
\f Form feed (clear the screen on a video display)
\n New line
\r Carriage return
\t Horizontal tab

转义与续行

在bash中使用反斜杠存在一个小问题。当反斜杠不在引号内时,它的作用只是保持后面字符的字面值。这对于特殊的shell元字符非常有用,后面会讲到。这条规则还存在一个例外,那就是当一个反斜杠紧跟着一个换行符时,将导致这两个字符都被吞没,而被解释为续行。这在脚本中分割过长的行时非常有用。

为了让echo以及其他能处理转义字符的命令能正确处理上面的序列,你必须把这些转义序列放入引号内,除非你再使用第二个反斜杠让shell保留第一个反斜杠。下面的例子展示了反斜杠的各种用法。

[root@centos192 /]# echo -n No new line
No new line[root@centos192 /]# echo -e "No new line\c"
No new line[root@centos192 /]# echo "A line with a typed
> return"
A line with a typed
return
[root@centos192 /]# echo -e "A line with an escaped\nreturn"
A line with an escaped
return
[root@centos192 /]# echo "A line with an escaped\nreturn but no -e option"
A line with an escaped\nreturn but no -e option
[root@centos192 /]# echo -e Doubly escaped\\n\\tmetacharacters
Doubly escaped
	metacharacters
[root@centos192 /]# echo Backslash \
> followed by newline.\
> serves as line continuation.
Backslash followed by newline.serves as line continuation.

注意当没有匹配的引号而回车时,bash会显示一个特殊的>提示符,这样输入会继续并且包含一个换行符。

元字符和控制操作符

bash有一些元字符,当没有被引号包含时,也用来分割输入,除了空白,这些元字符还有:

  • |
  • &
  • ;
  • (
  • )
  • <
  • >
接下来我们还会对其中的一些进一步讨论。现在,请注意如果你想在你的文本里使用一个元字符,它必须被引号包围或者使用反斜杠转义。

换行符和一些特定的元字符或者元字符对还会组成控制操作符,如:

  • ||
  • &&
  • &
  • ;
  • ;;
  • |
  • (
  • )
其中的一些操作符允许你来构造命令序列。

最简单的命令序列就是使用;来分割两个命令。这两个命令按顺序执行。在任何编程环境中,命令执行完成后会返回一个表示成功或失败的标志。对于Linux命令来说,成功返回0,失败返回非零。通过使用&&和||可以引入条件处理。如命令A&&命令B,此时当且仅当命令A返回0时,命令B才会执行。下面是使用echo的一些命令序列,因为echo总是返回0,所以这些例子可能不怎么好玩,后面当我们有更多地命令可以使用时会实验更多有趣的例子。

[root@centos192 /]# echo line1;echo line2; echo line3
line1
line2
line3
[root@centos192 /]# echo line1&&echo line2&&echo line3
line1
line2
line3
[root@centos192 /]# echo line1||echo line2; echo line3
line1
line3

译者注:bash解释输入序列的过程:查找\,进行转义-------对于引号内的字符串作为一个整体不处理,对于引号外的则查找控制操作符,从而分割开不同的命令或其他处理----------每一个命令序列在使用空白来分割选项与参数---------执行每一个命令序列。

3.2 exit命令

可以使用exit命令来终止shell。可以给exit提供一个参数作为退出状态值。如果当前shell运行在图形桌面的一个窗口中,这个窗口就会关闭。类似地,如果使用的是ssh或者telnet通过网络连接,那么连接就会断开。在bash中,可以使用Crl+d快捷键来退出。

让我们看看另外一个控制操作符。如果你把一个命令或多个命令列表使用括号包围起来,这个命令或命令序列将会在一个子shell中执行,所以exit命令将会退出这个子shell而不是你的工作shell。如下图所示:
[smstong@localhost test]$ (echo In subshell; exit 0) && echo OK || echo Bad exit
In subshell
OK
[smstong@localhost test]$ (echo In subshell; exit 4) && echo OK || echo Bad exit
In subshell
Bad exit

4 环境变量

当运行一个bash时,有很多东西构成了你的环境。如提示符的格式、家目录、工作目录、shell名称、已经打开的文件、定义的函数等等。还包括bash已经为你设置好的一组变量。bash还允许你使用shell变量,这种变量可以导出到你的环境中供shell中运行的其他进程使用,也可以供当前shell派生的其他shell中使用。
无论是环境变量还是shell变量都有一个名字。引用变量的值时需要在变量名之前加一个$。一些通用的bash环境变量如下所示:

Name Function
USER The name of the logged-in user
UID The numeric user id of the logged-in user
HOME The user's home directory
PWD The current working directory
SHELL The name of the shell
$ The process id (or PIDof the running bash shell (or other) process
PPID The process id of the process that started this process (that is, the id of the parent process)
? The exit code of the last command

下面是这些变量值的一个实例:
[ian@echidna ~]$ echo $USER $UID
ian 500
[ian@echidna ~]$ echo $SHELL $HOME $PWD
/bin/bash /home/ian /home/ian
[ian@echidna ~]$ (exit 0);echo $?;(exit 4);echo $?
0
4
[ian@echidna ~]$ echo $$ $PPID
2559 2558

创建或者设置shell变量的方法是在变量名后紧跟着一个=。如果这个变量已经存在,则会对其重新赋值。变量名是大小写敏感的,所以var1和VAR1是不同的两个变量。按照惯例,变量尤其是导出的变量,其名字是大写的,但这不是必须的。从技术上讲,$$和$?是shell参数而不是变量。他们只能被引用,不能被赋值。
创建一个shell变量后,经常需要把它导出到环境中,以备同一个shell启动的其他程序使用。导出的变量对父shell不可见。使用export命令导出一个变量名。作为在bash中的捷径,变量的赋值和导出可以一步完成。
为了展示赋值和导出,我们在bash中运行bash命令,然后在这个新bash中运行ksh。我们使用ps命令显示正在运行的命令。

[ian@echidna ~]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
 2559  2558 -bash
[ian@echidna ~]$ bash
[ian@echidna ~]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
 2811  2559 bash
[ian@echidna ~]$ VAR1=var1
[ian@echidna ~]$ VAR2=var2
[ian@echidna ~]$ export VAR2
[ian@echidna ~]$ export VAR3=var3
[ian@echidna ~]$ echo $VAR1 $VAR2 $VAR3
var1 var2 var3
[ian@echidna ~]$ echo $VAR1 $VAR2 $VAR3 $SHELL
var1 var2 var3 /bin/bash
[ian@echidna ~]$ ksh
$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
 2840  2811 ksh
$ export VAR4=var4
$ echo $VAR1 $VAR2 $VAR3 $VAR4 $SHELL
var2 var3 var4 /bin/bash
$ exit
[ian@echidna ~]$ echo $VAR1 $VAR2 $VAR3 $VAR4 $SHELL
var1 var2 var3 /bin/bash
[ian@echidna ~]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
 2811  2559 bash
[ian@echidna ~]$ exit
exit
[ian@echidna ~]$ ps -p $$ -o "pid ppid cmd"
  PID  PPID CMD
 2559  2558 -bash
[ian@echidna ~]$ echo $VAR1 $VAR2 $VAR3 $VAR4 $SHELL
/bin/bash

前面我们说过,使用引号时,可以使用单引号,也可以使用双引号。但是这两者存在一个重大的差异。bash会解释双引号包含的变量,但是不会解释单引号中的变量。前面我们在当前shell中启动了另一个shell,然后获得了一个不同的进程ID。使用-c参数可以向新shell中传递命令,新shell执行完这个命令后马上就返回。如果你传递一个引号包含的字符串作为命令,外层shell将会去掉引号并传递这个字符串。如果使用的是双引号,字符串传递前会进行变量解析,所以结果可能与预期不同。下面的例子阐述了这些概念。
[ian@echidna ~]$ echo "$SHELL" '$SHELL' "$$" '$$'
/bin/bash $SHELL 2559 $$
[ian@echidna ~]$ bash -c "echo Expand in parent $$ $PPID"
Expand in parent 2559 2558
[ian@echidna ~]$ bash -c 'echo Expand in child $$ $PPID'
Expand in child 2845 2559

到目前为止,所有的变量引用都以空格结尾,所以可以清楚地确定变量名的结束位置。实际上,变量名只能由字母、数字、和下划线组成。当变量名后面不是上述字符时,shell就可以确定变量名的结束。但有时候变量名后面接的还是字母、数字或者下划线,这带来了混淆。(译者注:比如$xy表示是变量x的值和字母y,也可以表示变量xy的值,实际上测试表明,bash是按照最长匹配来确定的,也就是表示的是xy变量的值)。这种情况下,需要使用大括号来包围变量名。如下所示:
[ian@echidna ~]$ echo "-$HOME/abc-"
-/home/ian/abc-
[ian@echidna ~]$ echo "-$HOME_abc-"
--
[ian@echidna ~]$ echo "-${HOME}_abc-"
-/home/ian_abc-

4.1 env命令

不带任何参数和选项的env命令会打印出当前的环境变量。除此之外,你还可以使用它来为一个命令的执行定制环境。-i选项会在执行命令之前清除所有的环境变量。-u选项则让你可以去掉不想要的变量。
下面的例子中,显示了不带参数的env输出的一部分,然后是不使用父环境启动不同shell的三个例子。在我们讨论之前,要看清楚。

注意:如果你的系统里没有安装ksh或者tcsh,你需要做实验之前安装它们。

[ian@echidna ~]$ env
HOSTNAME=echidna
SELINUX_ROLE_REQUESTED=
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
SSH_CLIENT=9.27.206.68 1316 22
SELINUX_USE_CURRENT_RANGE=
QTDIR=/usr/lib/qt-3.3
QTINC=/usr/lib/qt-3.3/include
SSH_TTY=/dev/pts/3
USER=ian
...
_=/bin/env
OLDPWD=/etc
[ian@echidna ~]$ env -i bash -c 'echo $SHELL; env'
/bin/bash
PWD=/home/ian
SHLVL=1
_=/bin/env
[ian@echidna ~]$ env -i ksh -c 'echo $SHELL; env'
/bin/sh
_=/bin/env
PWD=/home/ian
_AST_FEATURES=UNIVERSE - ucb
[ian@echidna ~]$ env -i tcsh -c 'echo $SHELL; env'
SHELL: Undefined variable.

可以看到,bash会设置SHELL变量,但是不会把它导出到环境变量中,bash预先会设置三个环境变量但是不包括SHELL。在ksh中,有二个预设的环境变量,但是我们尝试打印SHELL的值时会输出空行(译注:觉得原文有错误,与实验结果不符,请读者自行斟酌)。最后,tcsh没有创建任何环境变量,当我们引用SHELL值的时候给出一个错误。

4.2 unset和set命令

上面的例子中展示了不同的shell处理变量和环境的不同。本文关注的是bash,但是要注意并不是所有的shell都有一致的表现。另外,即使是同一种shell,根据是否是登陆shell,shell自身的表现也不相同。目前,我们简单的把你登陆系统后获得的shell叫做登陆shell。除此之外,你可以启动shell来让它表现得像登陆shell一样。上面例子中,通过env -i启动的三个shell都不是登陆shell。使用shell命令自身的-l选项来看看登陆shell和非登陆shell的不同。
我们尝试在三种非登陆shell中显示SHELL变量的值。

  1. 当bash 启动后,会设置SHELL变量,但是不会自动导出该变量到环境;
  1. 当ksh启动后,不设置SHELL变量,但是引用一个未定义的环境变量与引用一个空值变量相同;(译注:与实验不符)
  1. 当tcsh启动后,不设置SHELL变量。默认的表现与上两种shell不同,当使用一个不存在的变量时,tcsh会报告错误。

你可以使用unset命令来去掉一个变量,并把它从shell变量表中删除。如果这个变量被导出到了环境中,它也会从环境中被删除。你可以使用set命令来控制bash(以及其他shell)的工作方式。set是一个shell的内建命令,所以其选项和参数是shell特定的。在bash中,-u选项会让bash遇到一个未定义变量时不是当成空变量而是报告错误。你可以使用set - 来开启各种特性,而使用set + 来关闭特性。可以使用echo $-来查看当前的set选项。

[ian@echidna ~]$ echo $-
himBH
[ian@echidna ~]$ echo $VAR1

[ian@echidna ~]$ set -u;echo $-
himuBH
[ian@echidna ~]$ echo $VAR1
-bash: VAR1: unbound variable
[ian@echidna ~]$ VAR1=v1
[ian@echidna ~]$ VAR1=v1;echo $VAR1
v1
[ian@echidna ~]$ unset VAR1;echo $VAR1
-bash: VAR1: unbound variable
[ian@echidna ~]$ set +u;echo $VAR1;echo $-

himBH


如果不带任何选项使用set命令,它会显示所有的shell变量以及他们的值。还有另外一个命令: declare ,可以用来创建,导出,显示shell变量的值。通过man手册可以学到更多的关于declare和set的知识,后面我们会讨论man。
(译者注:env显示的是环境变量,set显示的是shell变量,两者并不相同,尽管有一些变量同时属于两者)

4.3 exec命令
本节中最后学习的一个命令是exec。你可以使用exec来运行另外一个程序来取代当前shell。下面例子中,启动了一个子bash,然手使用exec运行ksh来取代这个子bash。从ksh退出以后,你会回到最原始的那个bash中。
(译者注:exec会使用调用者进程执行另外一个程序,而不是启动一个新的进程来执行这个程序)。
[ian@echidna ~]$ echo $$
2852
[ian@echidna ~]$ bash
[ian@echidna ~]$ echo $$
5114
[ian@echidna ~]$ exec ksh
$ echo $$
5114
$ exit
[ian@echidna ~]$ echo $$
2852
            

5 使用uname获取系统信息

uname命令打印当前系统和内核的相关信息。下面例子显示了uname的不同选项以及输出结果,可用的选项在后面的表中。

                    
[ian@echidna ~]$ uname
Linux
[ian@echidna ~]$ uname -s
Linux
[ian@echidna ~]$ uname -n
echidna.raleigh.ibm.com
[ian@echidna ~]$ uname -r
2.6.29.6-217.2.3.fc11.i686.PAE
[ian@echidna ~]$ uname -v
#1 SMP Wed Jul 29 16:05:22 EDT 2009
[ian@echidna ~]$ uname -m
i686
[ian@echidna ~]$ uname -o
GNU/Linux
[ian@echidna ~]$ uname -a
Linux echidna.raleigh.ibm.com 2.6.29.6-217.2.3.fc11.i686.PAE 
#1 SMP Wed Jul 29 16:05:22 EDT 2009 i686 i686 i386 GNU/Linux


Table 3. Options for uname
Option Description
-s Print the kernel name. This is the default if no option is specified.
-n Print the nodename or hostname.
-r Print the release of the kernel. This option is often used with module-handling commands.
-v Print the version of the kernel.
-m Print the machine's hardware (CPU) name.
-o Print the operating system name.
-a Print all of the above information.

本例子的运行环境是运行在Intel CPU上的Fedora 11发行版。uname在大多数Unix和类Unix系统中可用。输出的信息根据发行版、版本、以及机器硬件的不同而不同,下面是运行在AMD Athlon 64上的Ubuntu 9.04结果:

ian@attic4:~$ uname -a
Linux attic4 2.6.28-14-generic #47-Ubuntu SMP Sat Jul 25 01:19:55 UTC 2009 x86_64
GNU/Linux

6 命令历史记录

当你敲入命令时,你会发现可能使用同一个命令多次,或者完全相同,或者只是参数不同。幸好bash维护着一个命令历史记录。默认这个功能是开启的。你可以使用set +o来关闭这个功能,也可使用set -o 来重新启用这个功能。bash保存的历史命令的个数取决于HISTSIZE这个环境变量。还有很多对命令历史记录进行控制的设置项,具体请参考bash的手册。

如下命令用来使用历史记录功能:

  • history 显示全部历史命令
  • history N 显示最后N条执行过的命令
  • history -d N 从历史记录中删除N行,当某个命令行包含密码时,通常使用这个功能删除它。(译者注: history -c 删除全部历史记录)
  • !! 刚刚执行过的那条命令
  • !N 历史记录中第N条命令
  • !-N 历史记录中倒数第N条命令
  • !# 你正在输入的这条命令
  • !string  开头匹配string的最近的那条命令
  • !?string? 包含string的最近的那条命令
你也能够使用:接着特定的值来获取或者修改一条历史命令的一部分,下面展示了这些应用:
[ian@echidna ~]$ echo $$
2852
[ian@echidna ~]$ env -i bash -c 'echo $$'
9649
[ian@echidna ~]$ !!
env -i bash -c 'echo $$'
10073
[ian@echidna ~]$ !ec
echo $$
2852
[ian@echidna ~]$ !en:s/$$/$PPID/
env -i bash -c 'echo $PPID'
2852
[ian@echidna ~]$ history 6
  595  echo $$
  596  env -i bash -c 'echo $$'
  597  env -i bash -c 'echo $$'
  598  echo $$
  599  env -i bash -c 'echo $PPID'
  600  history 6
[ian@echidna ~]$ history -d598

你还可以以交互方式来编辑历史命令。bash使用readline库赖管理命令编辑和历史记录。默认情况下,移动和编辑命令的按键或按键组合类似于GNU Emacs编辑器。Emacs按键组合通常使用C-x或者M-x来表达,其中x表示一般的按键,C表示Control键,M表示Meta键。在典型的PC系统上,Ctrl键就是Emacs的Control键,Alt键就是Meta键。下表展示了一些可用的编辑功能按键。除了这些功能,像光标移动上下左右键、Home和End键等按照正常的逻辑使用即可。其他的功能以及使用readline初始化文件(通常文件名为inputrc)来定制这些配置项,请参考手册。

Command Common PC key Description
C-f Right arrow Move one space to the right
C-b Left arrow Move one space to the left
C-p Up arrow Move one command earlier in history
C-n Down arrow Move one command later in history
C-r   Incremental reverse search. Type a letter or letters to search backwards for a string. Press C-r again to search for the next previous occurrence of the same string.
M-f Alt-f Move to beginning of next word; GUI environments usually take this key combination to open the Filemenu of the window
M-b Alt-b Move to beginning of previous word
C-a Home Move to beginning of line
C-e End Move to end of line
Backspace Backspace Delete the character preceding the cursor
C-d Del Delete the character under the cursor (Del and Backspace functions may be configured with opposite meanings)
C-k Ctrl-k Delete (kill) to end of line and save removed text for later use
M-d Alt-d Delete (kill) to end of word and save removed text for later use
C-y Ctrl-y Yank back text removed with a kill command


如果你你不喜欢Emacs风格的编辑模式,而喜欢vi模式,那么可以通过set -o vi来切换到vi模式。使用set -o emacs可以重新回到Emacs模式。当你在vi模式获得一条命令时,会进入vi的插入模式。

7 Paths-在哪里查找命令

bash中有些命令是内建的,其余的都是外部命令。现在我们看看如何运行外部命令,以及如何分辨一条命令是否是内建的。

7.1 shell去哪里查找命令文件

外部命令就是存储于文件系统中的文件。基本的文件管理在另外的文章中说明。在Linux和Unix系统中,所有的文件都存在一个单树模型上,树根就是/。到目前为止的所有例子中,我们当前的目录都是用户的家目录。非根用户通常在/home下有一个家目录,如/home/ian。根用户的家目录是/root。当你键入一个命令名字的时候,bash会在path中查找这个命令,它其实是一个用:分隔的目录列表,通过环境变量PATH来指定。

which 或者 type命令可以用来查看一个命令名对应的文件。下表显示了默认的path以及一些命令对应的文件。
[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'

注意,path中的路径大多数都以/bin结束。这是通常的惯例但不是必须的。which命令报告了:ls命令是一个alias,set命令没有找到(可能是没有这个文件或者这是一个内建命令)。type命令报告了:ls是一个alias,set是一个内建命令,echo也是一个内建命令(注意同时也存在外部的echo文件)。which和type报告的顺序也不相同。

可以看到,用来列出目录内容的ls命令是一个alias。alias是为了定制命令的选项(非默认)或者提供一个别名的便捷方式。在我们的例子中,--color=auto选项让目录列表根据文件或者目录的类型分别显示为不同的颜色。运行dircolors --print-database 看看颜色编码是如何控制的,以及各种类型文件对应的颜色。

这些命令(which和type)中的任何一个都有额外的选项。根据你的需要,你可以使用任何一个命令。当想要知道一个命令的路径时,我会使用which;当我写脚本时我则使用type来获取更精确的信息。

7.2 运行其他的命令

在上面的例子中,我们看到所有的可执行文件都有一个以/开头的完整路径,例如xclock对应/usr/bin/xclock,在一些老系统中可能是/usr/X11R6/bin/xclock。如果一个命令不在PATH指定的路径中,你仍然可以通过指定一个路径加上命令名来执行它。有两种指定路径的方式:
  • 绝对路径
绝对路径以/开头,如/bin/bash,/bin/env等。
  • 相对路径
相对路径是相对于当前的工作路径(pwd可以获得当前路径)。
无论你的当前路径是什么,都可以使用绝对路径。但是当一个命令离当前路径很近时,使用相对路径可能更方便。假设你开发了一个新版本的"hello world"程序,安装到了你的家目录下,名字为mytestbin。你可以使用相对路径来执行:mytestbin/hello。使用路径是有两个特殊的名字,一个是(.),代表当前目录;另一个是(..),代表当前目录的父目录。因为家目录通常都不在PATH指定的路径表中(也不应该在),要运行家目录下的程序是,就需要显式地提供一个路径。例如你可能把hello拷贝到了家目录下,那么就需要通过./hello来执行。你可以使用~来表示你的家目录,~username表示用户username的家目录。一些例子如下:
[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

7.3 改变工作目录

就像你可以运行各种不同目录下的文件一样,你可以可以使用cd命令来任意改变当前目录。cd的参数必须是绝对路径或者相对路径,与命令一样,你也可以使用., .., ~, ~username等。如果不带任何参数运行cd,则会把当前目录设置为你的家目录。cd - 则会设置前一个当前目录为当前目录。你的家目录存在环境变量HOME中,前一个当前目录则存在OLDPWD中,所以cd相当于 cd $HOME,cd - 则相当于 cd $OLDPWD。通常我们说改变目录而不是改变当前工作目录。
对于命令来说,还有一个相关的环境变量CDPATH。它包含了以:分隔的目录集合,当解析相对路径时,将从这些集合里查找。如果从CDPATH里找到了这个路径,cd会打印出完整的结果路径。正常情况下(不是从CDPATH解析到的),cd不会打印任何结果,只是bash的提示符会显示改变后的路径。下面是一些例子:
[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

(译者注:当存在CDPATH时,解析相对路径时,优先以CDPATH中的路径为基础,如果CDPATH中找不到匹配项,才以当前目录为基础查找)。

8 man手册页

本文最后一个话题是告诉你如何在手册页以及其他文档中找到与Linux命令相关的信息。

8.1 手册页和分节

主要的(传统的)文档资源就是手册页(man pages),通过man命令来读取。下图显示了man这个命令自己的手册页,执行的命令是man man。

上图显示了手册页典型的组成部分:
  • 头部----包括命令名、分节号,其中分节号用小括号包围。
  • 命令名以及与其在同一手册页描述的相关的命令。
  • 命令的选项和参数语法格式。
  • 命令的简短描述
  • 每一个选项的详细描述
你可能还会发现有其他的内容,如怎样报告bug,作者信息,相关命令列表等。例如,man命令的手册页告诉我们相关的命令(以及他们的手册分节号)有:
apropos(1), whatis(1), less(1), groff(1), man.config(5).

一共有8个常用的手册分节。手册页一般都是随软件一起被安装的,如果没有安装一个软件包,很可能相关的手册页也就没有安装。类似的,有些手册页分节可能是空空的。常用的分节以及各自的例子如下:
  • 分节1 -------普通用户命令(env,ls,echo,mkdir ,tty)
  • 分节2--------系统调用或者内核函数(link, sethostname, mkdir)(译者注:此处的是函数,不是同名的命令)
  • 分节3--------库函数(acosh, asctime, btree, locale, XML::Parser)
  • 分节4--------设备相关的信息(isdn_audio, mouse, tty, zero)
  • 分节5--------文件格式描述(keymaps, motd, wvdial.conf)
  • 分节6--------游戏(注意现在很多游戏都是图形化的,也提供了手册页外的图形化帮助信息)
  • 分节7--------杂项(arp, boot, regex, unix utf8)
  • 分节8--------系统管理命令(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 相关的几个命令。

8.2 相关命令

两个与man相关的重要的命令是whatis和apropos。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。如果你想打印手册页,使用groff或者troff命令加上-t选项来格式化分页。

less这个分页程序拥有很多命令来帮助你搜索字符串。使用man less来查看关于/(正向搜索)、?(反向搜索),n(重复搜索)以及其他命令的相关信息。

8.3 其他的文档资源

从命令行除了可以查看手册页外,自由软件基金会还创建了可以使用info命令查看的info信息。info提供了广泛的导航功能来跳转到其他的部分。使用man info或者 info info来深入学习之。并不是所有的命令都配有Info页,所以如果你是一个info用户,可能发现必须使用man 和info两个命令。

还存在一些可以访问手册页的图形化程序,如xman(来自XFree86项目),yelp(Gnome2.0的帮助浏览器)。

如果你还是找不到某个命令的帮助信息,试着使用--help选项运行这个命令。这可能会提供这个命令的使用帮助,或者告诉你如何去获得相关的帮助信息。


你可能感兴趣的:(IBM的LPI复习资料之LPI101-Topic103:GNU和Unix命令(1)Linux命令行)