集册 Shell 十三问 大于小于号差别

大于小于号差别

—— 大于小于号差别?

黑派客     最近更新时间:2020-08-04 05:37:59

172

这次的题目,之前我在 CU 的 shell 版说明过了: (原帖的连接在论坛改版后,已经失效) 这次我就不重写了,将帖子的内容 “抄” 下来就是了...

文件描述符 (fd, File Descriptor)

谈到I/O redirection,不妨先让我们认识一下File Descriptor(fd,文件描述符)。

进程的运算,在大部分情况下,都是进行数据 (data) 的处理, 这些数据从哪里,读进来?又输出到哪里呢? 这就是 file descriptor(fd) 的功用了。

在 shell 的进程中,最常使用的fd大概有三个,分别为:

  • 0:standard Input (STDIN)
  • 1: standard output(STDOUT)
  • 2: standard Error output (STDERR

在标准情况下,这些 fd 分别跟如下设备 (device) 关联:

  • stdin(0): keyboard
  • stdout(1): monitor
  • stderr(2): monitor

Tips: linux 中的文件描述符 (fd) 用整数表示。 linux 中任何一个进程都默认打开三个文件, 这三个文件对应的文件描述符分别是:0, 1, 2; 即 stdin, stdout, stderr.

我们可以用如下命令测试一下:

$ mail -s test root
this is a test mail。
please skip.
^d (同时按下ctrl 跟d键)

很明显,mail进程所读进的数据,就是从 stdin 也就是 keyboard 读进的。 不过,不见得每个进程的stdin都跟mail一样 从keyboard读进,因为进程的作者可以从文件参数读进stdin, 如:

$ cat /etc/passwd

但,要是cat之后没有文件参数则如何呢? 哦, 请你自己玩玩看...^_^

$ cat

Tips:

请留意数据输出到哪里去了, 最后别忘了按ctrl+d(^d), 退出 stdin 输入。

至于stdoutstderr,嗯... 等我有空再续吧...^_^ 还是,有哪位前辈来玩接龙呢?

相信,经过上一个练习后, 你对stdinstdout应该不难理解了吧? 然后,让我们看看stderr好了。

事实上,stderr没什么难理解的: 说白了就是 “错误信息” 要往哪里输出而已... 比方说, 若读进的文件参数不存在的, 那我们在 monitor 上就看到了:

$ ls no.such.file
ls: no.such.file: No such file or directory

若同一个命令,同时成生stdoutstderr呢? 那还不简单,都送到 monitor 来就好了:

$ touch my.file
$ ls my.file on.such.file
ls: no.such.file: No such file or directory
my.file

okay, 至此,关于 fd 及其名称、还有相关联的设备, 相信你已经没问题了吧?

I/O 重定向 (I/O Redirection)

那好,接下来让我们看看如何改变这些 fd 的预设数据通道。

  • <来改变读进的数据通道 (stdin),使之从指定的文件读进。
  • >来改变输出的数据通道 (stdout,stderr), 使之输出到指定的文件。

输入重定向n<(input redirection)

比方说:

$ cat < my.file

就是从 my.file 读入数据

$ mail -s test root < /etc/passwd

则是从 / etc/passwd 读入...

这样一来,stdin 将不再是从 keyboard 读入, 而是从指定的文件读入了...

严格来说,<符号之前需要指定一个 fd 的 (之前不能有空白),但因为 0 是<的预设值,因此,<0<是一样的 。

okay,这样好理解了吧?

那要是用两个<,即<<又是啥呢? 这是所谓的here document,它可以让我们输入一段文本,直到读到<< 后指定的字符串。

比方说:

$ cat <<EOF
first line here
second line here
third line here
EOF

这样的话,cat会读入 3 个句子, 而无需从 keyboard 读进数据且要等到 (ctrl+d, ^d) 结束输入。

重定向输出>n(output redirection)

当你搞懂了0< 原来就是改变stdin的数据输入通道之后, 相信要理解如下两个 redirection 就不难了:

  • 1> #改变 stdout 的输出通道;
  • 2> #改变 stderr 的输出通道;

两者都是将原来输出到 monitor 的数据, 重定向输出到指定的文件了。

由于 1 是>的预设值, 因此,1>>是相同的,都是改变stdout。

用上次的 ls 的例子说明一下好了:

$ ls my.file no.such.file 1>file.out
ls: no.such.file: No such file or directory

这样 monitor 的输出就只剩下stderr的输出了, 因为stdout重定向输出到文件 file.out 去了。

$ ls my.file no.such.file 2>file.err
my.file

这样 monitor 就只剩下了stdout, 因为stderr重定向输出到文件 file.err 了。

$ ls my.file no.such.file 1>file.out 2>file.err

这样 monitor 就啥也没有了, 因为stdoutstderr都重定向输出到文件了。

呵呵,看来要理解>一点也不难啦是不? 没骗你吧? ^_^ 不过有些地方还是要注意一下的。

$ ls my.file no.such.file 1>file.both 2>file.both

假如stdout(1) 与stderr(2) 都同时在写入 file.both 的话, 则是采取 "覆盖" 的方式:后来写入覆盖前面的。

让我们假设一个stdoutstderr同时写入到 file.out 的情形好了;

  • 首先stdout写入 10 个字符
  • 然后stderr写入 6 个字符

那么,这时原本的stdout输出的 10 个字符, 将被stderr输出的 6 个字符覆盖掉了。

那如何解决呢?所谓山不转路转,路不转人转嘛, 我们可以换一个思维: 将stderr导进stdout 或者将stdout导进到stderr, 而不是大家在抢同一份文件,不就行了。 bingo 就是这样啦:

  • 2>&1 #将stderr并进stdout输出
  • 1>&2 或者 >&2 #将stdout并进stderr输出。

这样,不就皆大欢喜了吗? ~~~ ^_^

不过,光解决了同时写入的问题还不够, 我们还有其他技巧需要了解的。 故事还没有结束,别走开广告后,我们在回来....

I/O 重定向与 linux 中的/dev/null

okay,这次不讲 I/O Redirection, 请佛吧... (有没有搞错?网中人是否头壳烧坏了?...) 嘻~~~^_^

学佛的最高境界,就是 "四大皆空"。 至于是空哪四大块,我也不知,因为我还没有到那个境界.. 这个 “空” 字, 却非常值得反复把玩: --- 色即是空,空即是色 好了,施主要是能够领会 "空" 的禅意,那离修成正果不远了。

在 linux 的文件系统中,有个设备文件: /dev/null。许多人都问过我,那是什么玩意儿? 我跟你说好了,那就是 "空" 啦。

没错空空如也的空就是 null 了... 请问施主是否忽然有所顿悟了呢? 然则恭喜了。

这个 null 在 I/O Redirection 中可有用的很呢?

  • 将 fd 1跟 fd 2重定向到 / dev/null 去,就可忽略 stdout, stderr 的输出。
  • 将 fd 0重定向到 / dev/null,那就是读进空 (nothing)。

比方说,我们在执行一个进程时,会同时输出到 stdout 与 stderr, 假如你不想看到 stderr(也不想存到文件), 那就可以:

$ ls my.file no.such.file 2>/dev/null
my.file

若要相反:只想看到 stderr 呢? 还不简单将 stdout,重定向的 / dev/null 就行:

$ ls my.file no.such.file >/dev/null
ls: no.such.file: No such file or directory

那接下来,假如单纯的只跑进程,而不想看到任何输出呢? 哦,这里留了一手,上次没讲的法子, 专门赠与有缘人... ^_^ 除了用 >/dev/null 2>&1之外,你还可以如此:

$ ls my.file no.such.file &>/dev/null```

>**Tips:**
>
>将 &> 换成 >& 也行!

### 重定向输出 append (>>)

okay? 请完佛,接下来,再让我们看看如下情况:

$ echo "1" > file.out $ cat file.out 1 $ echo "2" > file.out $ cat file.out 2


看来,我们在重定向 stdout 或 stderr 进一个文件时, 似乎永远只能获得最后一次的重定向的结果. 那之前的内容呢?

呵呵,要解决这个问题,很简单啦,将`>`换成`>>` 就好了;

$ echo "3" >> file.out $ cat file.out 2 3


如此一来,被重定向的文件的之前的内容并不会丢失, 而新的内容则一直追加在最后面去。so easy?...

但是,只要你再次使用`>`来重定向输出的话, 那么,原来文件的内容被 truncated(清洗掉)。 这是,你要如何避免呢? ---- 备份, yes,我听到了,不过,还有更好的吗? 既然与施主这么有缘分,老衲就送你一个锦囊妙法吧:

$ set -o noclobber $ echo "4" > file.out -bash:file: cannot overwrite existing file.


那,要如何取消这个限制呢? 哦,将`set -o`换成 `set +o`就行了:

$ set +o noclobber $ echo "5" > file.out $ cat file.out 5


再问:那有办法不取消而又 “临时” 改写目标文件吗? 哦,佛曰:不可告也。 啊,~ 开玩笑的,开玩笑啦~^_^, 哎,早就料到人心是不足的了

$ set -o noclobber $ echo "6" >| file.out $ cat file.out 6


留意到没有: **在`>`后面加个`|`就好, 注意: `>`与`|`之间不能有空白哦**...

### I/O Redirection 的优先级

呼....(深呼吸吐纳一下吧)~~~ ^_^ 再来还有一个难题要你去参透呢:

$ echo "some text here" >file $ cat < file some text here $cat < file >file.bak $cat < file.bak some text here $cat < file >file


嗯?注意到没有? --- 怎么最后那个 cat 命令看到 file 是空的呢? why? why? why?

前面提到:`$cat < file > file`之后, 原本有内容的文件,结果却被清空了。 要理解这个现象其实不难, 这只是 priority 的问题而已: ** 在 IO Redirection 中, stdout 与 stderr 的管道先准备好, 才会从 stdin 读入数据。** 也就是说,在上例中,`>file`会将 file 清空, 然后才读入 `< file`。 但这时候文件的内容已被清空了,因此就变成了读不进任何数据。

哦,~ 原来如此~^_^ 那... 如下两例又如何呢?

$ cat <> file $ cat < file >>file

展开阅读全文