How to set $argv to an empty list? - fish

I want to manipulate $argv and pass it on to another fish function. It is simple enough to trim it as follows:
switch $value
case a
set argv $argv[2..-1]
case b
set argv $argv[3..-1]
end
call_function $argv
But how can I set it to an empty list?
I don't want to set --erase argv because I do actually want to access the variable.
I tried set argv $argv[] but then count $argv returns 1. (And I may rely on count later -- unless there is a better way to test for emptiness?)

set argv
You don't actually need $argv to get to an empty list at all - the empty list is just the absence of arguments.

Related

why `echo HTTPS_PROXY=$HTTPS_PROXY` print an empty line when variable not set?

shouldn't it be printing out HTTPS_PROXY= instead? (when $HTTPS_PROXY is not set)
I know I can work around using
echo HTTPS_PROXY=(echo $HTTPS_PROXY) or echo HTTPS_PROXY="$HTTPS_PROXY" , but I want to know why I need a work around in this case.
In fish, all variables are lists. When you concatenate a string and a variable, what it does is combine every list element with the string.
So
set bar 1 2 3
echo foo$bar
prints "foo1 foo2 foo3".
Now, when you have an undefined variable (or an empty one, set like set bar without values), this combines nothing with the string, which ends up eliminating it.
You can think of it like any variable expansion being a brace expansion - echo foo{1,2,3} is the same as echo foo$bar with bar set like above.
In many cases, that is exactly what you want. Imagine $bar being a list of directories. To go over all files in them you could use
for file in $bar/*
and if $bar was empty (there was no directory), the entire loop would be skipped instead of e.g. showing all files in "/".
The obvious solution is to quote the variable if you want to supress this. Quoting turns the variable into always exactly one argument, even if it's empty or has multiple elements, so
echo foo"$bar"
prints "foo1 2 3" (as one argument).
This is documented at https://fishshell.com/docs/current/#combining-lists-cartesian-product.

/bin/sh Adding an element to the arguments array from within the script

Ultimate Goal
Essentially I have a while loop going through each command-line argument passed to the script, but want to set default behaviour if no arguments are passed.
Current solution idea
My plan for this is to test if there are any arguments prior to this while loop and if there aren't any, simply add the default arguments (flags in this case) to the argument array and let the script execute from there as normal.
Problem I'm having
Where I'm tripping up a little here is figuring out the syntax to add these default arguments to the arguments array itself (I can get values from it no problem).
This is what I have so far:
if test $# -eq 0
then
# ADD --default TO ARGUMENTS ARRAY HERE
fi
while test $# -gt 0
do
case "$1" in
--opt1) DO STUFF
;;
--opt2) DO MORE / OTHER STUFF
;;
--default) DO DEFAULT STUFF
;;
esac
shift
done
Following and altering examples using user-defined arrays I have tried:
if test $# -eq 0
then
$+=('--default')
fi
But just get the syntax error
syntax error near unexpected token `'--default''
sh does not have arrays other than the built-in command-line argument list, and thus no += operator for arrays.
You also seem to be missing a character; what you wrote evaluates to assigning (default) to the value of the variable $+ but this variable is not valid (though because there are no arrays, the parentheses cause an error already).
The shell allows you to use set to manipulate the built-in argument list.
if test $# -eq 0
then
set -- --default
fi
The condition requires the array to be empty, so we simply populate it with the single element you provided; in the general case, you could add stuff to the end with
set -- "$#" --default
or correspondingly to the beginning by putting something before "$#" (which of course contains the previous value of the argument list).

Valuable changes depends on the existence of argument file

I wrote configuration of fish shell like this:
# One or more argument(s) will be given
function run
set -l src $argv[1]
set -l var
switch "$src"
case *
set var "$src"
end
echo $var
end
I expected the first argument is printed in any case if one or more argument is given. However, $var becomes $argv[1] if file with the same name as $argv[1] exists, otherwise it becomes empty string.
Could someone tell me why?
The * in case * is interpreted as a glob. Quote it if you do not want that.
Faho already answered your question but I wanted to point out that your approach is more complicated than necessary. If you just want to print the first argument if one or more were provided do this:
set -q argv[1]
and echo $argv[1]
The first statement checks if argv has at least one value. The second echoes it if the previous statement returned success (i.e., $status set to zero).

how to retrieve array variable elements passed as a command line argument to Expect script

I have a Perl script that invokes an Expect script with command line arguments passed to it.
I wanted to know if we pass an array variable as argument, how do we retrieve the array variable in expect script?
For a simple variable passed as command line argument : set var [lindex $argv 0] is used.
I need to know how to access array variable in similar fashion and use its elements in the expect script. a sample code would be of great help!
looking forward for responses,
Thank you.
The expect global variable $argv is a list (i.e. a perl array). If you want to capture it in a different variable:
set myvar $argv
Or use it directly
foreach item $argv {
do something with $item
}

How does this Perl one liner to check if a directory is empty work?

I got this strange line of code today, it tells me 'empty' or 'not empty' depending on whether the CWD has any items (other than . and ..) in it.
I want to know how it works because it makes no sense to me.
perl -le 'print+(q=not =)[2==(()=<.* *>)].empty'
The bit I am interested in is <.* *>. I don't understand how it gets the names of all the files in the directory.
It's a golfed one-liner. The -e flag means to execute the rest of the command line as the program. The -l enables automatic line-end processing.
The <.* *> portion is a glob containing two patterns to expand: .* and *.
This portion
(q=not =)
is a list containing a single value -- the string "not". The q=...= is an alternate string delimiter, apparently used because the single-quote is being used to quote the one-liner.
The [...] portion is the subscript into that list. The value of the subscript will be either 0 (the value "not ") or 1 (nothing, which prints as the empty string) depending on the result of this comparison:
2 == (()=<.* *>)
There's a lot happening here. The comparison tests whether or not the glob returned a list of exactly two items (assumed to be . and ..) but how it does that is tricky. The inner parentheses denote an empty list. Assigning to this list puts the glob in list context so that it returns all the files in the directory. (In scalar context it would behave like an iterator and return only one at a time.) The assignment itself is evaluated in scalar context (being on the right hand side of the comparison) and therefore returns the number of elements assigned.
The leading + is to prevent Perl from parsing the list as arguments to print. The trailing .empty concatenates the string "empty" to whatever came out of the list (i.e. either "not " or the empty string).
<.* *>
is a glob consisting of two patterns: .* are all file names that start with . and * corresponds to all files (this is different than the usual DOS/Windows conventions).
(()=<.* *>)
evaluates the glob in list context, returning all the file names that match.
Then, the comparison with 2 puts it into scalar context so 2 is compared to the number of files returned. If that number is 2, then the only directory entries are . and .., period. ;-)
<.* *> means (glob(".*"), glob("*")). glob expands file patterns the same way the shell does.
I find that the B::Deparse module helps quite a bit in deciphering some stuff that throws off most programmers' eyes, such as the q=...= construct:
$ perl -MO=Deparse,-p,-q,-sC 2>/dev/null << EOF
> print+(q=not =)[2==(()=<.* *>)].empty
> EOF
use File::Glob ();
print((('not ')[(2 == (() = glob('.* *')))] . 'empty'));
Of course, this doesn't instantly produce "readable" code, but it surely converts some of the stumbling blocks.
The documentation for that feature is here. (Scroll near the end of the section)