web-dev-qa-db-ja.com

スクリプトの変数を介して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

もう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は私を助けていない:(

8
Paddy Landau

まったく同じコマンドがスクリプトで正常に機能するはずです。

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

配列はどうですか?

$ 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/'
2
muru

私は最終的に答えを見つけました。

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

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

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

次に、evalを使用します。

eval find ${SEARCH} -print

とても簡単です! evalを使用すると、${SEARCH}が変数からのものであるという解釈がバイパスされます。

0
Paddy Landau