2

I'd like to change function arguments before passing them to a next function.

firstfunction() {
   # change "-f" to "--format" in arguments
   secondfunction "$@"
}

I tried to convert to an array, change the array and convert back to arguments. But it looks so complicated. Is it possible to make it simpler?

UPDATE: to be more specific...

firstfunction data.txt -f "\d+"

should call

secondfunction data.txt --format "\d+"
Dmitry Petrov
  • 1,490
  • 1
  • 19
  • 34
  • @anubhava For simplicity let's assume I need to change short options to long options: -f --> --format, -x --> --execute. – Dmitry Petrov Jul 27 '16 at 08:14

2 Answers2

1

You can use getopts to reliable parse and process optional arguments like this:

firstfunction() {
   OPTIND=1
   local arr=()
   while getopts "f:x:" opt; do
      case $opt in
         f) arr+=("--format $OPTARG");;
         x) arr+=("--execute $OPTARG");;
      esac
   done
   echo "${arr[@]}"; # or call second function here
}

firstfunction -fabc -x foobar
--format abc --execute foobar

firstfunction -fabc -xfoobar
--format abc --execute foobar

firstfunction -f abc -xfoobar
--format abc --execute foobar
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Yes that's correct @bgoldst. For foolproof way to handle this I would suggest a loop using `while getopts` – anubhava Jul 27 '16 at 08:39
1

This is a surprisingly tough problem. Bash is simply not very good at working with (slightly) complex data structures like arrays.

I think the only conceivable robust solution will require a loop. This is the easiest way I can think of:

function firstfunction {
    local -A map=(['-f']='--format');
    local -a args=();
    local arg;
    for arg in "$@"; do
        if [[ -v map[$arg] ]]; then
            args+=("${map[$arg]}");
        else
            args+=("$arg");
        fi;
    done;
    echo ${args[@]+"${args[@]}"}; ## replace echo with secondfunction to run
};
firstfunction;
##
firstfunction a b;
## a b
firstfunction x -f -fff -f-f -fxy x-f \ -f -f\  -f;
## x --format -fff -f-f -fxy x-f  -f -f  --format

Using ${args[@]+"${args[@]}"} instead of just "${args[@]}" for the final expansion works around the ill-advised design decision the bash developers made to reject an empty array as an "unbound variable" if you have the nounset set option (set -u) enabled. See Bash empty array expansion with `set -u`.


Alternative:

function firstfunction {
    local -A map=(['-f']='--format');
    local -a args=("$@");
    local -i i;
    for ((i = 0; i < ${#args[@]}; ++i)); do
        if [[ -v map[${args[i]}] ]]; then
            args[i]="${map[${args[i]}]}";
        fi;
    done;
    echo ${args[@]+"${args[@]}"}; ## replace echo with secondfunction to run
};
Community
  • 1
  • 1
bgoldst
  • 34,190
  • 6
  • 38
  • 64
  • 1
    Thank you @bgoldst! I was playing with @anubhava's solution and realized than I actually need more general. – Dmitry Petrov Jul 27 '16 at 09:15
  • Not for nitpicking but `firstfunction -fabc` is a valid option argument to send `abc` with `-f` option. To be generic it should become `--format abc` or `--format=abc` – anubhava Jul 27 '16 at 09:30
  • You can use a much simpler loop here: `for arg in "$@"; do` – chepner Jul 27 '16 at 12:21