I cant make the following check to work

for file in `ls -R .`
    if [-f $file] ......

The fact is that I want this check to match when it finds a file.gz (gzip compressed file) and it is not working.

  • 3
    How is it not working? Please provide a concrete example – roaima Oct 8 '15 at 14:16
  • 3
    any reason not to use find . -type f -name file.gz and work with its output? – Anthon Oct 8 '15 at 14:20

Perhaps if you specified what "it is not working" means, we could help you better.

Anyway, if the output you get is "[-f command not found" then you have to put a space between [ and the inside (/usr/bin/[ is a full featured command):

if [ -f $file ] ...

but please be aware that the file might contain spaces or other strange characters, so as the very minimum I'd suggest putting quotes around "$file":

if [ -f "$file" ] ...

and even better, do not parse ls -R . but use find . instead

  • Note that [ -f "$file" ] will also return true for symlinks to regular files (as opposed to find's -type f for instance). – Stéphane Chazelas Oct 8 '15 at 15:10

You do not want to parse ls output. If you want a list of all .gz files under a folder and do something to each result, the following is a better idea if you're using bash or zsh and your find supports the -iname and -print0 predicate (like on GNU, Solaris 11 or some BSD systems):

while IFS= read -r -d $'\0' file; do
    echo "found a .gz: $file"
done < <(find . -type f -iname "*.gz" -print0)
  • 1
    Note find's -type f will not include symlinks to regular files (contrary to [ -f). Use -xtype f (GNU specific though) to include symlinks to regular files (well symlinks which you can tell eventually resolve to regular files, like symlinks to symlinks to regular files). – Stéphane Chazelas Oct 8 '15 at 15:12

You have three problems.

One problem is that you need whitespace around the [ … ] builtin.

[ -f $file ]

Another problem is that the output of ls -R is pretty useless. ls -R displays file names without the directory part, so unless you do some relatively heavy parsing of the output, you can't use it. The right tool to traverse directories recursively is find, which has a different syntax.

The third problem is that you're missing double quotes around variable substitutions. This doesn't completely break the script, only if you encounter file names containing whitespace or \[*?. Dealing with these file names is trivial as long as you remember to always use double quotes around variable and command substitutions: "$file", "$(mycommand)". For more information, see Why does my shell script choke on whitespace or other special characters?

find . -exec sh -c '
    if [ "$0" ] …
' {} \;

Note that you can use find -type f to only execute a command (or print out) on regular files, or (on Linux) find -xtype f to also include symbolic links to regular files.


I know it's not quite what you asked, but it's common on Unix.

Have you considered perl? Because it lets you grep based on all sorts of clever conditions.


#!/usr/bin/env perl 
use strict;
use warnings;

foreach my $file ( grep { -f } glob ( "*" ) ) {
   print "$file is a file\n";

grep iterates the list that glob returns (expands as you would expect in a shell command) and then runs a -f test on each, filtering the list accordingly.

You can also compound these tests in all sorts of arbitrary ways (like grabbing values from stat etc.)

The reason I'm suggesting it is because it doesn't suffer any interpolation/expansion issues where parsing ls might.


You can use the find command like so:

for file in $(find . -type f -iname "*.gz")
  #now if is not needed

This will save you from testing for file inside the loop.


need to use spaces

for i in `ls -R .`; do if [ $i == "file.gz" ]; then echo $i; fi; done

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.