無法使用 rm 刪除名稱上帶有空格的多個文件
我正在為我的 dotfiles 開發一個部署腳本,它們儲存在 Git 裸儲存庫中,我
git checkout
的文件從該儲存庫進入我的主目錄,為了在沒有數千個 Git 簽出錯誤的情況下無縫執行,我必須刪除已經存在的那些(.bashrc
例如),我實際上是用以下命令檢索它們git --git-dir=$HOME/repos/dots --work-tree=$HOME ls-tree --full-tree --full-name --name-only -r HEAD | sed "s|^|/mnt/home/henriquehbr/|"
注意:原因
/mnt
是它是我的 chrooted 安裝的掛載點,我實際上是從我的自定義 Arch Linux 安裝映像執行它但是當談到實際刪除這些文件時,我沒有成功,問題在於名稱中包含空格的文件(
Font\ Awesome\ Icons.otf
例如)我嘗試通過在以下命令序列上傳遞上面的命令來引用要刪除的每個文件:
<get_dotfiles> | sed "s|^|\"/mnt/home/henriquehbr/|" | sed "s/$/\"/"
上面的方法不起作用,我認為
rm
是按字面意思對待引號,就好像它們是文件名的一部分其他試驗是通過製作一個好的 ol’
for
循環:dotfiles=$(git --git-dir=$HOME/repos/dots --work-tree=$HOME ls-tree --full-tree --full-name --name-only -r HEAD | sed "s|^|/mnt/home/henriquehbr/|") for dotfile in $dotfiles; do echo "$dotfile" # Works properly, displays dotfile path rm "$dotfile" # Doesn't works, writes: '$'\n'' after the path and fails done
實際上,我最終得到了一種更簡單的解決方案,它不需要所有這些複雜性,一個簡單的“強制” git checkout 可以在部署過程中替換我所有的舊點文件:
git checkout -f
使用空字節作為分隔符:
git ls-tree -z … | xargs -0 rm
請注意,在大多數 shell 中,您不能將空字節放入變數中,因此
$(git ls-tree -z …)
不會做任何有用的事情。如果它們導致將引號傳遞給
rm
.rm
,像大多數程序一樣,只需要文件名。對它來說,"
是一個普通字元,就像可以出現在文件名中的任何其他字元一樣。注入引號也無助於 shell 中的替換:未引用的變數替換或命令替換 ($foo
or$(mycommand …)
) 只是在空格處拆分(然後將每個單詞視為萬用字元模式),引號在此階段不相關。如果文件名本身不包含過於“異國情調”的字元,例如反斜杠、製表符、雙引號等,則注入引號和傳遞到xargs
(不帶)可以工作,但它比使用和復雜得多。-0``git ls-tree -z``xargs -0
假設文件名不包含任何
git ls-tree
本身引用的字元(包括換行符),這將起作用(再一次,不干預引號):set -f # disable wildcard expansion (globbing) IFS=' ' # set only newline as the word separator for dotfile in $(git ls-tree …); do …
這並不是一個好主意。如果你只是想在執行時強行擦除現有文件
git checkout
,Git 會很樂意在你執行時刪除文件git checkout -f …
。檢查現有文件會更安全,以防它們包含重要的東西。git checkout -b original-files git --git-dir=… ls-tree -z | xargs -0 git add git commit -m "Original files on $HOSTNAME" git checkout origin/master