存在しない可能性があるディレクトリセットを探す

存在しない可能性があるディレクトリセットを探す

生成された「開始点」セットから find を呼び出す必要がありますが、一部のパスが無効である可能性があります。

paths() {
    #
    # mock version of the generator
    #
    echo /bin
    echo kjsdfhk
    echo /etc
}

find $(paths) -type f name foo.sh

私の問題は、パスが有効であるかどうかわからず、そうでない場合は静かに無視したいということです。私にとって最も簡単なことは今です。

paths \
  | while read path;
    do
        test -e "$path" || continue
        find "$path" -type f -name foo.sh
    done

しかし、これは高価です。探すすべての有効なパスに対して、コード全体をループ内で呼び出すことができるので、より効率的な方法を見つけたいと思います。

シンプルで非常にUnixのソリューションは次のとおりです。探すSTDINで「スタートポイント」を読んでください。

paths \
  | while read path;
    do
        test -e "$path" || continue
    done \
  | find - -type f -name foo.sh

それを除いて...探すこれはサポートされていません! :)

どんなアイデアがありますか?

パスはユーザーが提供するため、スペース(およびその他の興味深い事項)を考慮する必要があります。また、POSIXを目指していますが、/bin/sh犠牲になる可能性があります。 (ああ、STDERR全体を沈めることも/dev/nullオプションではありません...)

修正する:申し訳ありません。 bashだけが利用できることを言及するのを忘れていました。 POSIXのコメントのため、より混乱する可能性があります。実際、コードはBashスクリプトでソースコードの断片を探しており、そのほとんどは現在次の場所にあります。シェン今後のバージョンでは、いくつかの屈辱感を取り除くことができます。したがって、より多くのbashismを追加することを避けることができれば素晴らしいです。しかし、@stephaneの答え(今投票してください!)のように、どんなにエレガントであっても、「zshisms」を余裕がありません。

答え1

zsh配列に次のパスがある場合:

files=($(paths))

paths(これにより、出力は空白、タブ改行、またはヌルに分割されます)または:

files=(${(f)"$(paths)"})

行を分割するには、次のようにします。

find $^files(N) -type f -name foo.sh

またはディレクトリに制限したい場合:

find $^files(/N) -type f -name foo.sh

これで、これらのファイルがない場合は、次のように実行できます。

find -type f -name foo.sh

一部のfind実装(GNUなど)では、これは現在のディレクトリから検索することを意味します。これを防ぐには、次のようにします。

dirs=($^files(/N))
(($#dirs)) && find $dirs -type f -name foo.sh

または:

setopt cshnullglob
find $^files(/) -type f -name foo.sh

今はzshもう必要ありませんfind。簡単に次のことができます。

files=($^files/**/foo.sh(.N))

!これにより、ファイルが類似またはブロック-nameされている場合でも操作が容易になります。find

ただし、ファイルがディレクトリへのシンボリックリンクである場合(findここではファイルを見つけることはありませんが、実際に望むものかもしれません)、zsh状況は異なります。

答え2

bashを使用しているので、パスのリストを次の場所に保存します。大量に。配列を繰り返して、既存のパスの配列を作成します。結果の配列が空の場合は特別な場合が必要です。それ以外の場合は、findエラーが発生したり、現在のディレクトリに移動したりします。

完全に堅牢にするには、パスパラメータがaで始まらないようにする必要があります-。それ以外の場合は、findオプションまたはデフォルトパラメータとして解釈されます。

paths=(/some/where around/here -print 'one  with
odd spaces')
existing_paths=()
for x in "${paths[@]}"; do
  if [ -e "$x" ]; then
    if [[ "$x" = -* ]]; then x="./$x";; fi
    existing_paths+=("$x")
  fi
done
if [[ ${#existing_paths[@]} -ne 0 ]]; then
  find "${existing_paths[@]}" -type f -name foo.sh
fi

答え3

Pythonを使用してパスが存在するかどうかをテストし、見つかったNULで区切られたパスをエコーし​​、それを入力してに渡すことができますxargs。ただし、Python出力の長さがfindを一度だけ呼び出す必要がある最大引数の長さを超えない限り、次のようになりますfind

python -c 'import os, sys;  sys.stdout.write("\0".join([x for x in sys.argv[1:] if os.path.exists(x)]) + "\0")' a\ b xyz abc| xargs -0 --no-run-if-empty find

Python部分(-cの一重引用符引数):

  • 必要なモジュールをインポートしてosコマンドsysに使用
  • パラメータを繰り返しa bxyz使用abcfor x in sys.argv[1:]
  • パスが存在する場合のみリストに入れます。[x ... if os.path.exists(x)]
  • NULを使用してリストに参加し、NULを追加してstdoutに記録します。 sys.stdout.write("\0".join[...] + "\0")

空のディレクトリがある場合、touch a\ b xyz最初の2つの引数が見つかりましたが見つからabcず、findが後者のパスに渡されないことがわかります。

答え4

これがあなたにうまくいけば

$(paths) 検索 -f 名前 foo.sh と入力します。

その後、有効なパスのみをエコーするようにパス機能を変更できます。

if [ -d "$path" ]; then echo $path fi

または、パスがファイルである可能性がある場合

if [ -d "$path" ] || [ -e "$path" ]; then echo $path fi

このように、 find は一度だけ実行されます。パス関数を変更できない場合は、findコマンドに渡す前に、すべての有効なパスをフィルタリングする他の関数を常に生成して、間違ったパスが渡されないようにすることができます。

関連情報