views:

87

answers:

5
+1  Q: 

Using $@ properly

I am trying to write a tiny script that accepts any number of command line arguments that prints out the rwx permissions for a file (not directory)

What I have is

file=$@    
if [ -f $file ] ; then    
ls -l $file    
fi

This accepts only one command line argument however. Thanks for any help.

A: 

the bash variable for all arguments passed to a script is "$*". Try:

for file in $*; do
  if [ -f $file ] ; then
    ls -l $file
  fi
done

(not tested)

David Dombrowsky
This doesn't work with arguments that contain spaces. Always use "$@" instead, and quote all your variables.
Philipp
+1  A: 

You could usefully loop over any files specified like this:

for file in "$@"; do
  ls -l "$file"
done

If you want to double-check that the name specified is not a directory, you could do this:

for file in "$@"; do
  if [ ! -d "$file" ]; then
    ls -l "$file"
  fi
done
Tim
`$@` and `$file` should be quoted to protect spaces and other special characters.
Jefromi
Quite right - I was being too lazy in my thinking. Many thanks. Now Fixed. :-)
Tim
+3  A: 

How about this one:

for file
do
    test -f "$file" && ls -l "$file"
done

The for loop by default will work on $@, so you don't have to mention it. Note that you will need to quote "$file" in case if the file name has embedded space. For example, if you save your script to 'myll.sh':

$ myll.sh "My Report.txt" file1 file2

Then "My Report.txt" will be passed in as a whole token instead of 2 separate tokens: "My" and "Report.txt"

Hai Vu
I think it's generally best to include the `$@` anyway for clarity.
Jefromi
It's a matter of taste, but I agree with you. I have been including $@ for a long time, but just stopped doing that recently.
Hai Vu
+2  A: 

The variable you want is indeed $@ - this contains all command-line arguments as separate words, each passed on intact (no expansion). ($* treats all of them as a single word - good luck sorting it out if you have spaces in filenames).

You can loop, if you like. This is easily expanded to more complex actions than ls.

for file in "$@"; do
    if [ -f "$file" ]; then
        ls -l "$file"
    fi
done

Note: you should quote $@ to protect any special characters inside! You should also quote $file for the same reason - especially inside the test. If there is an empty string in $@, file will also be empty, and without quotes, -f will attempt to act on the ']'. Errors ensue.

Also, if all you need to do is ls (skipping your if) you can just do this:

ls -l "$@"
Jefromi
+1  A: 

Here is a demonstration of the some of the differences between $* and $@, with and without quotes:

#/bin/bash
for i in $*; do
    echo "\$*: ..${i}.."
done; echo
for i in "$*"; do
    echo "\"\$*\": ..${i}.."
done; echo
for i in $@; do
    echo "\$@: ..${i}.."
done; echo
for i in "$@"; do
    echo "\"\$@\": ..${i}.."
done; echo

Running it:

user@host$ ./paramtest abc "space here"
$*: ..abc..
$*: ..space..
$*: ..here..

"$*": ..abc space here..

$@: ..abc..
$@: ..space..
$@: ..here..

"$@": ..abc..
"$@": ..space here..
Dennis Williamson