グローバルスキーマを拡張する最良の方法は何ですか?

グローバルスキーマを拡張する最良の方法は何ですか?

../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

または一緒zshyash

set -o nullglob

zshnullglob元のソース)グローバル設定を変更したくない場合は、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 nullglobPOSIXで定義されていない方法で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。)

関連情報