web-dev-qa-db-ja.com

パラメータ展開内でのグロブ

次の引数として渡された一連のディレクトリ内のファイルを選択しようとしています。

${@/%/*}

ただし、これは理想的ではありません。スペースのあるパスが壊れ、パラメータ展開を引用するとファイル名の展開が停止するためです。満足のいく解決策は可能ですか?

追伸:これを実行するにはfind -maxdepthを使用する必要があることは承知していますが、パラメーター展開とグロブを組み合わせるのが非常に難しいように見える理由について知りたいです。

3
Xerz

あなたはいつでもすることができます:

_IFS=              # don't split
files=(${@/%/*})  # use split+glob upon expansion with split disabled
_

ただし、_"$@"_にワイルドカード文字が含まれている場合は、そのように解釈されます。また、一致しない各グロブのパターンが展開されない状態になります。

ループとnullglobオプションを使用して、これらに対処できます。

_shopt -s nullglob
files=()
for arg do files+=("$arg"*); done
_

または、zshの代わりにbashを使用して、次を使用することもできます。

_files=($^argv*(N))
_

_$^array_は、_$array_の1つの拡張に対してrcexpandparamオプションをオンにします。 rcシェル(およびfishシェルの場合も同様)では、_$array*_ここで、_$array_には2つの要素abは_a*_、_b*_として展開されるため、ここではfiles=($argv[1]*(N) $argv[2]*(N)...)を実行するようなものです(_$argv[1]_は_$1_と同じですが、より冗長です)。 _(N)_自体は、その1つのグロブのnullglob動作をオンにするグロブ修飾子です。

fishシェルでは、次のようになります。

_set files $argv*
_

as fishは、nullglobへの引数でsetのような動作を有効にし、rcと同様に配列を展開します。ただし、fishの構文はbashの構文とは根本的に異なります。zshよりもはるかに異なります。

これらは、_"$1"*_のように_"$2"*_、_${@/%/*}_...グロブを拡張することを目的としていることに注意してください。実際に_"$1"/*_、_"%2"/*_...グロブを意味する場合は、引数として渡される一連のディレクトリ内のファイルが示唆するように、_${@/%/\/*}_が必要です。代わりに、上記のコード部分の_"$arg"/*_、$^argv/*(N)、_$argv*_。

1

他のグロブ文字をバックスラッシュ+文字に置き換えます。空白文字をバックスラッシュ+文字に置き換えるか、(より堅牢に)IFSを空の文字列に設定します。前のエスケープステップの結果としてバックスラッシュが再エスケープされないように、最初にバックスラッシュをエスケープすることを忘れないでください。ただし、バックスラッシュはワイルドカード文字がある場合にのみ特別であるため、エスケープするものがない場合は何もエスケープしないでください。

一致するものがない場合は、nullglobオプションを設定して、展開されていないパターンではなく、空のリストを取得します。関数でこれを行い、シェルオプションとIFSローカルへの変更を保持するように調整します。

最小限のテスト:

function set_patterns {
  typeset restore_shopt=$(shopt -p nullglob)
  shopt -s nullglob
  typeset IFS=
  typeset x
  patterns=()
  for x; do
    if [[ "$x" == *[*?[]* ]]; then
      x="${x//\\/\\\\}"
      x="${x//\*/\\*}"
      x="${x//\?/\\?}"
      x="${x//\[/\\[}"
    fi
    x="${x//%/*}"
    patterns+=($x)
  done
  eval "$restore_shopt"
}