A script receives its arguments already split, one word per argument. args.sh will show exactly what it got.
Everything after the script's name on the command line bash splits into arguments — at spaces outside quotes — and hands to the script. Inside the script the arguments sit in positional parameters: $1 is the first, $2 the second, and so on, and $# is how many there are. args.sh prints $# and every argument in angle brackets so you can see where each one begins and ends.
See what the script receives
In ~/args-lab, run args.sh three times: with the name in quotes, with the same name unquoted, and with no arguments at all.
First, predict
How many arguments will args.sh count in the first command, where "my file.txt" is quoted?
Now start the exercise, run the command and compare your prediction with the output.
The quotes in the call did their job: args.sh got my file.txt as one argument. From there on, the script is in charge. args.sh walks through its arguments like this: for a in "$@". A for loop runs the commands between do and done for every word in the list after in, and the variable a takes each word in turn. "$@" in double quotes is a special form: all of the script's arguments, each as a separate word, exactly as they were passed.
report.sh writes for f in $@ — without quotes. Then bash expands the arguments and splits them at spaces all over again, like the unquoted $dir in the lesson on scripts. wc -l $f inside the loop repeats the same mistake. For a call with "my file.txt" notes.txt, the difference looks like this:
| In the script | Words the loop gets |
|---|---|
"$@" |
my file.txt, notes.txt |
$@ or $* |
my, file.txt, notes.txt |
"$*" |
my file.txt notes.txt — one word |
Expand one argument as "$1", all of them as "$@", and quote the loop variable too: wc -l "$f".