I was trying to learn and write some fish scripts, but I encountered a strange issue.

# a.fish
# this is a fish script 
set temp (getopt -o abc -l ace,bad,correct -- $argv)
echo $temp

When I ran fish ./a.fish -a -b --correct it worked fine and output

-a -b --correct --

However, when I changed the $argv to "$argv" and ran it again, I got this issue

Then I wrote a bash script

# a.sh
temp=$(getopt -o abc -l ace,bad,correct -- $@)
echo $temp

and ran it with bash ./a.sh -a -b --correct. Worked fine. After I added " around $@ it still worked fine!

So here comes the question:

What's the difference between $argv and $@ (or maybe I should ask whether fish and bash handle variables differently?)

I thought it was just simple replacement but this confused me. Can somebody help me? Any help will be appreciated :)

OS: ubuntu 16.04


Yes, different shells are different. strace as always can help show what exactly is sent through an execve(2) call.

$ cat fecho
/usr/bin/echo $argv
$ cat fechoquoted 
/usr/bin/echo "$argv"
$ fish ./fecho a b c
a b c
$ fish ./fechoquoted a b c
a b c

Yet these two are actually rather different under the strace-scope:

$ strace -qq -f -e trace=execve fish ./fecho a b c
execve("/usr/bin/fish", ["fish", "./fecho", "a", "b", "c"], [/* 22 vars */]) = 0
[pid 15532] execve("/usr/bin/echo", ["/usr/bin/echo", "a", "b", "c"], [/* 21 vars */]) = 0
a b c
$ strace -qq -f -e trace=execve fish ./fechoquoted a b c
execve("/usr/bin/fish", ["fish", "./fechoquoted", "a", "b", "c"], [/* 22 vars */]) = 0
[pid 15542] execve("/usr/bin/echo", ["/usr/bin/echo", "a b c"], [/* 21 vars */]) = 0
a b c

The first passes a list of arguments to echo ("a", "b", "c"), and the second a single list item ("a b c"). So in your getopt case, when quoted as "$argv" the arguments are passed to getopt(1) as a single string, not as a list of individual elements, and getopt(1) fails as it wants a list of items, not a single string.

A strace on bash should show what "$@" does; one hypothesis would be that it splits the elements out to a list instead of smushing them together as a single item.

  • yes, just as you said, bash splits the “$@" to a list. I just searched the manpage of bash and found that at around 27% . – Charles Oct 12 '17 at 23:59

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.