[英]How to cope with spaces in file names when iterating results from git diff --name-only
我正在处理的脚本需要从 git diff 中遍历每个文件。 但是,我不知道如何处理文件名中的空格。 任何有空格的文件都被分成“2 个文件”。 我知道它们需要被包裹在" "
但我不知道如何在它进入@
param 之前实现它。
当文件名中有空格时,我应该如何遍历文件
git diff --name-only $1
?
这是一个重现错误的简单测试:
copyfiles()
{
echo "Copying added files"
for file in $@; do
new_file=$(echo ${file##*/})
directory=$(echo ${file%/*})
echo "Full Path is is $file"
echo "File is $new_file"
echo "Directory is $directory"
cp $file $COPY_TO
done
}
COPY_TO="testDir"
DIFF_FILES=$( git diff --name-only $1)
copyfiles $DIFF_FILES
该脚本目前运行如下:
test.sh <git commit id>
--name-only
的输出会受到一定程度的转义。 不幸的是,与它合作很尴尬。
git diff
解释了-z
选项下的转义(和替代方案):
-z
当 --raw、--numstat、--name-only 或 --name-status 已给出时,不要修改路径名并使用 NUL 作为输出字段终止符。
如果没有此选项,则每个路径名输出将分别用 TAB、LF、双引号和反斜杠字符替换为 \\t、\\n、\\" 和 \\,如果发生任何这些替换,路径名将用双引号括起来.
一个例子:
$ git init ugh
$ cd ugh
$ touch 'spa ce' $'new\nline' $'t\tab'
$ ls # Unhelpful really
new?line spa ce t?ab
$ ls --quote # Minorly helpful but wrong (for shell usage)
"new\nline" "spa ce" "t\tab"
$ git add -A
$ git diff --cached --name-only
"new\nline"
spa ce
"t\tab"
$ git diff --cached --name-only -z # Doesn't copy and paste well and is a bit confusing to read this way
new
line^@spa ce^@t ab^@
$ printf %q\\n "$(git diff --cached --name-only -z )"
$'new\nlinespa cet\tab'
总之,这里的关键是,要做到这一点的最好办法是使用-z
输出和读取文件的列表与read
。
while IFS= read -r -d '' file; do
printf 'file = %q\n' "$file"
done < <(git diff --cached --name-only -z)
您还可以将git diff
的输出通过管道传输到while
循环,但是如果循环完成后您需要循环内部的变量,则需要此 Process Substitution 方法来避免管道方法 D 的子外壳问题。
使用-z
来让 git-diff 使用空终止符。 例如:
export COPY_TO
git diff -z --name-only | xargs -0 sh -c 'for file; do
new_file=$(echo ${file##*/})
directory=$(echo ${file%/*})
echo "Full Path is is $file"
echo "File is $new_file"
echo "Directory is $directory"
cp "$file" "$COPY_TO"
done' sh
请注意,更合理的解决方案是拒绝创建名称中包含空格的文件的人的拉取请求。
git diff -z --name-only |
while read -d $'\0' file
do
echo ${file}
done
感谢@Etan Resiner 的回答。 这是一个示例,展示了如何使用git diff --name-only -z "$merge_base" $BACKUP_BRANCH
作为包含发送到git diff
或git difftool
转义文件名的输入。 它需要一个额外的--
,所以请看下面的代码。
我能够用它修复我的git changes
程序,所以现在它可以处理 git repo 中的文件名,这些文件名在文件名中包含空格或特殊字符(例如'
)。 现在,程序看起来是这样的:
用法:
Usage: git changes <common_base> <backup_branch> [any other args to pass to git difftool]
git-changes.sh:
特别注意files_changed_escaped
变量的填充,这是直接从@Etan Reisner 的回答中学到的。
COMMON_BASE_BRANCH="$1"
BACKUP_BRANCH="$2"
# Obtain all but the first args; see:
# https://stackoverflow.com/questions/9057387/process-all-arguments-except-the-first-one-in-a-bash-script/9057392#9057392
ARGS_3_AND_LATER="${@:3}"
merge_base="$(git merge-base $BACKUP_BRANCH $COMMON_BASE_BRANCH)"
files_changed="$(git diff --name-only "$merge_base" $BACKUP_BRANCH)"
echo "Checking for changes against backup branch \"$BACKUP_BRANCH\""
echo "only in these files which were previously-modified by that backup branch:"
echo "--- files originally changed by the backup branch: ---"
echo "$files_changed"
echo "------------------------------------------------------"
echo "Checking only these files for differences between your backup branch and your current branch."
# Now, escape the filenames so that they can be used even if they have spaces or special characters,
# such as single quotes (') in their filenames!
# See: https://stackoverflow.com/questions/28109520/how-to-cope-with-spaces-in-file-names-when-iterating-results-from-git-diff-nam/28109890#28109890
files_changed_escaped=""
while IFS= read -r -d '' file; do
escaped_filename="$(printf "%q" "$file")"
files_changed_escaped="${files_changed_escaped} ${escaped_filename}"
done < <(git diff --name-only -z "$merge_base" $BACKUP_BRANCH)
# DEBUG PRINTS. COMMENT OUT WHEN DONE DEBUGGING.
echo "$files_changed_escaped"
echo "----------"
# print withOUT quotes to see if that changes things; ans: indeed, it does: this removes extra
# spaces and I think will replace each true newline char (\n) with a single space as well
echo $files_changed_escaped
echo "=========="
# NB: the `--` is REQUIRED before listing all of the files to search in, or else escaped files
# that have a dash (-) in their filename confuse the `git diff` parser and the parser thinks they
# are options! It will output this error:
# fatal: option '-\' must come before non-option arguments
# Putting the list of all escaped filenames to check AFTER the `--` forces the parser to know
# they cannot be options, because the `--` with nothing after it signifies the end of all optional
# args.
git difftool $ARGS_3_AND_LATER $BACKUP_BRANCH -- $files_changed_escaped
echo "Done."
您可以在此处下载git changes
程序作为我的 dotfiles 项目的一部分: https : //github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles 。
它还包含诸如git diffn
类的东西,它是带有行号的git diff
。
我认为你的代码需要这个命令IFS=$'\\n'
echo "this command is important"
IFS=$'\n'
for file_change in `git diff --name-only $1`
do
echo "Put $file_change ..."
# File Name
fileName=$(basename "$file_change")
echo "$fileName"
# Directory
dir=$(dirname "$file_change")
echo "$dir"
# copy file
cp $file_change $REMOTE_DIR$file_change
done
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.