Differences between revisions 14 and 24 (spanning 10 versions)
Revision 14 as of 2008-10-06 03:19:49
Size: 2230
Editor: GreyCat
Comment: No, Bourne shell doesn't have $(). Or [.
Revision 24 as of 2009-12-11 14:06:05
Size: 3140
Editor: GreyCat
Comment: clean up, quite a bit
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[Anchor(faq4)]] <<Anchor(faq4)>>
Line 3: Line 3:

In Bash, you can do this safely and easily with the nullglob and dotglob options (which change the behaviour of [:glob:globbing]), and arrays:
In Bash, you can do this safely and easily with the `nullglob` and `dotglob` options (which change the behaviour of [[glob|globbing]]), and [[BashFAQ/005|arrays]]:
Line 14: Line 13:
As you can see we unset the nullglob after using it, to prevent it affecting other globs in the script in unexpected ways. `nullglob` also simplifies various other operations: Or you can pour it into a SubShell to avoid having to unset (in fact, reset! - the code above assumes the shell options were unset before) the shell options again:
Line 18: Line 17:
    shopt -s nullglob
    for i in *.zip; do
        blah blah "$i" # No need to check $i is a file.
    done
    shopt -u nullglob
    if (shopt -s nullglob dotglob; f=(*); ((! ${#f[@]}))); then
        echo "The current directory is empty."
    fi
Line 25: Line 22:
Without the {{{nullglob}}}, that would have to be: Of course, you can use any glob you like instead of `*`. E.g. `*.mpg` or `/my/music/*.mpg` works fine.

Some people dislike `nullglob` because having unmatched globs vanish altogether confuses programs like `ls`. Mistyping `ls *.zip` as `ls *.zpi` may cause every file to be displayed. Setting `nullglob` in a SubShell avoids accidentally changing its setting in the rest of the shell, at the price of an extra `fork()`.

If your script needs to run with various non-Bash shell implementations, you can try using an external program like python, perl, or find; or you can try one of these:
Line 28: Line 29:
    # Bash
    for i in *.zip; do
        [[ -f $i ]] || continue # If no .zip files, i becomes *.zip
        blah blah "$i"
    done
    # POSIX
    # Clobbers the positional parameters, so make sure you don't need them.
    set -- *
    if test -e "$1" || test -L "$1"; then
        echo "directory is non-empty"
    fi
}}}
(The `-L` test is required because `-e` fails if the first file is a dangling symlink.)

{{{
    # Bourne
    # (Of course, the system must have printf(1).)
    if test "`printf '%s %s %s' .* *`" = '. .. *' && test ! -f '*'
    then
        echo "directory is empty"
    fi
Line 35: Line 47:
(You may want to use the latter anyway, if there's a possibility that the glob may match directories in addition to files.) Yes, they're quite ugly, but they should be more portable than anything depending on [[ParsingLs|ls output]]. Even `ls -A` solutions can break (e.g. on HP-UX, if you are root, `ls -A` does the exact ''opposite'' of what it does if you're not root -- and no, I can't make up something that incredibly stupid).
Line 37: Line 49:
In fact, you may wish to avoid the ''direct'' question altogether. Usually people want to know whether a directory is empty... ''because'' they want to do something involving the files therein, etc. Look to the larger question. For example, one of these [:UsingFind:find-based examples] may be an appropriate solution: In fact, you may wish to avoid the ''direct'' question altogether. Usually people want to know whether a directory is empty ''because'' they want to do something involving the files therein, etc.  Look to the larger question.  For example, one of these [[UsingFind|find-based examples]] may be an appropriate solution:
Line 46: Line 58:
If your script needs to run with various shell implementations, you can try using an external program like python, perl, or find as indicated above, or you can try something like:
Most commonly, all that's really needed is something like this:
Line 50: Line 61:
    # (Of course, the system must have printf(1).)
    cd foo || exit 1
    if test "`printf '%s %s %s' .* *`" = '. .. *' && ! test -f '*'
    then
        echo "directory is empty"
    fi
    for f in ./*.mpg; do
        test -f "$f" || continue
        mympgviewer "$f"
    done
Line 57: Line 66:

Yes, it's extremely ugly, but it should be more portable than anything depending on `ls` output. Even `ls -A` solutions can break (HPUX for one, if you are root).
In other words, the person asking the question may have ''thought'' an explicit empty-directory test was needed to avoid an error message like `mympgviewer: ./*.mpg: No such file or directory` when in fact no such test is required.

How can I check whether a directory is empty or not? How do I check for any *.mpg files?

In Bash, you can do this safely and easily with the nullglob and dotglob options (which change the behaviour of globbing), and arrays:

    # Bash
    shopt -s nullglob dotglob
    files=(*)
    (( ${#files[*]} )) || echo directory is empty
    shopt -u nullglob dotglob

Or you can pour it into a SubShell to avoid having to unset (in fact, reset! - the code above assumes the shell options were unset before) the shell options again:

    # Bash
    if (shopt -s nullglob dotglob; f=(*); ((! ${#f[@]}))); then
        echo "The current directory is empty."
    fi

Of course, you can use any glob you like instead of *. E.g. *.mpg or /my/music/*.mpg works fine.

Some people dislike nullglob because having unmatched globs vanish altogether confuses programs like ls. Mistyping ls *.zip as ls *.zpi may cause every file to be displayed. Setting nullglob in a SubShell avoids accidentally changing its setting in the rest of the shell, at the price of an extra fork().

If your script needs to run with various non-Bash shell implementations, you can try using an external program like python, perl, or find; or you can try one of these:

    # POSIX
    # Clobbers the positional parameters, so make sure you don't need them.
    set -- *
    if test -e "$1" || test -L "$1"; then
        echo "directory is non-empty"
    fi

(The -L test is required because -e fails if the first file is a dangling symlink.)

    # Bourne
    # (Of course, the system must have printf(1).)
    if test "`printf '%s %s %s' .* *`" = '. .. *' && test ! -f '*'
    then
        echo "directory is empty"
    fi

Yes, they're quite ugly, but they should be more portable than anything depending on ls output. Even ls -A solutions can break (e.g. on HP-UX, if you are root, ls -A does the exact opposite of what it does if you're not root -- and no, I can't make up something that incredibly stupid).

In fact, you may wish to avoid the direct question altogether. Usually people want to know whether a directory is empty because they want to do something involving the files therein, etc. Look to the larger question. For example, one of these find-based examples may be an appropriate solution:

   # Bourne
   find "$somedir" -type f -exec echo Found unexpected file {} \;
   find "$somedir" -maxdepth 0 -empty -exec echo {} is empty. \;  # GNU/BSD
   find "$somedir" -type d -empty -exec cp /my/configfile {} \;   # GNU/BSD

Most commonly, all that's really needed is something like this:

    # Bourne
    for f in ./*.mpg; do
        test -f "$f" || continue
        mympgviewer "$f"
    done

In other words, the person asking the question may have thought an explicit empty-directory test was needed to avoid an error message like mympgviewer: ./*.mpg: No such file or directory when in fact no such test is required.

BashFAQ/004 (last edited 2023-03-28 07:52:15 by emanuele6)