Reputation: 2766
What is the most elegant way to essentially 'map' a list of bash arguments through some transformation (such as concatenating each string) before forwarding the list onto some other command? Using xargs
comes to mind but I can't seem to conceptualize how to do this.
function do_something {
# hypothetically
for arg in "$@"; do
arg="$arg.txt"
done
command "$@"
}
do_something file1 file2 file3
Such that the result would be to call command file1.txt file2.txt file3.txt
.
Upvotes: 4
Views: 4278
Reputation: 5385
You can use the following definition for a map
that is similar to the one found in many functional programming languages (eg python, haskell):
function map
{
local f="$1"
shift # consume first argument
for arg
do
"$f" "$arg" # assuming `f` prints a single line per call
done
}
Here's how you would use it in your example. Here some_cmd
may be a function defined locally:
function do_something
{
local IFS=$'\n' # only split on newlines when word splitting
result=($(map suffix "$@")) # split into lines and store into array
some_cmd "${result[@]}" # call some_cmd with mapped arguments.
}
function suffix
{
echo "$@".txt
}
do_something file1 file2 file3
Here's another variation of writing do_something
. Here some_cmd
must exist in $PATH
:
function do_something
{
map suffix "$@" | xargs some_cmd # call some_cmd with mapped arguments.
}
The main downside is that to use the result in another function, you need to mess around with IFS
to split on the newlines, or pipe into xargs; and if your map outputs contain newlines then either method fails completely.
Upvotes: 3
Reputation: 4704
In order to "forward" arguments to other commands, there are several ways. Try this script:
printargs() {
echo "Args for $1:"
shift
for a in "$@"; do
echo " arg: -$a-"
done
}
printargs dolstar $*
printargs dolstarquot "$*"
printargs dolat $@
printargs dolatquot "$@"
and invoke it with test aguments:
./sc.sh 1 2 3
Args for dolstar:
arg: -1-
arg: -2-
arg: -3-
Args for dolstarquot:
arg: -1 2 3-
Args for dolat:
arg: -1-
arg: -2-
arg: -3-
Args for dolatquot:
arg: -1-
arg: -2-
arg: -3-
Things go a little differently if an argument contains spaces:
./sc.sh 1 "2 3"
Args for dolstar:
arg: -1-
arg: -2-
arg: -3-
Args for dolstarquot:
arg: -1 2 3-
Args for dolat:
arg: -1-
arg: -2-
arg: -3-
Args for dolatquot:
arg: -1-
arg: -2 3-
The dolatquot "$@" is the only version that correctly forwards arguments. Otherwise, as seen in another answer, you can manipulate arguments and construct a new list via arrays or a single string.
Upvotes: 0
Reputation: 12140
What you've done is mostly correct, except that you'll need to use an array to store the new arguments:
function do_something {
array=()
for arg in "$@"; do
array+=("$arg.txt")
done
command "${array[@]}"
}
do_something file1 file2 file3
Upvotes: 5