有一个文件夹,里面有一堆文件和一个 perl 脚本,使用 ls 和通配符列出它们,例如:
#!/usr/bin/perl
system("ls -U -1 dir/*");
我注意到,如果我从 bash 终端运行完全相同的命令,我会得到相同的结果,但它们的顺序不同。
为什么会这样?通配符在两种情况下的处理方式是否不同?
例子:
mkdir dir
touch dir/a_0 dir/a1_0
终端输出:
dir/a_0
dir/a1_0
珀尔输出:
dir/a1_0
dir/a_0
答案1
Perl 的system("cmd")
函数通常会分叉一个进程,并在子进程中运行系统的 shell(通常为/bin/sh
),并以 为参数,["sh", "-c", "cmd"]
以sh
解析并执行该 shell 命令行。
作为一种优化,如果除了空格和制表符之外cmd
不包含任何 shell 元字符(例如引用字符或通配字符或诸如;
, ... 之类的东西),有时可能不需要 shell 调用,但这里我们有 shell 元字符&&
- 字符,因为我们有一个*
.
因此,这ls -U -1 dir/*
将由系统的 shell 解释。
shell 会扩展dir/*
为传递给 的匹配文件列表ls
,因此其完成方式取决于 shell。
在终端中,您通常会运行登录 shell,这通常不是/bin/sh
.另外(如彼得夫指出),该 shell 因为以交互方式运行,通常会读取配置文件,例如~/.zshrc
(如果 shell 是zsh
),其中某些设置可能会影响通配符的完成方式。
例如:
我的外壳是,并且我的 中zsh
有一个,所以:setopt dotglob
~/.zshrc
$ echo *
.a d é f
无需阅读~/.zshrc
:
$ zsh -c 'echo *'
d é f
$ LC_ALL=C zsh -c 'echo *'
d f é
您会注意到zsh
在对列表进行排序时会遵循区域设置。
$ sh -c 'echo *'
d f é
sh
(在我的例子中是 Debian ash
)不尊重语言环境,并且像在 C 语言环境中一样排序。
如果你想perl
用system()
特定的 shell 解释命令行,你可以这样写:
system("zsh", "-c", "cmd");
当传递多个参数时,perl
ssystem()
永远不会隐式调用 shell,因此上面它会分叉一个进程,并在该进程中将其/bin/zsh
作为["zsh", "-c", "cmd"]
参数运行。