fish shell: filter $argv (lightweight / dynamic argparse)

48 Views Asked by At

I would like to split $argv into two variables. One with opts and one with everything else.

function filter_opts
    set -l filtered_opts
    set -l new_argv

    for opt in $argv
        if string match -r -- '-.*' $opt
            set -a filtered_opts $opt
        else
            set -a new_argv $opt
        end
    end

    set -g argv $new_argv
    echo $filtered_opts
end

function test_filter 
    set -g argv $argv
    echo $argv
    set -l opts (filter_opts $argv)
    echo $opts
    echo $argv

    # prog1 $opts $argv
    # prog2 $argv
end

But the output has the filtered opts duplicated and $argv isn't modified... :-(

$ test_filter Twilight Zone --test -t
Twilight Zone --test -t
--test -t --test -t
Twilight Zone --test -t

Ideally the output would look like this:

$ test_filter Twilight Zone --test -t
Twilight Zone --test -t
--test -t
Twilight Zone
1

There are 1 best solutions below

0
ridiculous_fish On BEST ANSWER

Two improvements:

  1. It's not necessary to loop over $argv, you can just pass multiple arguments to string filter.
  2. A function by default has its own scope, but you can have it modify its caller's scope via --no-scope-shadowing (see function docs)

Putting those ideas together:

function filter_opts --no-scope-shadowing
    set options (string match  -- '-*' $argv)
    set arguments (string match --invert -- '-*' $argv)
end

function test_filter 
    filter_opts $argv
    echo "Options: $options"
    echo "Arguments: $arguments"
end

test_filter Twilight Zone --test -t

this outputs:

Options: --test -t
Arguments: Twilight Zone

also don't miss argparse which is fish's argument parsing command.