
我想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 {} +
或使用 not $'\0'
,因為/
它無論如何都不能作為參數傳遞給外部命令:
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。