スクリプト内の変数を介して find コマンドのパス パラメータに '*' ワイルドカードを渡すにはどうすればよいでしょうか?

スクリプト内の変数を介して find コマンドのパス パラメータに '*' ワイルドカードを渡すにはどうすればよいでしょうか?

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

もうひとつ例を挙げます。

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'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

これは次のものと同等です:

find te*/my?files/more -print

これにより、正しいフォルダーだけでなくterse/myxfiles/more、本来返されるべきではない も返されます。

私がやろうとしていることをどうすれば達成できるでしょうか? Google は私を助けてくれませんでした :(

答え1

まったく同じコマンドがスクリプトでも問題なく動作するはずです:

#!/usr/bin/env bash
find  te*/my\ files/ -print

もし、あんたが必要それを変数として持つと、少し複雑になります。

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

警告:

このような使用方法はeval安全ではなく、ファイル名に特定の文字が含まれている場合、任意の、場合によっては有害なコードが実行される可能性があります。バッシュ FAQ 48詳細については。

パスを引数として渡す方が良いでしょう:

#!/usr/bin/env bash
find "$@" -name "file*"

find別のアプローチとしては、これを完全に避けて、bash の拡張グロブ機能と glob を使用することです。

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

bashオプションを使用すると、再帰的に一致させるglobstarことができます。**

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.

ドットファイル(隠しファイル)を検索して含めるのとまったく同じように動作させるには、

#!/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/**

答え2

配列はどうでしょうか?

$ 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 自体にこれができるはずだと気づきました。次のようになります:

find . -path 'D*/my folder/more/'

答え3

今では少し古くなっていますが、この質問の回答に役立つのであれば、 find コマンドライン引数内で変数を[[.space.]]引用符で囲まずにRE の照合シンボルを使用する$SEARCHと、拡張パス名にアスタリスクの代わりに特殊文字が含まれていない限り機能します。

set -x
mkdir -p te{flon,nnis,rrine}/my\ files/more
SEARCH=te*/my[[.space.]]files/more
find $SEARCH

次の結果が得られます。

+ mkdir -p 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
+ SEARCH='te*/my[[.space.]]files/more'
+ find 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
teflon/my files/more
tennis/my files/more
terrine/my files/more

*不要な文字がグロブ化されるのを防ぐには、アスタリスク ( ) を任意の照合要素 (文字) に置き換えます。

set -x
mkdir -p -- te{,-,_}{flon,nnis,rrine}/my\ files/more
SEARCH=te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more
find $SEARCH

次の結果が得られます。

+ mkdir -p -- 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more' \
'te-flon/my files/more' 'te-nnis/my files/more' 'te-rrine/my files/more' \
'te_flon/my files/more' 'te_nnis/my files/more' 'te_rrine/my files/more'
+ SEARCH='te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more'
+ find 'te-flon/my files/more' 'te_flon/my files/more' 'teflon/my files/more' \
'te-nnis/my files/more' 'te_nnis/my files/more' 'tennis/my files/more' \
'te-rrine/my files/more' 'te_rrine/my files/more' 'terrine/my files/more'
te-flon/my files/more
te_flon/my files/more
teflon/my files/more
te-nnis/my files/more
te_nnis/my files/more
tennis/my files/more
te-rrine/my files/more
te_rrine/my files/more
terrine/my files/more

行を短くするには、 を または[.hyphen.]に置き換え 、 を またはに置き換えることができることに注意してください。[.-.]-[.underscore.][._.]_



\読みやすくするために、バックスラッシュ ( ) で行を切り捨てています。

答え4

ついに答えを見つけました。

すべてのスペースにバックスラッシュを追加します。

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

この時点で、SEARCHが含まれていますte*/my\ files/more

次に、 を使用しますeval

eval find ${SEARCH} -print

とても簡単です! を使用すると、変数からのeval解釈が回避されます。${SEARCH}

関連情報