9

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.

2
  • 1
    “The goal is to check for the existence of a few directories and for the nonexistence of others.” Well, your example checks for existence of all listed directories. Can you elaborate on this part ? Is it all listed or any listed ? Do you need a check that passed values are in fact directories and not other types of files ? – Sergiy Kolodyazhnyy Mar 2 '19 at 5:28
  • 1
    You don't need the $PWD, by the way. [ -d "$PWD/dir1"] is equivalent to [ -d "dir1" ]. – terdon Mar 2 '19 at 14:43
5

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.

8
  • 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. – Elegance Mar 1 '19 at 18:46
  • @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/… – Jeff Schaller Mar 1 '19 at 18:51
  • 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? – Elegance Mar 1 '19 at 19:02
  • 3
    (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). – G-Man Says 'Reinstate Monica' Mar 2 '19 at 3:40
  • 1
    this command could take much longer to run than the test in the original question. also it doesn't check for directories. – Jasen Mar 2 '19 at 6:16
10

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 &&

5

A loop might be more elegant:

arr=("$PWD/dir1" "$PWD/dir2" "$PWD/dir2")
for d in "${arr[@]}"; do
    if [ -d "$d"]; then
        echo True
    else
        echo False
    fi
done

This is Bash. A more portable one is Sh. There you can use the positional array:

set -- "$PWD/dir1" "$PWD/dir2" "$PWD/dir2"

Then to loop over it use "$@".

5

Why not just:

if [ -d "dir1" -a -d "dir2" -a -d "dir3" ]; then
    echo True
else
    echo False
fi
6
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
1

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
      ︙

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.