findの-pruneオプションに複数のディレクトリを渡す

findの-pruneオプションに複数のディレクトリを渡す

バックアップファイルを見つけて削除するために使用していますが、find特定のディレクトリを検索から除外したいと思います。バックアップファイル名は、.bckまたはで終わることができます。bak~backup

3 つのディレクトリのみを除外する最小作業例 (MWE) コードは次のとおりです。

#! /bin/bash
find . -type d \( -path "./.*" -o -path "./Music" -o -path "./Documents" \) -prune -o -type f \( -name "*.bck" -o -name "*.bak" -o -name "*~" -o -name "*.backup" \) -print0 | xargs -0 --no-run-if-empty trash-put

\( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneMWEでは3つしか表示していませんが、除外するディレクトリが10個ほどある場合、構文が少し薄暗く見えます。

サービスにプッシュできる入力ファイル(除外ディレクトリのリストを含む)や、配列やリストなどの設定を使用するよりエレガントな方法はありますか?

もともと質問を書くときは明確ではないので申し訳ありません。

注:trash-putファイルを削除する代わりにファイルを移動するTrashcanユーティリティです[1]。

[1]。https://github.com/andreafrancia/trash-cli

答え1

使用-path ... -prune(-oまたは)括弧でグループ化された論理\( ... \)

find /somepath \( -path /a -prune -o \
                  -path /b -prune -o \
                  -path /c -prune \
               \) \
               -o -print

この例では、/somepath/aディレクトリやファイルは繰り返されません。/somepath/b/somepath/c


これは、いくつかの式と複雑な操作を使用して設計された例です-exec。これは、Linuxホスト上の純粋ファイルへのファイルパスとチェックサムを印刷し、主に一時ファイルまたは文字デバイスを含むパスを切り取ります。

$ find / \( -path /dev -prune -o \
            -path /proc -prune -o \
            -path /sys -prune \
         \) \
         -o -type f \
            -printf '%p ' -exec sh -c 'md5sum -- "{}" | cut -f1 -d" "' \;
/etc/services 00060e37207f950bf0ebfd25810c19b9
/etc/lsb-release f317530ede1f20079f73063065c1684e
/etc/protocols bb9c019d6524e913fd72441d58b68216
/etc/rsyslog.conf 8f03326e3d7284ef50ac6777ef8a4fb8
...

答え2

GNU findを使用すると(つまり、組み込みのLinuxまたはCygwinでは)、-regexこれらすべての-pathワイルドカードを単一の正規表現に組み合わせることができます。

find . -regextype posix-extended \
     -type d -regex '\./(\..*|Music|Documents)' -prune -o \
     -type f -regex '.*(\.(bck|bak|backup)|~)' -print0 |
xargs -0 --no-run-if-empty trash-put

BSDまたはmacOS-Eでは-regextype posix-extended

また、それをより短い標準語に置き換えることもできます-print0 | xargs -0 --no-run-if-empty trash-put--no-run-if-empty-exec trash-put {} +


1 ただし、より短い-r形式は他のいくつかの実装でサポートされており、一部のBSDではデフォルトの実装でもあります。

答え3

find私が知っている限り、ファイルからパターンを読むように指示するオプションはありません。簡単な回避策は、除外したいパターンをファイルに保存し、そのファイルを逆の入力に渡すことですgrep。たとえば、次のようなファイルとディレクトリを作成しました。

$ tree -a
.
├── a
├── .aa
├── .aa.bak
├── a.bck
├── b
├── .dir1
│   └── bb1.bak
├── dir2
│   └── bb2.bak
├── b.bak
├── c
├── c~
├── Documents
│   └── Documents.bak
├── exclude.txt
├── foo.backup
└── Music
    └── Music.bak

投稿した例を正しく理解したら、、ゴミ箱に移動a.bckし、その場所に保管したいと思います。そのため、次の内容でファイルを作成しました(必要に応じて追加できます)。.aa.bakb.bakc~foo.backupdir2/bb2.bak.aa.bak.dir1/bb1.bakDocuments/Documents.bakMusic/Music.bakexclude.txt

$ cat exclude.txt 
./.*/
./Music
./Documents

最初の結果は、現在のディレクトリ()の隠しバックアップファイルを移動し、隠されたディレクトリ()のすべてのバックアップファイルを除外することを意味していることを./.*/理解したので、これを使用します。これでコマンドを実行し、それを使用して不要なファイルを除外できます。.foo.foo/barfindgrep

$ find . -type f | grep -vZf exclude.txt | xargs -0 --no-run-if-empty trash-put

グレブオプション:

   -v, --invert-match
          Invert  the  sense  of matching, to select non-matching
          lines.  (-v is specified by POSIX.)
   -f FILE, --file=FILE
          Obtain patterns from FILE, one  per  line.   The  empty
          file  contains  zero  patterns,  and  therefore matches
          nothing.  (-f is specified by POSIX.)
   -Z, --null
          Output a zero byte (the ASCII NUL character) instead of
          the  character  that normally follows a file name.  For
          example, grep -lZ outputs a zero byte after  each  file
          name  instead  of the usual newline.  This option makes
          the output unambiguous, even in the  presence  of  file
          names  containing  unusual  characters  like  newlines.
          This  option  can  be  used  with  commands  like  find
          -print0,  perl  -0,  sort  -z,  and xargs -0 to process
          arbitrary file names, even those that  contain  newline
          characters.

答え4

これは問題ではなくシェル問題のように見えますfind。 (「\」なし!)を含むファイルの場合は、( -name dir1 -o -name dir2 ) -prune単に次のことができます。

find ... $(< /path/to/file)

ただし、これは検索呼び出し自体を変更せずに($ IFSを変更または変更することで)、eval find空白のないパスでのみ機能します。

ファイルをより簡単にしたい場合は、スクリプトを作成できます。

# file content
dir1
dir2
dir3

# script content
#!/bin/bash
file=/path/to/file
# file may be checked for whitespace here
grep '[^[:space:]]' "$file" | { empty=yes
  while read dir; do
    if [ yes = "$empty" ]; then
      echo -n "( "
      empty=no
    else
      echo -n " -o "
    fi
    echo -n "-name ${dir}"
  done
  if [ no = "$empty" ]; then
    echo -n " ) -prune"
  fi; }

そして使用

find ... $(/path/to/script)

代わりに。

関連情報