1

I'm studying the RBENV codebase, and I see that on line 116 of the rbenv-init file, a function is created which contains a switch statement. My hypothesis is that we check whether the value of the command variable is one of the members of the array of values in the commands (plural) variable. If it is, we execute branch 1 of the switch statement. Otherwise, we execute branch 2.

I wanted to write a simple script to test my hypothesis, so I wrote the following:

#!/usr/bin/env fish

set command "foo"

switch $command
case ${argv[*]} 
  echo "command in args"
case "*" 
  echo "command not found"
end

However, when I run this script, I get the following error:

$ ./foo bar baz

./foo (line 6): ${ is not a valid variable in fish.
case ${argv[*]} 
      ^
warning: Error while reading file ./foo

I was expecting argv to evaluate to an array containing bar and baz, since those are the two args I provide to the script. My syntax matches that of line 117 in the source code (i.e. case ${commands[*]}).

The shell I'm executing the script in is zsh v5.8.1, however my shebang specifically references the 'fish' shell so I'd think my shell wouldn't matter. I have fish v3.5.1 installed, fwiw.

1
  • Pay attention to backslashes in there. Notice how some of the $ are prefixed with one, and then make it to the fish code and some aren't which means they're expanded (by bash) before making it to the fish code. Commented Sep 10, 2022 at 21:19

1 Answer 1

2

The bash code in there is:

commands=(`rbenv-commands --sh`)

That's split+glob applied to the output of rbenv-commands --sh and the resulting words assigned to elements of the $commands bash array

case "$shell" in
fish )
  cat <<EOS
function rbenv
  set command \$argv[1]
  set -e argv[1]
  switch "\$command"
  case ${commands[*]}
    rbenv "sh-\$command" \$argv|source
  case '*'
    command rbenv "\$command" \$argv
  end
end
EOS
  ;;

cat << EOS... outputs some fish code, but as that EOS is not quoted, expansions (by bash) are still performed. Except when there's a backslash in front, $param is expanded by bash. You'll notice that most $s are prefixed with a \, but not ${commands[*]} (which anyway is Korn shell syntax, not fish syntax). That's expanded by bash to the elements of the $commands array joined with the first character of $IFS (space by default).

So the fish code that that cat command produces is rather like:

function rbenv
  set command $argv[1]
  set -e argv[1]
  switch "$command"
  case elements of the commands bash array
    rbenv "sh-$command" $argv|source
  case '*'
    command rbenv "$command" $argv
  end
end

To check whether a string is among a list, you'd use fish's contains buitin:

set list foo bar baz
set string foo
if contains -- $string $list
  echo $string is in the list
end

(like zsh's if (( $list[(Ie)$string] )) or for non-empty lists if [[ $string = (${(~j[|])list}) ]])

You can also do:

switch $string
  case $list
    echo $string matches at least one of the patterns in the list
end

(which is not the same unless none of the elements of the list contain * or ? characters)

(that's more like zsh's [[ $string = (${(j[|])~list}) ]] (for non-empty lists)).

There's also:

if string match -q -- $pattern $list > /dev/null
  echo at least one of the elements of the list matches $pattern
end

(like zsh's if (( $list[(I)$pattern] ))).

4
  • 1
    In retrospect, I think my confusion was in mentally juggling the bash parameter expansion with fish syntax. This made it hard for me to tell which was which in this script. Thanks for the help! Commented Sep 10, 2022 at 21:57
  • Also, I just read who you are. Thank you for your service with the "Shell Shock" exploit! Commented Sep 11, 2022 at 2:34
  • Using a case with multiple words will also check if the switch argument is in that list. case foo bar baz is also true for foo, bar and baz. So this does in fact check for known commands.
    – faho
    Commented Sep 11, 2022 at 6:55
  • @faho, d'oh you're right, the IFS='|' is for another section of the code there, my bad. I'll fix that. Commented Sep 11, 2022 at 6:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .