有沒有辦法在 POSIX shell 腳本中修改 $@ 數組

有沒有辦法在 POSIX shell 腳本中修改 $@ 數組

我可能知道如何使用readlink符號連結獲取普通文件。我有一個非常基本的想法來替換$@傳遞給我的函數的任何符號鏈接,如下所示:

for file in "$@"; do
    [ -L "$file" ] && file=$(readlink -f "$file")
done

範例是 SymLink /etc/os-release -> ../usr/lib/os-release

但如果我這樣做:

set -- '/etc/os-release'; for file in "$@"; do [ -L "$file" ] && file=$(readlink -f "$file"); done; echo "$@"

我仍然得到原始的 SymLink 路徑,這令人失望。我希望能夠直接修改$@,如果不可能的話,有什麼解決方法嗎?

答案1

你原來的程式碼:

for file in "$@"; do
    [ -L "$file" ] && file=$(readlink -f -- "$file")
done

問題是file循環中的設定對位置參數清單的內容沒有影響。

相反,您可能想使用

for file in "$@"; do
    [ -L "$file" ] && file=$(readlink -f -- "$file")
    set -- "$@" "$file"
    shift
done

現在,我們將從 的開頭逐一移出條目"$@",並將可能修改的條目新增至清單的後面。$@for循環中進行修改$@不是問題,因為for循環始終迭代靜態列表,即迭代的列表僅在循環開始時評估一次。

上面的循環與下面的循環一樣,在每次迭代中重置位置參數的整個列表。為一個巨大的文件名列表,這可能很快就會變得相當慢。如果你的 shell 有數組,史蒂芬·基特的回答展示了一種加快速度的方法。

循環也可以寫成稍微短一點的形式(呼叫readlink每個元素),

for dummy do
    set -- "$@" "$(readlink -f -- "$1")"
    shift
done

這按照您的問題使用readlink,即使這不是 POSIX 實用程式。

請注意,如果輸出readlink 以一個或多個換行符號結尾(除了添加的用於終止最後一行輸出的內容之外),這些將被刪除。這是命令替換的結果。如果這是一個問題,請在命令替換中人為地添加尾隨字符,然後按照mosvy建議將其刪除在評論中

for file do
    [ -L "$file" ] && file=$(readlink -f -- "$file"; echo x)
    set -- "$@" "${file%x}"
    shift
done

答案2

請注意, 和 都不readlinkrealpathPOSIX 指令。 POSIX 命令列工具箱沒有realpath()類似的 API。在這裡,readlink您可以使用它(其中存在不同的不相容實作),zsh它也具有realpath()內建的類似功能及其:P修飾符。在你的sh腳本中:

eval "set -- $(zsh -c 'print -r ${(qq)argv:P}' zsh "$@")"

zsh儘管您也可以首先編寫腳本,但它應該是:

argv=($argv:P)

或是否python3比 zsh 更可用:

eval "set -- $(
  LC_ALL=C python3 -c '
import sys, os, shlex
print(" ".join(map(shlex.quote, map(os.path.realpath, sys.argv[1:]))))' "$@")"

相關內容