我想rsync
遍历一些用数组指定的文件,并删除目录中的任何其他文件。
我能想到的唯一方法是使用 , 删除其他文件find
,并rsync
覆盖文件,因此我们复制尽可能少的文件。
在下面的示例中,我想删除 中/tmp/tmp/
除btrfs_x64.efi
和之外的任何其他文件iso9660_x64.efi
。
$ refind_efi_dir='/tmp/tmp/'
$ drivers=('btrfs_x64.efi' 'iso9660_x64.efi')
$ find ${refind_efi_dir}drivers_x64/ "${drivers[@]/#/! -name }" -type f -exec rm -f {} +
我希望扩展扩展为以下命令:
$ find /tmp/tmp/drivers_x64/ ! -name btrfs_x64.efi ! -name iso9660_x64.efi -type f -exec rm -f {} +
但它似乎正在运行以下命令:
$ find /tmp/tmp/drivers_x64/ "! -name btrfs_x64.efi" "! -name iso9660_x64.efi" -type f -exec rm -f {} +
有办法获得前者吗?理想情况下,如果某些数组条目中有空格,它也可以工作。
答案1
是的,这里您需要find
为数组的每个元素生成 3 个参数。 Alsofind
需要一个模式,因此为了使其匹配确切的文件名,-name
您需要转义find
通配符运算符(*
、?
和):[
\
set -o extendedglob # for (#m)
exclusions=()
for name ($drivers) exclusions+=(! -name ${name//(#m)[?*[\\]/\\$MATCH})
find ${refind_efi_dir}drivers_x64/ $exclusions -type f -exec rm -f {} +
"${array[@]/pattern/replacement}"
对每个元素执行替换后,将扩展为与数组中的元素一样多的元素。
在这里,假设-name
需要一个文件姓名模式,它不应该包含/
,因此您可以将每个元素替换为!/-name/element
,然后拆分/
:
set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
${(@s[/])${drivers//(#m)[?*[\\]/\\$MATCH}/#/!\/-name\/} \
-type f -exec rm -f {} +
或者使用$'\0'
not ,因为/
它无论如何都不能作为参数传递给外部命令:
set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
${(@0)${drivers//(#m)[?*[\\]/\\$MATCH}/#/!$'\0'-name$'\0'} \
-type f -exec rm -f {} +
但这对可读性没有多大帮助......
在这里,您还可以使用zsh
's glob 来表示所有内容:
(cd -P -- $refind_efi_dir && rm -f -- **/^(${(~j[|])drivers})(D.))
其中参数扩展标志使用和连接数组j[|]
元素,导致其被视为全局运算符。该模式被否定(您需要该选项)。包括隐藏文件,限制为常规文件,例如您的.$drivers
|
~
|
^
extendedglob
D
.
-type f
答案2
"${drivers[@]/#/! -name }"
放入! -name
与每个数组元素相同的 shell 字。"${=drivers[@]/#/! -name }"
会分割结果,因此!
and-name
最终会成为单独的 shell 单词,但数组元素也会在空格处分割。
一种解决方案是滥用e
和P
全局限定符:
find … /(e\''reply=($drivers)'\'P\''!'\'P\''-name'\') …
/(…)
扩展 glob 模式/
,它始终与一件事(根目录)完全匹配,并将 glob 限定符应用于它。 glob 限定符e
用数组变量 的新值替换每个匹配项reply
,我们将其设置为要应用后续 glob 限定符的单词列表。然后,使用两次的 glob 限定符P
在每次匹配之前将指定的文本作为单独的单词插入。
在这个具体案例中,正如斯特凡·查泽拉斯 (Stéphane Chazelas) 所展示的,您可以简单地使用 zsh glob 完成所有操作,而无需使用 find。