![POSIXシェルスクリプトで$@配列を変更する方法はありますか](https://rvso.com/image/168778/POSIX%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%A7%24%40%E9%85%8D%E5%88%97%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B.png)
SymLinks から通常のファイルを取得する方法を知っているかもしれません。関数に渡されるreadlink
SymLink を次のように置き換えるという非常に基本的なアイデアがありました。$@
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
ここで、 の先頭からエントリを"$@"
1 つずつシフトし、変更された可能性のあるエントリをリストの後ろに追加します。ループは常に静的リストを反復処理するため、ループ$@
内での変更は問題になりません。つまり、反復処理されるリストは、ループの開始時に 1 回だけ評価されます。for
$@
for
上記のループは、下記のループと同様に、各反復で位置パラメータのリスト全体をリセットします。巨大なファイル名のリストの場合、これはすぐにかなり遅くなるかもしれません。シェルに配列がある場合は、スティーブン・キットの回答スピードを上げる一つの方法を示します。
ループは、少し短い形式(readlink
各要素を呼び出す)で記述することもできます。
for dummy do
set -- "$@" "$(readlink -f -- "$1")"
shift
done
これはreadlink
POSIX ユーティリティではありませんが、質問どおりに使用されます。
出力がreadlink
1つまたは複数の改行で終わる(出力の最後の行を終了するために追加された文字以外)これらは削除されます。これはコマンド置換の結果です。これが問題になる場合は、コマンド置換で末尾の文字を人為的に追加し、次にそれを削除しますmosvy
。コメント欄:
for file do
[ -L "$file" ] && file=$(readlink -f -- "$file"; echo x)
set -- "$@" "${file%x}"
shift
done
答え2
readlink
も も POSIX コマンドではないことに注意してくださいrealpath
。POSIX コマンドライン ツールチェストには、realpath()
-like API はありません。ここでは、 (互換性のないさまざまな実装が存在します) の代わりに、 -like 機能が組み込まれており、修飾子も付いている whichreadlink
を使用できます。スクリプトでは、次のようになります。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:]))))' "$@")"