将多个目录传递给 find 中的 -prune 选项

将多个目录传递给 find 中的 -prune 选项

我用于find查找和删除备份文件,但希望从搜索中排除某些目录。备份文件名可以以.bckbak~或结尾backup

仅需要排除三个目录的最小工作示例 (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" \) -prune似乎有点笨拙,特别是如果有大约十个目录需要排除,尽管我在 MWE 中只显示了三个。

是否有一种更优雅的方法,使用输入文件(带有排除的目录列表)或类似数组或列表的构造,可以将其压入服务?

我很抱歉在写原始问题时没有更明确。

注意: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.backup移至dir2/bb2.bak垃圾箱,并保留.aa.bak.dir1/bb1.bakDocuments/Documents.bakMusic/Music.bak它们所在的位置。因此,我创建了包含以下内容的文件exclude.txt(您可以添加任意数量的内容):

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

我之所以使用,./.*/是因为我理解您最初的发现意味着您想要移动.foo当前目录中的隐藏备份文件 ( ),但排除隐藏目录 ( .foo/bar) 中的任何备份文件。因此,我现在可以运行find命令并使用它grep来排除不需要的文件:

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

grep 选项:

   -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)

不过,在不更改 find 调用本身(更改为eval find或通过更改 $IFS)的情况下,这仅适用于没有空格的路径。

如果您想让文件更简单,您可以编写一个脚本。

# 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)

反而。

相关内容