POSIXシェルスクリプトで$ @配列を変更する方法はありますか?

POSIXシェルスクリプトで$ @配列を変更する方法はありますか?

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

それでは、アイテムを最初から1つずつ削除"$@"し、潜在的に変更されたアイテムをリストの後ろに追加します。ループ$@内のfor修正は、ループが常に静的リストを繰り返すので$@問題ではありませんfor。つまり、反復リストはループの開始時に一度だけ評価されます。

下のループと同様に、上のループは各反復ごとに位置パラメータの完全なリストをリセットします。一つのために巨大なファイル名のリストが遅すぎる可能性があります。シェルに配列がある場合、Steven Kitの答え作業速度を上げる方法を示します。

ループは少し短い形式(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

POSIXコマンドは両方ともではreadlinkありません。 POSIXコマンドラインツールボックスには同様のAPIはrealpathありません。ここでは、同様の機能とその修飾子が組み込まれているものをrealpath()代わりに使用できますreadlink(互換性のない他の実装があります)。スクリプトから:zshrealpath():Psh

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

zshスクリプトを最初に作成することもできますが、次のようにする必要があります。

argv=($argv:P)

それともpython3zshよりも便利ですか?

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

関連情報