1
foo (){
            sudo -- sh -c "cd /home/rob; echo \"$@\""
}

I'm trying to make a bash function in .bashrc that will sudo, change to a particular directory and then run a Python command. For demo purposes I have changed this to echo in the above example.

Even though I have quoted $@ to pass all arguments to my echo command, it only works with one:

$ foo 1
1
$ foo 1 2
2": -c: line 0: unexpected EOF while looking for matching `"'
2": -c: line 1: syntax error: unexpected end of file

What gives? How can I pass multiple arguments to this function?

2

Add set -v to your function and you will see what is happening:

$ f (){ set -v; sudo -- sh -c "cd /tmp; echo \"$@\""; } 
$ f 1 2
+ sudo -- sh -c 'cd /tmp; echo "1' '2"'

What is happening? Your use of $@ has created two strings that have been single quoted to cd /tmp; echo "1 and 2". You can use $* if you dont want this splitting. See the bash man page section on Special Parameters:

@ ... When the expansion occurs within double quotes, each parameter expands to a separate word. ... If ... within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word.

So in your case the single string (or word) "xxx$@yyy" expands to 2 strings 'xxx1' and '2yyy'. You can test this using set -- 1 2 3 to set $@ and then use printf ">%s<" "$@" to see the number of words that "$@" becomes (as the printf will repeat the format for each argument):

$ set -- 1 2 3
$ printf ">%s< " "x$@y"
>x1< >2< >3y<
$ printf ">%s< " "x$*y"
>x1 2 3y<

If you want to pass arguments to sh -c command one hack is to place them after the single command as arguments, but this may not work on all shells:

f (){ set -v; sudo -- sh -c 'cd /tmp; echo "$@"' sh "$@"; }
f 1 2
+ sudo -- sh -c 'cd /tmp; echo "$@"' sh 1 2

The extra sh argument is because the first arg is taken by the shell as the name of the process.

  • Thanks although set -v just seems to give me __vte_prompt_command in the output. – SilverlightFox Aug 23 '17 at 17:21
  • That seems to be some sort of function for the vte terminal emulator squatting the interactive shell prompt. You might want to try a separate bash --norc or similar for testing in a more neutral setting. – meuh Aug 23 '17 at 17:40
  • I will mark this as answer - thanks. Why does $@ cause single quoting of the two strings? Why is there then a mixture of single quotes and double quotes? Your solution works, but I'm still at a loss why you need sh when you don't need it for other functions where I've used double quotes around $@. – SilverlightFox Aug 23 '17 at 21:20
  • I added the relevent section from the bash man page to my answer. Note, the single quotes is just an artifact of set -v so that it can show you where each string (or word) ends. Your \" is at this stage just an ordinary character. It is only when sh -c parses the command that it will see the character as a double quote. – meuh Aug 24 '17 at 7:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.