无法使用 rm 删除名称上带有空格的多个文件

hen*_*hbr 1 bash arch-linux dash 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/|"
Run Code Online (Sandbox Code Playgroud)

注意:原因/mnt是它是我的 chroot 安装的挂载点,我实际上是从我的自定义 Arch Linux 安装映像中运行它的

但是当谈到实际删除这些文件时,我没有成功,问题在于名称中包含空格的文件(Font\ Awesome\ Icons.otf例如)

我已经尝试通过在以下命令序列中通过管道传输上面的命令来引用要删除的每个文件:

<get_dotfiles> | sed "s|^|\"/mnt/home/henriquehbr/|" | sed "s/$/\"/"
Run Code Online (Sandbox Code Playgroud)

上面的方法不起作用,我认为rm是按字面意思处理引号,就好像它们是文件名的一部分

其他试验是通过制作一个很好的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
Run Code Online (Sandbox Code Playgroud)

Gil*_*il' 5

使用空字节作为分隔符:

git ls-tree -z … | xargs -0 rm
Run Code Online (Sandbox Code Playgroud)

请注意,在大多数 shell 中,您不能将空字节放入变量中,因此$(git ls-tree -z …)不会做任何有用的事情。

如果您尝试使用引号将引号传递给rm. rm,像大多数程序一样,只需要文件名。对它来说,"是一个普通字符,就像可以出现在文件名中的任何其他字符一样。注入引号也无法帮助 shell 中的替换:未加引号的变量替换或命令替换 ( $fooor $(mycommand …)) 只是在空格处拆分(然后将每个单词视为通配符模式),在此阶段引号不相关。如果文件名本身不包含太“异国情调”的字符,例如反斜杠、制表符、双引号等,则注入引号并传递给xargs(没有-0)可以工作,但它比使用git ls-tree -zand复杂得多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 … 
Run Code Online (Sandbox Code Playgroud)

并不是说这些都是一个好主意。如果你只是想在运行时强行擦除现有文件,如果你运行git checkout.git ,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
Run Code Online (Sandbox Code Playgroud)