![有沒有辦法在 POSIX shell 腳本中修改 $@ 數組](https://rvso.com/image/168778/%E6%9C%89%E6%B2%92%E6%9C%89%E8%BE%A6%E6%B3%95%E5%9C%A8%20POSIX%20shell%20%E8%85%B3%E6%9C%AC%E4%B8%AD%E4%BF%AE%E6%94%B9%20%24%40%20%E6%95%B8%E7%B5%84.png)
我可能知道如何使用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
請注意, 和 都不readlink
是realpath
POSIX 指令。 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:]))))' "$@")"