34

I am creating a function (below) with which you can provide an argument, a directory. I test if the $argv is a directory with -d option, but that doesn’t seem to work, it always return true even if no arguments are supplied. I also tried test -n $argv -a -d $argv to test is $argv is empty sting, but that returns test: Missing argument at index 1 error. How should I test if any argument is provided with the function or not? Why is test -d $argv not working, from my understanding it should be false when no argument is provided, because empty string is not a directory.

function fcd
     if test -d $argv
         open $argv
     else
         open $PWD
     end
 end

Thanks for the help.

user14492
  • 2,116
  • 3
  • 25
  • 44

5 Answers5

46

count is the right way to do this. For the common case of checking whether there are any arguments, you can use its exit status:

function fcd
    if count $argv > /dev/null
        open $argv
    else
        open $PWD
    end
end

To answer your second question, test -d $argv returns true if $argv is empty, because POSIX requires that when test is passed one argument, it must "Exit true (0) if $1 is not null; otherwise, exit false". So when $argv is empty, test -d $argv means test -d which must exit true because -d is not empty! Argh!

edit Added a missing end, thanks to Ismail for noticing

ridiculous_fish
  • 17,273
  • 1
  • 54
  • 61
  • 2
    The above code is missing an `end`, I cannot edit since it is less than the minimum 6 characters - Please update above answer... – Ismail Moghul Apr 21 '15 at 22:42
  • 3
    I was confused with the ` > /dev/null` so I found an alternative which is more verbose, but is more readable to me: `if test (count $a) -gt 0; ...; end` – FMCorz Nov 04 '17 at 15:19
33

In fish 2.1+ at least, you can name your arguments, which then allows for arguably more semantic code:

function fcd --argument-names 'filename'
    if test -n "$filename"
        open $filename
    else
        open $PWD
    end
end
steevee
  • 2,238
  • 1
  • 21
  • 16
  • 2
    `-a` is the shorthand flag as mentioned in the documentation: https://fishshell.com/docs/current/commands.html#function – Sgnl Apr 17 '18 at 22:28
  • 1
    The quotes around $filename in the call to `test` are important. Otherwise: `test: Missing argument at index 2` if the argument is not provided. – Raman Jan 08 '19 at 15:48
12
if not set -q argv[1]
    echo 'none'
else
    echo 'yes'
end

From the man set page:

   set ( -q | --query ) [SCOPE_OPTIONS] VARIABLE_NAMES...

-q or --query test if the specified variable names are defined. Does not output anything, but the builtins exit status is the number of variables specified that were not defined.

Y. E.
  • 687
  • 1
  • 10
  • 29
Duke
  • 7,070
  • 3
  • 38
  • 28
  • 3
    Great, i was doing `set -q $argv[1]` for the if statement which turns out to be wrong. We do not need to **expand** the `argv`. Thanks! – LeOn - Han Li Apr 16 '19 at 03:22
6

$argv is a list, so you want to look at the first element, if there are elements in that list:

if begin; test (count $argv) -gt 0; and test -d $argv[1]; end
    open $argv[1] 
else
    open $PWD
end
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
4

Maybe is non related, but i would like to add another perspective for the question.

I want to broaden the insight to a wider scope the scope of testing the shell code with the libraries developed in the fisherman group.

With mock you can check if the open command is called safely without side effects.

Example.:

function fcd
    if count $argv > /dev/null
        open $argv
    else
        open $PWD
    end
end
mock open 0 "echo \$args"
fcd "cool" # echoes cool
mock open 0 "echo \$args"
fcd # echoes $PWD

Is a recent library, but it could help to test things that might be dangerous, like for example rm

mock rm 0 "echo nothing would happen on \$args"
rm "some file" # simply echoes the message with a list of the files that would been affected

I hope that it gives a more out of the box point of view

P.S.: Sorry for the blatant publicity, but i think that is a cool idea that would be nice to be adopted by shell scripters, to test and add sustainability to shell scripts is nuts. :P

EDIT: i recently noticed a bug about the sample i posted. Please do not use rm *, because the asterisk is not treated as a param, instead fish shell expands asterisk to the list of files found and the command only mocks the first call, this means that the first file would be ignored by the mock, but all the subsequent files will get erased so please be careful if trying the sample and use a single file for the example not the wildcard.

markcial
  • 9,041
  • 4
  • 31
  • 41