A script I am working on needs to go through each file from a git diff. However, I don't know how to deal with spaces in the file name. Any files that have a space are split into "2 files". I know they need to be wrapped in " "
but I don't know how to achieve that before it goes to the @
param.
When there are spaces in filenames, how should I iterate over the files from
git diff --name-only $1
?
Here is a simple test that reproduces the error:
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
The script is currently run like:
test.sh <git commit id>
There are two main ways to handle such files or directories; one uses escape characters, i.e., backslash (\<space>), and the second is using apostrophes or quotation marks. Using backslash can be confusing; it's easy and better to use quotation marks or apostrophes.
Avoid spaces Spaces are not supported by all operating systems or by command line applications. A space in a filename can cause errors when loading a file or when transferring files between computers. Common replacements for spaces in a filenames are dashes (-) or underscores (_).
Don't start or end your filename with a space, period, hyphen, or underline. Keep your filenames to a reasonable length and be sure they are under 31 characters. Most operating systems are case sensitive; always use lowercase. Avoid using spaces and underscores; use a hyphen instead.
The output from --name-only
is subject to a certain amount of escaping. Unfortunately it is awkward to work with.
git diff
explains the escaping (and an alternative) under the -z
option:
-z
When --raw, --numstat, --name-only or --name-status has been given, do not munge pathnames and use NULs as output field terminators.
Without this option, each pathname output will have TAB, LF, double quotes, and backslash characters replaced with \t, \n, \", and \, respectively, and the pathname will be enclosed in double quotes if any of those replacements occurred.
An example:
$ 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'
Anyway, the point here is that the best way to do this is to use the -z
output and read the list of files with read
.
while IFS= read -r -d '' file; do
printf 'file = %q\n' "$file"
done < <(git diff --cached --name-only -z)
You could also pipe the output from git diff
to the while
loop but if you need variables from inside the loop once the loop is done you need this Process Substitution method to avoid the subshell problems with the pipe methodD.
git diff -z --name-only |
while read -d $'\0' file
do
echo ${file}
done
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With