引数で find コマンドを書く

引数で find コマンドを書く

多くの場合、ファイルを見つける必要がありますが、次のようにファイル名が何であるかわかりません。

$ find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'

ちょっと退屈です。次のように使いやすいエイリアスを作成したいと思います。

$ findany foo bar blah

私の試みは次のとおりです。

findany() {
    args=("-iname" "'*$1*'")
    shift

    while [ $# -gt 0 ]; do
        args+=("-o" "-iname" "'*$1*'")
        shift
    done

    find ${args[@]}
}

問題は、ファイルがすぐそこにあっても結果がまったく生成されないことです。

$ ls
bar.txt  blah.txt  foo.txt

$ findany foo bar blah
# nothing

echoコマンドの前に追加すると正しく表示されます。

$ findany foo bar blah                     
find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'

上記の出力をコピーして実行すると正常に動作します。

$ find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
./bar.txt
./foo.txt
./blah.txt

パラメータの分割や引用に関連しているようですが、どのようにデバッグするのかわかりません。

私は主にzshを使用していますが、bashでも同じ動作を確認しました。

答え1

あなたは非常に近いです!ただし、何らかの理由でファイル名自体に一重引用符を含めると、一致しません。

また、"${args[@]}"これが正しく機能するには、見積もりを作成する必要があります。それ以外の場合はトークン化の影響を受け、すべてのglobはfind表示される前にシェルから拡張されます。

以下を試してみてください(特に場合bash)。

findany() {
    local args=('-iname' "*$1*")
    shift

    while [ "$#" -gt 0 ]; do
        args+=('-o' '-iname' "*$1*")
        shift
    done

    find . "${args[@]}"
}

答え2

zshを使用すると、次のことができます。

set -o extendedglob # usually in your ~/.zshrc, here used for (#i) for
                    # case insensitive matching
print -rC1 -- **/*(#i)(foo|bar|blah)*(ND)

配列からパターンを構築するには、j[|]パラメータ拡張フラグを使用して配列の要素を連結し、パラメータ拡張|フラグと組み合わせてグローバルパターン演算子として処理する~か、パラメータ拡張演算子を使用して接続を実行できます。文字列内のワイルドカード文字(配列要素内にある場合でも)はパターンと見なされます。|~

findany1() print -rC1 -- **/*(#i)(${(~j[|])argv})*(ND)

または:

findany2() print -rC1 -- **/*(#i)(${(j[|])~argv})*(ND)

たとえば、名前findany1 '??'に2文字以上の文字を含むファイルが検索されます。を含む名前が必要または検索されます。??findany2findany2 '\?\?'findany2 '[?][?]'??findany2

と同じ区別をするにはfind

findany2() (
  for arg do
    argv+=( -o -iname "$arg" )
    shift
  done
  shift
  find . "$@"
)
findany1() (
  set -o extendedglob
  for arg do
    argv+=( -o -iname "${arg//(#m)[][\\?*]/\\$MATCH}" )
    shift
  done
  shift
  find . "$@"
)

find?、および)として*認識されるワイルドカード文字を(/はサポートされている唯一の参照演算子です)にエスケープします。[...]\\findfnmatch()

答え3

$ find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'

これは問題を解決する1つの方法ですが、正直なところ、次のような結果が得られます。

shopt -s globstar  ## enable recursive globbing operator **
shopt -s extglob   ## enable (|) pattern lists
shopt -s nocasematch  ## take a guess!

echo **/*@(foo|bar|blah)*

(しかし、そうするのに助けは必要ありませんfind)。

シェルスクリプトをすばやく作成できます。

#! /bin/bash -
shopt -s globstar  ## enable recursive globbing operator **
shopt -s extglob   ## enable (|) pattern lists
shopt -s nullglob  ## don't error if nothing matches
shopt -s nocasematch  ## take a guess!

IFS='|' # "$*" joins with the first character of IFS
pattern="**/*@(${*})*"

IFS= # do globbing but not splitting upon unquoted expansion:
matches=( $pattern )

for element in "${matches[@]}"; do
  printf '%s\n' "${element}"
done

関数にしたい場合は、pattern=…関数done宣言に入れてください。

関連情報