4 ms·
The only time you should use $* is inside a debug message like "unable to open $* ($!)". If you’re passing around arguments, always use "$@". If you know enoug
by floatingatoll 7y ago
The only time you should use $* is inside a debug message like "unable to open $* ($!)". If you’re passing around arguments, always use "$@".
If you know enough bash to disagree, you know enough bash to use the third case safely :)
- eikenberry 7y agoI was going to say the same thing (so I will). The rule is just always use "$@" and then you have only one case to reason about and it is what you want 99.99% of the time anyways.
- dnautics 7y agoI think it's also reasonable to use in ssh and I believe su, which are commands that expect a single string as their inner command parameter.
- floatingatoll 7y agoI can count on ten fingers the number of times in twenty years I've worked with another bash coder who did the su and ssh cases correctly without triggering escaping bugs. It's not any insult on them, but it's almost always done incorrectly and happens to work due to the absence of whitespace and backslashes, leading to eventual bugs (that Shellcheck won't always catch). Given: # ARGV=( "one two", "three four" ) It's probably safe to recommend "$@" for use with su only whenyou use -c correctly, as you're locally specifying the args without any further IFS interference. But $* isn't usable: # CORRECT su root -c 'rm "$@"' -- "$@" rm "one two" "three four" # incorrect su root -c "rm \"$@\"" rm one two three four # wrong arguments # incorrect su root -c "rm" "$@" rm # -c doesn't use arguments # incorrect su root -c 'rm "$@"' "$@" rm "three four" # loses the first argument (?!) # incorrect: su root "rm" "$*" rm one two three four # wrong arguments # incorrect: su root "rm $*" "rm one two three four" # command not found It's probably safe to recommend "$@" for use with ssh only when using printf %q to ensure that you escape your arguments for their transit through ssh to the remote host, as otherwise the arguments get corrupted by the extra layer of shell processing. $* isn't usable here either: # CORRECT ssh remote -- 'rm '"$(printf '%q ' "$@")" rm "one two" "three four" # incorrect ssh remote 'rm '$(printf '%q ' "$*") rm "one two three four" # wrong arguments # incorrect ssh remote rm "$@" rm one two three four # wrong arguments # incorrect ssh remote "rm \"$@\"" rm "one two three four" # wrong arguments # incorrect ssh remote 'rm "$@"' "$@" rm one two three four # wrong arguments # incorrect: ssh remote "rm" "$*" rm one two three four # wrong arguments # incorrect: ssh remote "rm" "$*" rm one two three four # wrong arguments EDIT: Shellcheck misses 3 of the 4 broken su cases, but catches all of the broken ssh cases. (And produced a warning I disagreed with in one of the complete examples, but in the spirit of things, added double quotes to silence it.)
- wadkar 7y agowow, this was very enlightening! I was wondering if you could elaborate a little bit on what you mean by: > as you're locally specifying the args without any further IFS interference I know that IFS = Input Field Separator. But how does it interfere when doing su root -c “command”? I might be missing something obvious here. Also the ssh + printf is gold! But to be honest, I would personally never use anything from $@ inside an ssh remote “command”. Too risky even with proper quoting. Thanks!
- floatingatoll 7y agoIFS controls the "$* joining character, if I remember correctly, and so if you use IFS=\0 then you can “safely” pass multiparty args in and then somehow take them apart on the other side. But this is the bad kind of crazy to me, and if I was reviewing code with this in it, I’d flag it for replacement. It’s the sort of thing that leads to feeling smart at working around knowing $@ but in the end it’s best just to truly understand $@ or use helper scripts to avoid quoting drama altogether. YMMV.
- hinkley 7y agoI’m confused why your correct rm examples include $@ twice. Do you mind explaining? I’ve been bitten by $@ and —- related mistakes at roughly the same time within the last month or so. Luckily nothing to do with sudo.
- floatingatoll 7y agoThis one, probably: # CORRECT su root -c 'rm "$@"' -- "$@" rm "one two" "three four" The command is going to be evaluated by bash, with ARGV set to what you pass after the hyphens as arguments to the bash environment that su spawns. Passing the arguments to su using "$@" to encode your calling function’s ARGV ensures that they’re uncorrupted into the su-spawned bash environment’s ARGV, and then executing the literal command rm "$@" ensures that they’re uncorrupted into the spawned rm command’s ARGV. You could fake this with printf %q if you tried hard enough but that’s a high-risk game to diagnose issues with, for example when you’re trying to figure out why newlines are reaching the command as the letter n.
- AlexCoventry 7y agoI know enough bash to know a relevant shellcheck rule... Not sure that's enough to use it safely. :) https://github.com/koalaman/shellcheck/wiki/SC2124 https://github.com/koalaman/shellcheck/wiki/SC2124