Reputation: 249303
Suppose you want to make a bash
script which supports no options but acts like cp
, because the cp
supplied by your system does not accept multiple sources.
The usage for the system's (hypothetical and broken) cp
is:
cp source target # target may be a directory
The usage for the script will be:
cp.sh source... target # target must be a directory
Here's a starting point for the script:
#!/bin/bash
tgt="$1"
shift
for src in "$@"; do
echo cp $src $tgt
done
When run with the arguments "a b c d
" (note that d
is the target), it outputs:
cp b a
cp c a
cp d a
The goal is to fix the script to output this instead, while keeping the code simple:
cp a d
cp b d
cp c d
Upvotes: 0
Views: 2021
Reputation: 15582
You don't need any bash-specific features:
eval last=\${$#} while [ $# -gt 1 ]; do echo "cp $1 $last" shift done
Upvotes: 0
Reputation: 124563
You can do this directly without writing a script using xargs:
echo source1 source2 | tr "\n" "\0" | tr " " "\0" |
xargs --verbose -0 -I{} cp {} dest
Upvotes: 0
Reputation: 146093
#!/bin/bash
t=(x "$@")
i=1; while [ $i -lt $# ]; do
echo cp ${t[$i]} ${t[$#]}
i=$(($i + 1))
done
Upvotes: 0
Reputation: 125858
You can use array slicing to leave off the last of the arguments:
tgt="${!#}"
for src in "${@:1:$#-1}"; do
cp "$src" "$tgt"
done
Upvotes: 2
Reputation: 46965
Use this to extract the last parameter:
eval tgt=\$$#
then just process the same way you are and when you hit $# just exit the loop
Here's the whjole script:
eval tgt=\$$#
for src in $@
do
if [ "$src" == "$tgt" ];
then
exit
fi
echo cp $src $tgt
done
Pretty simple if you ask me!
Upvotes: -1
Reputation: 7640
/test.bash source1 source2 target1
#!/bin/bash
target=${!#}
if [ ! -d $target ] ; then
echo "$target must be a directory " >&2
exit 1;
fi
args=("$@")
unset args[${#args[@]}-1]
for src in "${args[@]}"; do
echo cp $src $target
done
will output
cp source1 target1
cp source2 target1
Upvotes: 5
Reputation: 40733
Why? The cp command already does that. Do a man cp and you will see.
If you still insist, here are two ways to get the last argument. Method 1: place command line in an array and extract the last element:
arg=("$@")
last_arg=${arg[(($# - 1))]}
The first line puts the command line arguments into the array arg. If your command line contains a b c d then arg[0] == 'a', ... argv[3] == 'd'.
The second line extract the last argument. The (($# - 1)) takes the number of arguments (4 in this case), subtract 1 from it (to get 3). That expression then becomes:
last_arg=${arg[3]}
which points to the last argument.
The second method is not very portable, it makes use of the BASH_ARGV variable, which is $@ but in reverse order. If your command line is a b c d then ${BASH_ARGV[0]} == 'd', ... ${BASH_ARGV[3]} == 'a':
last_arg=${BASH_ARGV[0]}
I hope this helps.
Upvotes: 1
Reputation: 360153
#!/bin/bash
tgt="${@: -1}" # get the last parameter
for src in "$@"; do
if [[ $src != $tgt ]]; then
echo cp "$src" "$tgt"
fi
done
Upvotes: 1