我正在find . -name *.bak
运行命令,并收到错误"Paths must precede expression"
错误。这个问题已经回答了,答案也很好,但我不明白的是,为什么 bash 会在运行 find 命令之前扩展通配符?原始示例。
它说find . -name *.bak
扩展到find . -name tim.bak example.bak
。有人可以解释为什么它被扩展,我不明白为什么为 globs 提供此功能是个好主意。
这是在 unix 历史早期做出的设计决策。文件名通配符在某些时候需要扩展,但可以选择是否应该由命令解释器(又名 shell)完成,并将结果(匹配文件列表)传递给可执行文件,或者命令解释器是否应该只传递它提供给可执行文件的内容,并让它进行扩展。不同的操作系统以不同的方式执行此操作;Unix执行第一种方式,但VMS(我在迁移到Unix之前使用过)以第二种方式执行。两者都有优点和缺点。
-
Unix 方式的主要优点是通配符扩展代码只需要在一个地方编写和使用:在 shell 中。命令很简单,不必担心。其次,您可以通过在一个位置进行更改来改进/扩展匹配语法(例如 bash 的 extglob 语法)。第三,您可以在所有不同的命令之间获得一致的扩展语法(而不是必须为不同的命令学习不同的规则,例如基本与扩展与Perl兼容的正则表达式混乱)。
-
VMS方式的主要优点是可执行程序知道参数的含义,并且可以适当地更改/抑制扩展。例如,
find
知道不要扩展当前目录中的通配符,grep
知道不要尝试扩展正则表达式模式,就好像它是文件通配符一样,scp
可以扩展远程计算机上的通配符,等等。第二个优点是通配符可以以 unix 系统根本不允许的方式使用,因为程序可以更深入地了解参数的指定方式;例如,rename *.jpeg *.jpg
(如果我没记错的话)是一个非常好的VMS命令,它完全可以执行它看起来应该做的事情。[编辑] 另一个优点是它避免了文件名被误认为命令选项的风险。对于Unix方法来说,这可能是一个严重的安全问题,因为任何可以控制文件名的人也可以控制处理这些文件的命令和脚本。例如,创建名为"-e somecommand"的文件将导致
rsync -t * foo:src/
在远程计算机上执行somecommand
。本文提供了更多示例。每个程序都需要做自己的通配符扩展,这并不像你想象的那么糟糕。有处理扩展的标准库函数,所以程序需要做的就是调用它,然后处理生成的文件列表,没什么大不了的。这些库函数可以像 shell 语法一样扩展,并且对它们进行标准化提供了跨程序的一致性等。
正如您可能从上面可以看出的那样,我总体上认为VMS方式更好。对于程序员来说,这需要多做一些工作,但在可用性和功能方面具有显着的好处。但我敢肯定,这是Unix人中的少数人的观点,无论如何,要改变Unix的工作方式需要付出巨大的努力,所以只要Unix是Unix,它就不会改变。
一个非常简短的答案:
这是Bash
的工作方式,它总是尽可能地扩展,用变量的值替换变量,使用环境变量(例如$PATH
),解释别名并将结果(相应的参数)传递给您正在调用的">正确"命令(从$PATH
中提取)。论点扩展可以被视为众多责任之一。
想象一下,如果你没有这个功能,你需要为你开发的每个程序实现这个功能,以正确扩展参数(在工作目录或你指向的目录中使用相应的文件)。简而言之,这将是一场噩梦!!这就是为什么它集中在 bash 中。
最后但并非最不重要的一点是,对于您的find
命令,请以这种方式更改它以避免来自 bash 的任何干扰。通过这样做,find
命令将接收文件名的正则表达式,以正确搜索和管理它
find . -name '*.bak'
^ ^
壳牌职责列表:
http://www.informit.com/articles/article.aspx?p=31480&seqNum=4 http://ptgmedia.pearsoncmg.com/images/chap3_0672324903/elementLinks/03fig08.jpg http://tldp.org/LDP/abs/html/x9644.html
UNIX的创始人之一Dennis Ritchie给出了这样的解释:
"将这种膨胀机制放入外壳中有几个优点: 代码只出现一次,因此不会浪费空间和命令 无需采取特殊行动;该算法肯定会被应用 统一。
D.M.Ritchie,在Unix分时系统中:回顾展 BSTJ 1978年7月至8月