我用于find
查找和删除备份文件,但希望从搜索中排除某些目录。备份文件名可以以.bck
、bak
、~
或结尾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
使用(-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.bak
、b.bak
、c~
和foo.backup
移至dir2/bb2.bak
垃圾箱,并保留.aa.bak
、.dir1/bb1.bak
、Documents/Documents.bak
和Music/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)
反而。