../smth*/*
プログラムでは、グローバルパターン(たとえば、または/etc/cron*/
)をファイルリストに展開する必要があります。最良のアプローチは何ですか?
答え1
配列宣言の右側に展開するようにしてください。
list=(../smth*/) # grab the list
echo "${#list[@]}" # print array length
echo "${list[@]}" # print array elements
for file in "${list[@]}"; do echo "$file"; done # loop over the array
nullglob
シェルオプションを設定する必要があります。
デフォルトでは設定されません。zsh
エラーが発生したり(またはからbash -O failglob
)文字通りに渡されるのではなく(他のすべてのBourne様シェル)、一致しないglobはnullに拡張されます。
bash
に設定する
shopt -s nullglob
または一緒zsh
にyash
set -o nullglob
zsh
(nullglob
元のソース)グローバル設定を変更したくない場合は、glob修飾子を使用することをお勧めします(N)
。
list( ../smth*/(N) )
ksh93に対応:
list=( ~(N)../smth*/ )
答え2
compgen
エスケープ (!) パターンを渡すことができる Bash 組み込み機能で、一致が存在するかどうかに応じて true または false を返して出力します。これは、変数/スクリプト引数でグローバルパターンを渡す必要がある場合に特に便利です。
glob_pattern='../smth*/*'
while read -r file; do
# your thing
echo "read $file"
done < <(compgen -G "$glob_pattern" || true)
|| true
エラーの返却によるcompgen
問題の発生を防ぐために追加されました。この方法は、一致しない問題を防ぎ、nullglobオプションを変更する必要はありません。
配列内の項目が必要な場合は、files=()
ループの前とfiles+=("$file")
ループ内で項目を初期化できます。その後、単にを使用して配列の長さを確認して、一致するものがあるかどうかを確認できますif [[ ${#files[@]} -gt 0 ]]; then
。
答え3
生成されたコマンドがコマンドラインの長さ制限を超える場合は、標準入力(パイプライン)を使用したいと思います。次のコマンドが私にとって効果的でした。
echo "../smth*/*" "/etc/cron*/" | xargs -n1 -I{} bash -O nullglob -c "echo {}" | xargs -n1
またはグローバルリストの場合:
cat huge_glob_list.txt | xargs -n1 -I{} bash -O nullglob -c "echo {}" | xargs -n1
答え4
私は最近同じ問題を経験しました。私は解決策がとても簡単であることがわかりました。 (そしてPOSIXと互換性があります。)
$IFS
空白文字で単語分割を無効にするには、空の文字列に設定します。- その後、変数を逆参照してグローバルに拡張できます。
サンプルコードを図に示します。forループ:
pattern='some * dir/my file *'
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
IFS=''
for f in ${pattern} ; do
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
printf 'Filenames: %s \n' "${f}"
done
shopt -s nullglob
POSIXで定義されていない方法でnullglobを設定しないことに注意してください。shopt
グローバルパターンが見つからない場合、パターンはそれ自体に拡張されます。Filenames: some dir/my file *
上記のコードで印刷されました。if [ -e "${f}" ]; then ...
必要に応じて小切手を追加するのは簡単です。
同じ方法で設定できます。位置パラメータ返品。
pattern='some * dir/my file *'
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
set -- ${pattern}
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
unset old_IFS
printf '[%s]\n' "$@"
これを1行に置き換えることはできませんIFS='' command set -- ${pattern}
。この行は単語分割を無効にしません。
それは使用することができます機能パラメータしかし、これはお勧めできません。回復は$IFS
関数の最初のステートメントで行わなければなりません。これは非対称的で簡単に忘れられます。
func() {
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
unset old_IFS
printf '[%s]\n' "$@"
}
pattern='some * dir/my file *'
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
func $pattern
個人的には、私は$pattern
関数に渡してからset -- $pattern
関数の中に渡すことを好みます。ただし、関数が他の位置パラメータも使用している場合、これは必ずしも可能ではありません。
func() {
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
set -- ${pattern}
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
unset old_IFS
printf '[%s]\n' "$@"
}
pattern='some * dir/my file *'
func $pattern
この方法はパターンとファイルパスに使用されます。
- 空白文字、実際にはすべての文字が含まれている場合
\*
グローバル文字が含まれている場合は、リテラルを一致させるためにエスケープを使用します*
。- glob文字がディレクトリパスコンポーネントおよび/またはファイル名コンポーネントにある場合。 (ディレクトリパスにglobを使用する必要がある場合は簡単にはできません
find
。)