I want to run this cmd line script
$ script.sh lib/* ../test_git_thing
I want it to process all the files in the /lib folder.
FILES=$1
for f in $FILES
do
echo "Processing $f file..."
done
Currently it only prints the first file. If I use $@, it gives me all the files, but also the last param which I don't want. Any thoughts?
In bash and ksh you can iterate through all arguments except the last like this:
for f in "${@:1:$#-1}"; do
echo "$f"
done
In zsh, you can do something similar:
for f in $@[1,${#}-1]; do
echo "$f"
done
$#
is the number of arguments and ${@:start:length}
is substring/subsequence notation in bash and ksh, while $@[start,end]
is subsequence in zsh. In all cases, the subscript expressions are evaluated as arithmetic expressions, which is why $#-1
works. (In zsh, you need ${#}-1
because $#-
is interpreted as "the length of $-
".)
In all three shells, you can use the ${x:start:length}
syntax with a scalar variable, to extract a substring; in bash and ksh, you can use ${a[@]:start:length}
with an array to extract a subsequence of values.
As correctly noted, lib/*
on the command line is being expanded into all files in lib
. To prevent expansion, you have 2 options. (1) quote your input:
$ script.sh 'lib/*' ../test_git_thing
Or (2), turn file globbing off. However, the option set -f
will disable pathname expansion within the shell, but it will disable all pathname expansion (setting it within the script doesn't help as expansion is done by the shell before passing arguments to your script). In your case, it is probably better to quote the input or pass the first arguments as a directory name, and add the expansion in the script:
DIR=$1
for f in "$DIR"/*
The argument list is being expanded at the command line when you invoke "script.sh lib/*" your script is being called with all the files in lib/ as args. Since you only reference $1 in your script, it's only printing the first file. You need to escape the wildcard on the command line so it's passed to your script to perform the globbing.
This answers the question as given, without using non-POSIX features, and without workarounds such as disabling globbing.
You can find the last argument using a loop, and then exclude that when processing the list of files. In this example, $d
is the directory name, while $f
has the same meaning as in the original answer:
#!/bin/sh
if [ $# != 0 ]
then
for d in "$@"; do :; done
if [ -d "$d" ]
then
for f in "$@"
do
if [ "x$f" != "x$d" ]
then
echo "Processing $f file..."
fi
done
fi
fi
Additionally, it would be a good idea to also test if "$f"
is a file, since it is common for shells to pass the wildcard character through the argument list if no match is found.