Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cope with spaces in file names when iterating results from git diff --name-only

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>
like image 671
chrispepper1989 Avatar asked Jan 23 '15 12:01

chrispepper1989


People also ask

How do you handle spaces in filename?

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.

Why can spaces cause problems in file names?

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 (_).

Are spaces in file names a problem?

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.


2 Answers

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.

like image 96
Etan Reisner Avatar answered Sep 19 '22 10:09

Etan Reisner


git diff -z --name-only |
while read -d $'\0' file
do
    echo ${file}
done
like image 33
liu Avatar answered Sep 19 '22 10:09

liu