[SOLVED] Passing string with a space between words through various function layers

Issue

I have two functions in Bash. One is a generic run function, that accepts an input and evaluates it, while printing the command, and testing the exit code. This is used in a large script to ensure each command executes successfully before continuing.

The second one is a complex function, that is doing some Git history parsing. The problematic line is the only one shown.

I am calling this function from a for-loop, that iterates over a list of terms to search. The issue is that spaces are not being handled correctly, when between other words. I have tried running my script though shell-checking websites, and all of the suggestions seem to break my code.

function run() {
        echo "> ${1}"
        eval "${1}"
        # Test exit code of the eval, and exit if non-zero        
}

function searchCommitContents() {
        run 'result=$(git log -S'"${1}"' --format=format:%H)'
        # Do something with result, which is a list of matching SHA1 hashes for the commits
        echo "${result}"
}

# Main

declare -a searchContents=('foo' 'bar' ' foo ' 'foo bar')

for i in "${searchContents[@]}"
do
    searchCommitContents "${i}"
done

Here is the output I get:

> result=$(git log -Sfoo --format=format:%H)
<results>

> result=$(git log -Sbar --format=format:%H)
<results>

> result=$(git log -S foo  --format=format:%H)
<results>

> result=$(git log -Sfoo bar  --format=format:%H)
fatal: ambiguous argument 'bar': unknown revision of path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

I tried to add additional single and double-quotes to various areas of the code, such that the 'foo bar' string would not resolve to two different words. I also tried adding an escape to the dollar sign, like so: -s'"\${1}"' based on other questions on this site.

Solution

Why are you printing result=$(? It’s an internal variable, it can be anything, there is no need for it in logs.

Print the command that you are executing, not the variable name.

run() {
   echo "+ $*" >&2
   "[email protected]"
}
searchCommitContents() {
   local result
   result=$(run git log -s"${1}" --format=format:%H)
   : do stuff to "${result}"
   echo "$result"
}

issue with an input that has a space in the middle.

If you want quoted string, use printf "%q" or ${[email protected]} for newer Bash, but I don’t really enjoy both quoting methods and just use $*. I really like /bin/printf from GNU coreutils, but it’s a separate process… while ${[email protected]} is the fastest, it’s (still) not enough portable for me (I have some old Bash around).

# compare
$ set -- a 'b  c' d
$ echo "+ $*" >&2
+ a b  c d
$ echo "+$(printf " %q" "[email protected]")" >&2
+ a b\ \ c d
$ echo "+" "${@@Q}" >&2
+ 'a' 'b  c' 'd'
$ echo "+$(/bin/printf " %q" "[email protected]")" >&2
+ a 'b  c' d

Answered By – KamilCuk

Answer Checked By – Marie Seifert (BugsFixing Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *