I am writing a script to act on files given to the script using the $1 variable. What is the best way to loop through these files. At the moment I have hard coded the script using something like

for file in *.pdf
   act on $file

However I would rather do this using the $1 variable Thanks Barry

  • you can do for file in $1 inside the code... to ensure, shell doesn't expand *, put that argument inside single quotes
    – user93868
    Aug 9 '15 at 12:10

$1 is simply the first parameter passed to your shell script. So if you have a script myscript and I call it like this:

myscript somefile

Then $1 will be somefile. If I call it like this:

myscript *

And there happen to be files in my current directory named:

foo bar baz

Then $1 will be foo. In either case, $1 only has a single value and there is nothing to loop over.

There are a couple of ways to loop over the arguments. The most common is:

for file in "$@"; do
  echo doing something with "$file"

Note that $@ is not the same as "$@"; the latter is a special construct that will correctly deal with filenames that contain spaces. You could also do something like:

while [ -n "$1" ]; do
  echo doing something with "$1"

Here, the shift command drops the first parameter, and $2 becomes $1 (and so on). This will loop as long as $1 is non-empty. You could also use $# for this (a variable which contains the number of command line arguments):

while [ $# -gt 0 ]; do
    echo arg: "$1"
  • Note that the canonical (and most portable) way to write for file in "$@" is for file do. Aug 9 '15 at 17:32
  • I have honestly never seen that syntax used in practice. Are you aware of any Bourne-ish shells that don't work properly given for file in "$@"?
    – larsks
    Aug 9 '15 at 17:40
  • It doesn't work properly in some versions of the Bourne shell when $# is 0. It doesn't work with the Bourne shell or pdksh when $IFS is empty (doesn't contain space for the Bourne shell). Aug 9 '15 at 17:46

If you do

script *.pdf

*.pdf is already expanded on the command line, and $1 will only refer to the first file

for file in $1

will therefore not do what you need. You could use

for file in "$@"


See the section Special Parameters in the man bash

  • Arrgghhh!  Don't say for file in $@; that will fail if arguments contain spaces or various other special characters.  Use for file in "$@", or just plain for file for short.
    – Scott
    Aug 9 '15 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.