Checking for the existence of multiple directories

9,184

Solution 1

If you already expect them to be directories and are just checking whether they all exist, you could use the exit code from the ls utility to determine whether one or more "errors occurred":

ls "$PWD/dir1" "$PWD/dir2" "$PWD/dir3" >/dev/null 2>&1 && echo All there

I redirect the output and stderr to /dev/null in order to make it disappear, since we only care about the exit code from ls, not its output. Anything that's written to /dev/null disappears — it is not written to your terminal.

Solution 2

I would loop:

result=True
for dir in \
        "$PWD/dir1" \
        "$PWD/dir2" \
        "$PWD/dir3" 
do
    if ! [ -d "$dir" ]; then
        result=False
        break
    fi
done
echo "$result"

The break causes the loop to short-circuit, just like your chain of &&

Solution 3

Why not just:

if [ -d "dir1" -a -d "dir2" -a -d "dir3" ]; then
    echo True
else
    echo False
fi

Solution 4

As per the question, two portable shell functions that test for the existence and nonexistence of multiple directories:

# Returns success if all given arguments exists and are directories.
ck_dir_exists () {
    for dir do
        [ -d "$dir" ] || return 1
    done
}

# Returns success if none of the given arguments are existing directories.
ck_dir_notexists () {
    for dir do
        [ ! -d "$dir" ] || return 1
    done
}

Example:

$ mkdir dir1 dir2
$ ck_dir_exists dir1 dir2; echo $?
0
$ ck_dir_exists dir1 dir2 dir3; echo $?
1
$ ck_dir_notexists dir1 dir2 dir3; echo $?
1
$ ck_dir_notexists dir3 dir4; echo $?
0

Solution 5

The goal is to check for the existence of a few directories and for the nonexistence of others.  [Emphasis added]

Building on glenn jackman’s answer, we can test for the nonexistence of other names like this:

result=True
for dir in \
        "$PWD/dir1" \
        "$PWD/dir2" \
        "$PWD/dir3" 
do
    if ! [ -d "$dir" ]; then
        result=False
        break
    fi
done
for dir in \
        "$PWD/dir4" \
        "$PWD/dir5" \
        "$PWD/dir6" 
do
    if [ -e "$dir" ]; then                      # Note: no “!”
        result=False
        break
    fi
done
echo "$result"
I used [ -e "$dir" ] to test whether "$dir" exists; i.e., if dir5 exists but is a file, the result is False.  If you want only to test whether the names in the second group are directories, use [ -d "$dir" ], like in the first loop.

Since we’re talking about checking for the existence of things in the current working directory, it’s probably not necessary to specify $PWD/ on the names; just do

for dir in \
        "dir1" \
        "dir2" \
        "dir3" 
do
      ︙
Share:
9,184

Related videos on Youtube

Elegance
Author by

Elegance

Updated on September 18, 2022

Comments

  • Elegance
    Elegance over 1 year

    I want to check for the existence of multiple directories, say, dir1, dir2 and dir3, in the working directory.

    I have the following

    if [ -d "$PWD/dir1" ] && [ -d "$PWD/dir2" ] && [ -d "$PWD/dir3" ]; then
        echo True
    else
        echo False
    fi
    

    But I suspect there is a more elegant way of doing this. Do not assume that there is a pattern in the names of the directories.

    The goal is to check for the existence of a few directories and for the nonexistence of others.

    I'm using Bash, but portable code is preferred.

  • Elegance
    Elegance about 5 years
    Can you help me understand this command? I know what file descriptors are. I know 1 is stdout, 2 is stderr and I know what redirecting is. I don't understand the significance of /dev/null, and I do not know how to parse the command.
  • Jeff Schaller
    Jeff Schaller about 5 years
    @Elegance I added a little explanation. For more in-depth answers regarding /dev/null, see unix.stackexchange.com/questions/163352/… and unix.stackexchange.com/questions/438130/…
  • Elegance
    Elegance about 5 years
    Still trying to figure out how the syntax works. I read that &>filename redirects both stdout and stderr to filename. So couldn't the command be simplified (at least to me it is more simple) as ls "$PWD/dir1" "$PWD/dir2" "$PWD/dir2" &>/dev/null && echo All there?
  • Jeff Schaller
    Jeff Schaller about 5 years
    It could, but not portably -- plain sh does not understand &>; it would misinterpret that as "run me in the background and send stdout to the redirection". I spelled it out from habit and kept it there because of the "portable code" preference.
  • Elegance
    Elegance about 5 years
    Thank you very much, by the way. There's just one thing escaping me. ls "$PWD/dir1" "$PWD/dir2" "$PWD/dir2" >/dev/null This redirects the output to "nothing", right? Then, in my eyes, 2>&1 would take the the stderr (because of 2) of nothing and redirect to stdout (because of &1) of god knows what. I understand what this is doing, I just don't get the syntax, it's not consistent with the regular use of >. I know I'm wrong in saying this, I'm just trying to explain what's on my mind. Should I ask this in another question? One last question: can it be adapted for nonexistence?
  • Jeff Schaller
    Jeff Schaller about 5 years
    I redirected stdout for directories that exist and stderr for directories that don't exist -- we don't care about the contents of the former nor about ls's complaints about the latter. The redirections are processed in order, so after "dumping" stdout, stderr is then pointed to where stdout now points (the same /dev/null). See also: unix.stackexchange.com/questions/159513/…
  • Jeff Schaller
    Jeff Schaller about 5 years
    This is essentially what the OP started with, But I suspect there is a more elegant way of doing this
  • Elegance
    Elegance about 5 years
    Also POSIX discourages the use of -a: "-a and -o binary primaries (...) operators have been marked obsolescent": pubs.opengroup.org/onlinepubs/9699919799
  • David Conrad
    David Conrad about 5 years
    @JeffSchaller It's more terse since it does it all in one call to test.
  • David Conrad
    David Conrad about 5 years
    @Elegance They're still supported on all the systems I use, and probably will be a hundred years from now.
  • G-Man Says 'Reinstate Monica'
    G-Man Says 'Reinstate Monica' about 5 years
    (1) You should probably use the -d option (a) so ls needs only to stat the directories, and not read them, and (b) so the command will succeed even if the user doesn’t have read access to the directories.  (2) I don’t see any reason to use "$PWD/" except to guard against directories whose names begin with - (and, of course, there are better ways to do that).
  • Jasen
    Jasen about 5 years
    this command could take much longer to run than the test in the original question. also it doesn't check for directories.
  • Jasen
    Jasen about 5 years
    it's not at all the same, the original invokes test (aka [) three times this invokes it once,
  • G-Man Says 'Reinstate Monica'
    G-Man Says 'Reinstate Monica' about 5 years
    pubs.opengroup.org/onlinepubs/9699919799/utilities/… is a more precise reference to the location of the POSIX statement.
  • pbhj
    pbhj about 3 years
    In view of @G-ManSays'ReinstateMonica' 's comment then it looks like [-d "dir1" && -d "dir2" && -d "dir3"] would be compliant without using the obsolescent -a but instead shellcheck tells me to split them as in the OP's original version.
  • G-Man Says 'Reinstate Monica'
    G-Man Says 'Reinstate Monica' about 3 years
    @pbhj:  Obviously you didn't try that.   David Conrad's answer at least works; yours is wrong in a couple of ways.
  • pbhj
    pbhj about 3 years
    I did try it (BASH, on Kubuntu) in the context of a different script (for my mopp-unofficial project on gitlab), but rewrote it by hand to match the question here. Like I said, shellcheck said splitting it was preferable - but not why, I didn't investigate - so I reverted to the split version which basically matches the original question. As it was a comment I didn't check my syntax, that wasn't the point of the comment -- please explain my error.
  • pbhj
    pbhj about 3 years
    fwiw, gist.github.com/pbhj/cd6bd6b83a1ccbaaf55b7f870e16b9d4 shows the shellcheck outputs in case anyone wants to follow up.