简体   繁体   中英

Using Bash to send space separated list of files to Git

This one has me flummoxed.

I'm creating a Bash script which copies files into a series of repos and then adds them for commit. The files sometimes have spaces in the filenames, so they need to be quoted.

I've created a quoted space-separated list of filenames in a variable in Bash: $x . When I run echo $x I get this:

 'test 01.sql' 'test 02.sql' 'test_03.sql' 

If I manually run the following (in the appropriate directory), I have no problem:

git add 'test 01.sql' 'test 02.sql' 'test_03.sql'

But in my script, if I run:

git add $x or git add "$x" or git add "${x}" , I get a fatal pathspec error from Git.

fatal: pathspec ''test 01.sql' 'test 02.sql' 'test_03.sql'' did not match any files

I've tried both single and double quoted strings with no difference.

The example has been simplified. The full version uses absolute paths to the files.

'/Volumes/HardDrive/Repo/queries/test 01.sql' '/Volumes/HardDrive/Repo/queries/test 02.sql' '/Volumes/HardDrive/Repo/queries/test_03.sql'

It works when echoed from the script and pasted manually into the git add command, but doesn't work when passed from a variable in the script.

Instead of creating a concatenated string, use an array, for example:

arr=('test 01.sql')
arr+=('test 02.sql')
arr+=('test 03.sql')

Then you'll be able to add the files in the Bash array using:

git add "${arr[@]}"

A shell-quoted list of names is a very poor choice of formats to use for programmatic (as opposed to human) input.

This seems nonintuitive, but it's true for a reason: When you type in a command in the shell, that command is parsed as code; it's able to contain redirections, command substitutions, and other expansions with potentially dangerous side effects.

To allow data to be safely handled without any risk of evaluation as code, the shell performs parameter expansion only after most other parsing stages (exclusive of string splitting of expansion results and globbing) are complete.


If you were manually generating this input and reviewing it for correctness, you could use eval :

# THIS IS DANGEROUS unless you trust your string to contain no malicious content!
files="'test 01.sql' 'test 02.sql' 'test_03.sql'"
eval "git add -- $files"

However, if you're programmatically generating this list, format it as a NUL-delimited stream, and use xargs :

# generate a list in unambiguous NUL-delimited form
printf '%s\0' "/path/to/file 1" "/path/to/file 2" >file.txt

# use that list to run `git add` for the named files
xargs -0 git add -- <file.txt

...or a NUL-delimited stream can be read into a shell array:

# read that list into an array
files=( )
while IFS= read -r -d '' filename; do files+=( "$filename" ); done <files.txt

# ...and use the array
git add -- "${files[@]}"

Is it possible to simply do:

git add .

That way you don't need to specifically reference the filenames that may have spaces (but will stage everything)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM