Always practice safe patterns and switches.

This post is prompted by one floating around Google+.

Take a UNIX directory containing three files as follows:
bar.txt
foo.txt
-l

If you run ls with no arguments in that directory the output is as expected.
$ ls
bar.txt foo.txt -l

Even ls -l works similarly.
$ ls -l
total 0
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:23 bar.txt
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:23 foo.txt
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:22 -l

However, let’s do ls *
$ ls *
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:23 bar.txt
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:23 foo.txt

Something’s just a little different there! I’m not including the output here, but ls -l * produces the same output.

The reason is how the wildcard actually works, and we can easily see this using echo *
$ echo *
bar.txt foo.txt -l

So, when you type ls * in this directory, you are actually executing this command:
ls bar.txt foo.txt -l

An uncommon order, for sure, but ls happily accepts the -l switch at the end and modifies its behavior. How do you fix it?

The post on G+ suggested prefixing the wildcard with “./” which specifies the current directory. That works great with ls, but there is another solution that works, at least in the Linux world: two hyphens followed by a space.

$ ls -- *
bar.txt foo.txt -l
$ ls -l -- *
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:23 bar.txt
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:23 foo.txt
-rw-r--r-- 1 dcwowner dcwapp 0 Jun 23 09:22 -l

The reason I prefer this solution is that it has applications on other commands. Say I’m searching files in a directory for a line from a log file that happened at 13:32, and the format of the time stamp is yyyy-mm-dd-hh:mm:ss. My command would look like grep -RF "-13:32" * which is neat, simple, and wrong:
$ grep -RF "-13:32" *
grep: invalid option -- ':'
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.

In spite of the quoting (and it behaves the same with single quotes), the pattern “-13:32” is evaluated as an argument to grep, and grep complains about the colon. Run it again with the pattern “-13” and you get no output from the command. Add “– ” before the pattern, though:
$ grep -RF -- "-13:32" *
bat.txt:2014-06-24-13:32 my entry

This entry was posted in Uncategorized. Bookmark the permalink.