4
#!/bin/bash # number of expected arguments EXPECTED_ARGS=1 # exit value if the number of arguments is wrong E_BADARGS=1 if [ $# -ne $EXPECTED_ARGS ] then echo "Usage: `basename $0` {arg}" exit $E_BADARGS fi if [ ! -e $1 ] then echo "file $1 does not exist" exit $E_BADARGS fi for myfile in $1/* do if [ -d "$myfile" ] then echo "$myfile (DIR)" elif [ -f "$myfile" ] then echo "$myfile" fi done 

I'm new to bash and I cannot figure out what ! means in if[ ! -e $1] and what $1/* means in for myfile in $1/*. So far I've thought about if[! -e $1] as if (not equal to first parameter) (do this).... is this correct? But then what is not equal to first parameter? for myfile in $1/*, I've no idea what this means. Any help?

5
  • [ is not bash syntax per se -- it's an alias for the test command. Granted, bash has its own built-in version of test, but it's still run with the same semantics it would have if it were /usr/bin/test or /usr/bin/[ being invoked, and it's worth thinking of as a regular command. Commented Dec 3, 2015 at 22:10
  • For completeness: the documentation for the bash builtin can be found by typing help test (whereas man test will give you the documentation for the non-builtin /usr/bin/[). Commented Dec 3, 2015 at 22:11
  • ...that means, for instance, that if[is a completely different thing from if [; the former is going to be looking for a file named, literally, if[ if there's no function or alias sharing that name. Commented Dec 3, 2015 at 22:12
  • 1
    BTW, consider running your code through shellcheck.net for an automated pass against quoting errors and the like. Commented Dec 3, 2015 at 22:14
  • (Also, while I'm kibitzing -- all-caps variable names are actually reserved for use by the shell -- as with PS1, PWD, etc -- and the system -- as with PATH, LD_PRELOAD, etc, so your own variables should use lowercase names so as not to conflict; see fourth paragraph of the specification at pubs.opengroup.org/onlinepubs/009695399/basedefs/…, keeping in mind that environment variables and shell variables share a namespace). Commented Dec 3, 2015 at 22:16

2 Answers 2

6
if [ $# -ne $EXPECTED_ARGS ] 

The -ne operator tests if two numbers are "not equal." $# is a variable representing the number of arguments passed. This line checks if it's different from the variable $EXPECTED_ARGS.

if [ ! -e $1 ] 

The -e operator tests if a file exists on disk. $1 is a positional parameter, it's the first argument passed to the script. So this line is checking whether or not the file exists. See this page for more info on using if in bash.

for myfile in $1/* 

Expects that $1 is a directory, and goes through each file in the directory using globbing. Each file is loaded into the variable $myfile.

There are a few issues with the script as is, as mentioned in the comments. I'd do things a little differently; see this as a first quick run at improving it: http://pastebin.com/4fbsdrDw

Sign up to request clarification or add additional context in comments.

6 Comments

I'd suggest demonstrating good quoting practice here. In addition to behaving correctly with filenames with spaces &c, if [ "$#" -ne "$EXPECTED_ARGS" ] will also syntax-highlight better. :)
...also, in not all of these cases is good/correct quoting obvious; f'rinstance, for myfile in "$1"/* isn't something that a newbie might guess, but it's critical to understand if your directory name passed as $1 can contain characters in IFS, globs, or other interesting contents.
Agreed, there are plenty of problems with the script itself. I didn't want to get into critiquing it, but there's plenty of room for improvement.
if [ ! -e $1] Here, what is the individual definition of -e (equal?). Can you explain how you would read like is it " if (not equal to 1st argument)" ? What I can't understand is how is it evaluating the relation?
@Laura, it's not "equal", but "exists" -- telling whether a file exists on disk. The output of help test will tell you this.
|
5

The ! means NOT, so if [ ! -e $1 ] reads "if the first argument (of type file, directory or link) does not exist, do …" (the -e is a file test operator of meaning "exists")

for myfile in $1/* implies that your first argument is a directory and loops through all files in this directory.

5 Comments

Exactly what I was looking for !!
Well -- what it actually does is more complex than that, due to the lack of quoting. [ ! -e $1 ] string-splits and glob-expands the contents of $1 before passing it to [, meaning that the actual test command it runs could be potentially more interesting. Similarly, for myfile in $1/* will only expand to contents of the directory named in $1 if that directory name has no spaces; otherwise, only the last segment of $1 (as split by characters in IFS) will be treated as a directory, with other segments being glob-expanded and iterated over as individual entries prior.
...if you wanted the simple descriptions to be correct, it would need to be [ ! -e "$1" ] and for myfile in "$1"/* instead. :)
@CharlesDuffy Hope I can understand shell programming as well as you do. I'm just starting out so I was looking for really trimmed down answers so I won't be overwhelmed.
@Laura, ...indeed -- explaining how buggy code behaves across the full range of possible inputs is a lot of work for the person explaining, and a lot for the reader to understand; an answer in the form of "to be correct, write your code like THIS; when written that way, it works like so" is probably the best way to balance the competing goals of readability, completeness, and accuracy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.