我正在为我的点文件编写一个部署脚本,它们存储在一个 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
它是我的 chroot 安装的挂载点,我实际上是从我的自定义 Arch Linux 安装映像运行它
但是当涉及到实际删除这些文件时,我没有成功,问题在于名称中包含空格的文件(Font\ Awesome\ Icons.otf
例如)
我尝试通过将上面的命令传递到以下命令序列来引用要删除的每个文件:
<get_dotfiles> | sed "s|^|\"/mnt/home/henriquehbr/|" | sed "s/$/\"/"
上面的方法不起作用,我认为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
答案1
使用空字节作为分隔符:
git ls-tree -z … | xargs -0 rm
请注意,在大多数 shell 中,您不能将空字节放入变量中,因此$(git ls-tree -z …)
不会执行任何有用的操作。
如果您尝试使用引号导致将引号传递给 ,则不会起作用rm
。rm
与大多数程序一样,只需要文件名。对于它来说,"
是一个普通字符,就像文件名中出现的任何其他字符一样。注入引号也无助于 shell 中的替换:不带引号的变量替换或命令替换 ($foo
或$(mycommand …)
) 只是在空格处分割(然后将每个单词视为通配符模式),引号在此阶段不相关。如果文件名本身不包含太“异国情调”的字符(例如反斜杠、制表符、双引号等),则注入引号并传递给xargs
(不带) 可以起作用,但它比使用and复杂得多。-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