像这样的命令cd
不能通过管道将输出传递给它们以更改目录——它们需要命令行参数。
为什么该cd
命令(以及类似的命令,例如mv
, cp
, & rm
)在读入 时不像大多数其他命令那样起作用STDIN
?阻止它读取标准输入来更改目录背后的逻辑是什么?
最好的我能找到的答案指出:
cd 不是外部命令 - 它是 shell 内置函数。它在当前 shell 的上下文中运行,而不是像外部命令那样在 fork/exec'd 上下文中作为单独的进程运行。
然而,对我来说,上面的答案根本没有真正解释它:为什么cd
处理STDIN
与读入的许多其他命令不同STDIN
?
答案1
读取的命令stdin
几乎都是过滤器系列的,即将文本数据流转换为转换后的数据流的程序。
cat
、sed
、awk
、gzip
甚至sh
是此类“过滤器”的好例子。
引用的命令 、cp
和mv
绝对rm
不是过滤器,而是使用传递的参数(这里是文件或目录)执行操作的命令。
该cd
命令与它们类似,它需要一个参数(如果未提供,则模拟默认参数),并且通常不会在 上输出任何内容stdout
,尽管在某些情况下(例如使用 时)它可能会在其上输出一些内容CDPATH
。
即使想要创建一个cd
从 stdin 获取目标目录的变体,在 Bourne shell 的管道中使用时也不会产生任何效果,dash
仅举bash
几例。命令的最后一个组成部分在子 shell 中运行,对新目录的更改不会影响当前 shell。例如:echo /tmp | cd
可以使用,ksh93
但不能使用bash
, dash
, zsh
, sh
, ...
cd <(echo /tmp)
可以与支持进程替换的 shell 一起使用(至少ksh
,,bash
)zsh
,但与cd $(echo tmp)
唯一可能感兴趣的用例是:
echo tmp | (cd ; pwd)
最后,这样的变体需要解决没有给出参数但预期行为是将目录更改为用户主目录的情况,或者没有给出参数但预期行为是从中读取目标目录的名称的情况标准输入。由于没有可靠的方法来决定,这是注定的。
答案2
命令行参数与 stdin 完全不同;即使同时使用两者的命令通常也将它们用于不同的用途。举cat
个例子:
echo foo bar | cat # outputs "foo bar"
cat foo bar # looks for files named "foo" and "bar", and concatenates them (if found)
如果您查看从 stdin 读取的大多数其他命令,您会发现类似的事情:它们不会互换地对待参数和 stdin,而是将其视为向命令提供不同类型信息的方式。
有一些命令可以以任何一种方式获取相同类型的信息,但即使在这些情况下,通常也必须指定一个选项来告诉它使用哪个。例如,perl
从 stdin 读取其程序,除非另有指示:
echo 'print 1' | perl # runs "print 1" as a perl program, which prints "1"
perl -e 'print 1' # runs "print 1" as a perl program, which prints "1"
perl print 1 # tries to run a file named "print" as a script, giving it the argument "1"
...所以,我认为问题的前提是错误的:大多数命令不会从标准输入中获取与从参数中获取的相同类型的信息,因此cd
在这方面并不罕见。