為什麼從終端或 Perl 使用通配符結果的順序可能不同?

為什麼從終端或 Perl 使用通配符結果的順序可能不同?

有一個資料夾,裡面有一堆檔案和一個 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 語言環境中一樣排序。

如果你想perlsystem()特定的 shell 解釋命令列,你可以這樣寫:

system("zsh", "-c", "cmd");

當傳遞多個參數時,perlssystem()永遠不會隱式地呼叫 shell,因此上面它會分叉一個進程,並在該進程中將其/bin/zsh作為["zsh", "-c", "cmd"]參數運行。

相關內容