find
を使用して、ワイルドカードで制限された一連のフォルダー内のファイルを検索しますが、パス名にスペースが含まれています。
コマンドラインから、これは簡単です。次の例はすべて機能します。
find te*/my\ files/more -print
find te*/'my files'/more -print
find te*/my' 'files/more -print
これらは、たとえばterminal/my files/more
およびtepid/my files/more
内のファイルを検索します。
ただし、これをスクリプトの一部にする必要があります。私が必要なのは次のようなものです:
SEARCH='te*/my\ files/more'
find ${SEARCH} -print
残念ながら、私が何をしても、スクリプト内でfind
コマンドにワイルドカードとスペースを混在させることはできないようです。上記の例は、次のエラーを返します(バックスラッシュの予期しない二重化に注意してください):
find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory
引用符を使用しようとしても失敗します。
SEARCH="te*/'my files'/more"
find ${SEARCH} -print
これは、引用符の意味を無視して、次のエラーを返します。
find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory
もう1つ例を示します。
SEARCH='te*/my files/more'
find ${SEARCH} -print
予想通り:
find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory
私が試したすべてのバリエーションはエラーを返します。
回避策がありますが、返されるフォルダーが多すぎるため潜在的に危険です。次のように、すべてのスペースを疑問符(単一文字のワイルドカード)に変換します。
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?} # Convert every space to a question mark.
find ${SEARCH} -print
これは次と同等です:
find te*/my?files/more -print
これにより、正しいフォルダーだけでなく、terse/myxfiles/more
も返されますが、これは想定されていません。
私がやろうとしていることをどのように達成できますか? Googleは私を助けていない:(
まったく同じコマンドがスクリプトで正常に機能するはずです。
#!/usr/bin/env bash
find te*/my\ files/ -print
変数として必要な場合、それはもう少し複雑になります:
#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print
そのようなeval
を使用するのは安全ではなく、ファイル名に特定の文字が含まれている場合は、任意の有害なコードを実行する可能性があります。詳細については、 bash FAQ 48 を参照してください。
引数としてパスを渡すことをお勧めします:
#!/usr/bin/env bash
find "$@" -name "file*"
別のアプローチは、find
を完全に回避し、bashの拡張グロブ機能とグロブを使用することです。
#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
globstar
bashオプションを使用すると、**
を使用して再帰的に一致させることができます。
globstar
If set, the pattern ** used in a pathname expansion con‐
text will match all files and zero or more directories
and subdirectories. If the pattern is followed by a /,
only directories and subdirectories match.
ドットファイル(隠しファイル)を検索して含めるように100%動作させるには、次を使用します。
#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done
ループなしで直接echo
することもできます。
echo te*/my\ files/**
配列はどうですか?
$ tree Desktop/ Documents/
Desktop/
└── my folder
└── more
└── file
Documents/
└── my folder
├── folder
└── more
5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}"
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder
(*)
は、ワイルドカードに一致するものの配列に展開されます。 "${SEARCH[@]}"
は、配列内のすべての要素([@]
)に展開され、それぞれ個別に引用されます。
遅かれ早かれ、私は自分自身がこれに対応できるはずだと気づきました。何かのようなもの:
find . -path 'D*/my folder/more/'
私は最終的に答えを見つけました。
すべてのスペースにバックスラッシュを追加します。
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }
この時点で、SEARCH
にはte*/my\ files/more
が含まれています。
次に、eval
を使用します。
eval find ${SEARCH} -print
とても簡単です! eval
を使用すると、${SEARCH}
が変数からのものであるという解釈がバイパスされます。