0

I cant make the following check to work

for file in `ls -R .`
do
    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.

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

6 Answers 6

4

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

1
  • Note that [ -f "$file" ] will also return true for symlinks to regular files (as opposed to find's -type f for instance). Commented Oct 8, 2015 at 15:10
2

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
  • 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). Commented Oct 8, 2015 at 15:12
2

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.

1

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.

E.g.

#!/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.

0

You can use the find command like so:

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

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

0

need to use spaces

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .