对话 UNIX: !$#@*%


对话 UNIX: !$#@*%
学习更多的命令行技巧和操作符
更好地理解 UNIX® 用户输入的这些 “奇怪的” 字符。学习如何在 UNIX 中使用管道、重定向、操作符等特性。

现在,您已经在 IBM® AIX® 上工作了一段时间了。您已经学习了几个基本命令,能够在目录结构中移动、创建和修改文件、查看正在运行的进程以及管理用户和系统。这很不错,但是您希望了解 UNIX® 管理员输入的命令是什么意思。这些命令中包含许多奇怪的符号。在本文中,了解 |>>><<<[[]] 等符号在 UNIX 和 Linux® 中的意思,以及如何使用 &&||<<=!= 操作符。

管道

如果您熟悉 UNIX,那么管道(或 pipe)会是每天都要接触到的东西。管道最初是由 Malcolm McIlroy 开发的,可以使用管道把一个命令的标准输出(stdout)定向到下一个命令的标准输入(stdin),这样就形成了连续执行的命令链。可以在一个命令行上使用多个管道。在许多时候,一个命令的 stdout 用作下一个命令的 stdin,第二个命令的 stdout 又被重定向到另一个命令的 stdin,依此类推。

例如,在排除故障或执行日常检查时,大多数 UNIX 管理员首先做的事情之一是查看系统上当前正在运行的进程。清单 1 演示这样的检查。

清单 1. 日常进程检查示例
# ps –ef

     UID     PID    PPID   C    STIME    TTY  TIME CMD
    root       1       0   0   Jul 27      -  0:05 /etc/init
    root   53442  151674   0   Jul 27      -  0:00 /usr/sbin/syslogd
    root   57426       1   0   Jul 27      -  0:00 /usr/lib/errdemon
    root   61510       1   0   Jul 27      - 23:55 /usr/sbin/syncd 60
    root   65634       1   0   Jul 27      -  0:00 /usr/ccs/bin/shlap64
    root   82002  110652   0   Jul 27      -  0:24 /usr/lpp/X11/bin/X -x abx 
       -x dbe -x GLX -D /usr/lib/X11//rgb -T -force :0 -auth /var/dt/A:0-SfIdMa
    root   86102       1   0   Jul 27      -  0:00 /usr/lib/methods/ssa_daemon -l ssa0
    root  106538  151674   0   Jul 27      -  0:01 sendmail: accepting connections
    root  110652       1   0   Jul 27      -  0:00 /usr/dt/bin/dtlogin -daemon
    root  114754  118854   0   Jul 27      - 20:22 dtgreet
    root  118854  110652   0   Jul 27      -  0:00 dtlogin <:0>        -daemon
    root  131088       1   0   Jul 27      -  0:07 /usr/atria/etc/lockmgr 
       -a /var/adm/atria/almd -q 1024 -u 256 -f 256
    root  147584       1   0   Jul 27      -  0:01 /usr/sbin/cron
    root  155816  151674   0   Jul 27      -  0:04 /usr/sbin/portmap
    root  163968  151674   0   Jul 27      -  0:00 /usr/sbin/qdaemon
    root  168018  151674   0   Jul 27      -  0:00 /usr/sbin/inetd
    root  172116  151674   0   Jul 27      -  0:03 /usr/sbin/xntpd
    root  180314  151674   0   Jul 27      -  0:19 /usr/sbin/snmpmibd
    root  184414  151674   0   Jul 27      -  0:21 /usr/sbin/aixmibd
    root  188512  151674   0   Jul 27      -  0:20 /usr/sbin/hostmibd
    root  192608  151674   0   Jul 27      -  7:46 /usr/sbin/muxatmd
    root  196718  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.mountd
    root  200818  151674   0   Jul 27      -  0:00 /usr/sbin/biod 6
    root  213108  151674   0   Jul 27      -  0:00 /usr/sbin/nfsd 3891
    root  221304  245894   0   Jul 27      -  0:05 /bin/nsrexecd
  daemon  225402  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.statd
    root  229498  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.lockd
    root  241794  151674   0   Jul 27      -  0:51 /usr/lib/netsvc/yp/ypbind
    root  245894       1   0   Jul 27      -  0:00 /bin/nsrexecd
    root  253960       1   0   Jul 27      -  0:00 ./mflm_manager
    root  274568  151674   0   Jul 27      -  0:00 /usr/sbin/sshd -D
    root  282766       1   0   Jul 27   lft0  0:00 /usr/sbin/getty /dev/console
    root  290958       1   0   Jul 27      -  0:00 /usr/lpp/diagnostics/bin/diagd
    root  315646  151674   0   Jul 27      -  0:00 /usr/sbin/lpd
    root  319664       1   0   Jul 27      -  0:00 /usr/atria/etc/albd_server
    root  340144  168018   0 12:34:56      -  0:00 rpc.ttdbserver 100083 1
    root  376846  168018   0   Jul 30      -  0:00 rlogind
 cormany  409708  569522   0 19:29:27  pts/1  0:00 -ksh
    root  569522  168018   0 19:29:26      -  0:00 rlogind
 cormany  733188  409708   3 19:30:34  pts/1  0:00 ps -ef
    root  749668  168018   0   Jul 30      -  0:00 rlogind

系统上当前正在运行的进程的列表可能像 清单 1 这么简单;但是,大多数生产系统运行的进程更多,这会使 ps 的输出更长。为了把这个列表缩短到自己需要的范围,可以使用管道把 ps –ef 的标准输出重定向到 grep,从而搜索自己真正希望看到的结果。清单 2 把 清单 1 产生的进程列表重定向到 grep,搜索字符串 “rpc” 和 “ksh”。

清单 2. 把进程列表重定向到 grep
# ps –ef | grep –E "rpc|ksh"

    root  196718  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.mountd
  daemon  225402  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.statd
    root  229498  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.lockd
    root  340144  168018   0 12:34:56      -  0:00 rpc.ttdbserver 100083 1
 cormany  409708  569522   0 19:29:27  pts/1  0:00 -ksh
 cormany  733202  409708   0 19:52:20  pts/1  0:00 grep -E rpc|ksh

当多次把 stdout 重定向到 stdin 时,管道的使用方法可以很复杂。在下面的示例中,扩展了前面的 psgrep 示例,把它的 stdout 重定向到另一个 grep,其作用是排除包含 “grep” 或 “ttdbserver” 的字符串。当最后的 grep 操作完成时,再次使用管道把 stdout 重定向到一个 awk 语句,其作用是输出进程标识符(PID)大于 200,000 的所有进程:

# ps –ef | grep –E "rpc|ksh" | grep -vE "grep|rpc.ttdbserver" | 
   awk -v _MAX_PID=200000 '{if ($2 > _MAX_PID) {print "PID for 
   process",$8,"is greater than", _MAX_PID}}'

PID for process /usr/sbin/rpc.statd is greater than 200000
PID for process /usr/sbin/rpc.lockd is greater than 200000
PID for process -ksh is greater than 200000

图 1 通过图形说明命令的 stdout 重定向到后续命令的 stdin 的次序。

图 1. 管道示例

管道示例


用 >、>>、< 和 << 执行数据重定向

通过命令行界面(CLI)执行命令的另一个重要方面是,能够把各种输出写到一个设备,或者把来自另一个设备的输入读取到命令中。要想写一个命令的输出,需要在执行的命令后面加上大于号(> 或 >>)和所需的目标文件名或设备。如果目标文件不存在,而且您对目标目录有写权限,那么 > 和 >> 会创建这个文件并根据您的 umask 设置权限,然后把命令的输出写到刚创建的文件中。但是,如果这个文件存在,> 会尝试打开文件并覆盖整个内容。如果希望在这个文件中追加内容,那么只需使用 >>。可以认为它的作用是把左边命令的输出数据流移动到右边的目标文件中(即 <cmd> -> <output> -> <file>)。

下面的示例执行 “管道” 一节中的 ps –ef 示例,并把输出重定向到文件 ps_out

# ps –ef | grep –E "rpc|ksh" > ps_out

下面的代码执行前面扩展的管道示例并把输出重定向到同一个文件(ps_out),但是追加到当前数据后面:

# ps –ef | grep –E "rpc|ksh" | grep -vE "grep|rpc.ttdbserver" | 
   awk -v _MAX_PID=200000 '{if ($2 > _MAX_PID) {print "PID for 
   process",$8,"is greater than", _MAX_PID}}' >> ps_out

清单 3 给出前两个重定向的输出。

清单 3. 重定向的输出
# cat ps_out

    root  196718  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.mountd
  daemon  225402  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.statd
    root  229498  151674   0 11:00:27      -  0:00 /usr/sbin/rpc.lockd
    root  340144  168018   0 12:34:56      -  0:00 rpc.ttdbserver 100083 1
 cormany  409708  569522   0 19:29:27  pts/1  0:00 -ksh
 cormany  733202  409708   0 19:52:20  pts/1  0:00 grep -E rpc|ksh
PID for process /usr/sbin/rpc.statd is greater than 200000
PID for process /usr/sbin/rpc.lockd is greater than 200000
PID for process -ksh is greater than 200000

当只使用 > 重定向输出时,只重定向命令的 stdout。但是,除了 stdout,还有 stderr 输出:前者表示为 1,后者表示为 2。在 UNIX 中输出重定向没有区别。只需在 > 前面加上所需的输出类型(例如,1>2>),告诉 shell 要把输出路由到哪里。

清单 4 尝试列出 fileA.tar.bz2 和 fileC.tar.bz2。但是,如第一个命令(ls)所示,fileC.tar.bz2 不存在。好在可以把 stdout 和 stderr 分别重定向到 ls.out 和 ls.err,这样就能够看到错误消息。

清单 4. 列出文件 fileA.tar.bz2 和 fileC.tar.bz2
# ls
fileA.tar.bz2   fileAA.tar.bz2  fileB.tar.bz2   fileBB.tar.bz2

# ls fileA.tar.bz2 fileC.tar.bz2 1> ls.out 2> ls.err

# cat ls.out
fileA.tar.bz2

# cat ls.err
ls: 0653-341 The file fileC.tar.bz2 does not exist.

在 AIX 中,对 stdout 和 stderr 使用 >>> 时应用相同的规则。例如,以后的测试可以使用相同的输出文件,见 清单 5。

清单 5. 使用输出文件进行以后的测试
# ls fileB.tar.bz2 fileD.tar.bz2 1>> ls.out 2>> ls.err

# cat ls.out
fileA.tar.bz2
fileB.tar.bz2

# cat ls.err
ls: 0653-341 The file fileC.tar.bz2 does not exist.
ls: 0653-341 The file fileD.tar.bz2 does not exist.

有时候,可能需要把 stdout 和 stderr 写到同一个文件或设备。这有两种方法。第一种方法是把 1>2> 重定向到同一个文件:

# ls fileA.tar.bz2 fileC.tar.bz2 1> ls.out 2> ls.out

# cat ls.out
fileA.tar.bz2
ls: 0653-341 The file fileC.tar.bz2 does not exist.

第二个方法更简单更快速,有经验的 UNIX 用户更喜欢采用这种方法:

# ls fileA.tar.bz2 fileC.tar.bz2 > ls.out 2>&1

# cat ls.out
fileA.tar.bz2
ls: 0653-341 The file fileC.tar.bz2 does not exist.

我们分解这个语句。首先,执行 ls fileA.tar.bz2 fileC.tar.bz2。然后使用 > ls.out 把 stdout 重定向到 ls.out,使用 2>&1 把 stderr 重定向到前面重定向的 stdout(ls.out)。

请记住,可以把输出重定向到文件和其他设备。可以把数据重定向到打印机、软盘、终端类型(TTY)以及各种其他设备。例如,如果希望把一个消息发送给所有会话(或 TTY)上的某个用户,那么只需循环处理 who 并把一个消息重定向到 TTY(如果您有足够的权限的话),见 清单 6。

清单 6. 把消息重定向到一个 TTY
# for _TTY in 'who | grep "cormany" | awk '{print $2}''
> do
>   _TTY="/dev/${_TTY}"
>   echo "Sending message to cormany on ${_TTY}"
>   echo "Test Message to cormany@${_TTY}" > ${_TTY}
> done

Sending message to cormany on /dev/pts/13
Test Message to cormany@/dev/pts/13
Sending message to cormany on /dev/pts/14

stdin 而不是 stdout

尽管使用 > 和 >> 对于大多数人是一个相当容易掌握的概念,但是有的人在使用小于号(< 和 <<)时常常有困难。在考虑 > 和 >> 时,认为它们把左边命令的输出数据流移动到右边的目标文件中,这样最容易理解。同样的方法也适用于 < 和 <<。在使用 < 时,本质上是用一个已经提供的 stdin 执行一个命令。也就是说,把已经提供的数据提供给左边的命令作为 stdin(即 <cmd> <- <data>)。

例如,假设希望把一个包含 ASCII 文本文件的电子邮件发送给另一个用户。可以使用管道把 cat 的 stdout 重定向到 mail 的 stdin(即 cat mail_file.out | mail –s "Here's your E-mail!" acormany@yahoo.com),也可以把文件的内容重定向到 mail 命令的 stdin:

# mail –s "Here's your E-mail!" acormany@yahoo.com < mail_file.out

使用 <<(也称为 here-document)可以节省格式化时间,并且使命令执行的处理更容易。通过使用 <<,文本字符串被重定向到执行的命令作为 stdin,但是可以继续输入信息,直到到达终止标识符。只需输入命令,输入 << 和终止标识符,然后输入需要的任何内容,最后在一个新行上输入终止标识符。通过使用 here-document,可以保留空格、换行等。

例如,UNIX 必须单独处理下面五个 echo 语句:

# echo "Line 1"
Line 1

# echo "Line 2"
Line 2

# echo "Line 3"
Line 3

# echo "Line 4"
Line 4

# echo "Line 5"
Line 5

可以用以下代码替换多个 echo 语句,UNIX 只需处理一次执行:

# cat << EOF
> Line 1
> Line 2
> Line 3
> Line 4
> Line 5
> EOF

Line 1
Line 2
Line 3
Line 4
Line 5

还可以使用制表符让 shell 脚本中的内容更整洁一点,这只需要在 << 和终止标识符之间放上一个连字符(-):

# cat <<- ATC
>	Line 1
>	Line 2
>	Line 3
>	Line 4
>	Line 5
> ATC

Line 1
Line 2
Line 3
Line 4
Line 5

清单 7 给出的示例演示如何结合使用本文到目前为止讨论的东西。

清单 7. 组合 CLI
# cat redirect_example

#!/usr/bin/ksh

cat <<- ATC | sed "s/^/Redirect Example => /g" >> atc.out
This is an example of how to redirect
stdout to a file as well as pipe stdout into stdin
of another command (i.e. sed), all done inside
a here-document.

Cool eh?
ATC

现在,看看关于重定向和管道的脚本。

# ./redirect_example

# cat atc.out
Redirect Example => This is an example of how to redirect
Redirect Example => stdout to a file as well as pipe stdout into stdin
Redirect Example => of another command (i.e. sed), all done inside
Redirect Example => a here-document.
Redirect Example =>
Redirect Example => Cool eh?

子 shell

有时候,需要一起执行几个命令。例如,如果希望在另一个目录中执行某一操作,可以使用 清单 8 中的代码。

清单 8. 同时执行几个命令
# pwd
/home/cormany

# cd testdir

# tar –cf ls_output.tar ls.out?

# pwd
/home/cormany/testdir

这是有效的,但是要注意,在执行这些步骤之后,您就不再位于原来的目录中了。通过把这些命令放在它们自己的子 shell 中,它们会作为子 shell 的实例执行。清单 9 演示如何使用子 shell 执行相同的代码。

清单 9. 使用子 shell 同时执行几个命令
# pwd
/home/cormany

# (cd testdir ; tar -cf ls_output.tar ls.out?)

# pwd
/home/cormany 

更多详情见请继续阅读下一页的精彩内容:

  • 1
  • 2
  • 下一页

相关内容