GUI图形界面是友好的,但是却不能发挥Linux系统的真正威力,没有什么能够替代命令行。本文中,作者介绍了bash这个shell的主要特征,着重强调了对LPI考试非常重要的那些特征。学习了本文以后,你将能够轻松地使用echo、exit等命令,设置环境变量,查看系统信息。
[db2inst1@echidna db2inst1]$ ian@lyrebird:~> $ |
[root@echidna ~]# lyrebird:~ # # |
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
[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
当运行一个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 |
[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 |
[ian@echidna ~]$ echo "-$HOME/abc-" -/home/ian/abc- [ian@echidna ~]$ echo "-$HOME_abc-" -- [ian@echidna ~]$ echo "-${HOME}_abc-" -/home/ian_abc- |
不带任何参数和选项的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变量的值。
你可以使用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变量,两者并不相同,尽管有一些变量同时属于两者)。
[ian@echidna ~]$ echo $$ 2852 [ian@echidna ~]$ bash [ian@echidna ~]$ echo $$ 5114 [ian@echidna ~]$ exec ksh $ echo $$ 5114 $ exit [ian@echidna ~]$ echo $$ 2852 |
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 |
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 |
当你敲入命令时,你会发现可能使用同一个命令多次,或者完全相同,或者只是参数不同。幸好bash维护着一个命令历史记录。默认这个功能是开启的。你可以使用set +o来关闭这个功能,也可使用set -o 来重新启用这个功能。bash保存的历史命令的个数取决于HISTSIZE这个环境变量。还有很多对命令历史记录进行控制的设置项,具体请参考bash的手册。
如下命令用来使用历史记录功能:
[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的插入模式。
bash中有些命令是内建的,其余的都是外部命令。现在我们看看如何运行外部命令,以及如何分辨一条命令是否是内建的。
[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' |
[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 |
[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命令相关的信息。
[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 |