[bash] How can I check if a directory exists in a Bash shell script?

What command can be used to check if a directory exists or not, within a Bash shell script?

This question is related to bash shell unix posix

The answer is


if [ -d "$DIRECTORY" ]; then
    # Will enter here if $DIRECTORY exists
fi

This is not completely true...

If you want to go to that directory, you also need to have the execute rights on the directory. Maybe you need to have write rights as well.

Therefore:

if [ -d "$DIRECTORY" ] && [ -x "$DIRECTORY" ] ; then
    # ... to go to that directory (even if DIRECTORY is a link)
    cd $DIRECTORY
    pwd
fi

if [ -d "$DIRECTORY" ] && [ -w "$DIRECTORY" ] ; then
    # ... to go to that directory and write something there (even if DIRECTORY is a link)
    cd $DIRECTORY
    touch foobar
fi

The ls command in conjunction with -l (long listing) option returns attributes information about files and directories.
In particular the first character of ls -l output it is usually a d or a - (dash). In case of a d the one listed is a directory for sure.

The following command in just one line will tell you if the given ISDIR variable contains a path to a directory or not:

[[ $(ls -ld "$ISDIR" | cut -c1) == 'd' ]] &&
    echo "YES, $ISDIR is a directory." || 
    echo "Sorry, $ISDIR is not a directory"

Practical usage:

    [claudio@nowhere ~]$ ISDIR="$HOME/Music" 
    [claudio@nowhere ~]$ ls -ld "$ISDIR"
    drwxr-xr-x. 2 claudio claudio 4096 Aug 23 00:02 /home/claudio/Music
    [claudio@nowhere ~]$ [[ $(ls -ld "$ISDIR" | cut -c1) == 'd' ]] && 
        echo "YES, $ISDIR is a directory." ||
        echo "Sorry, $ISDIR is not a directory"
    YES, /home/claudio/Music is a directory.

    [claudio@nowhere ~]$ touch "empty file.txt"
    [claudio@nowhere ~]$ ISDIR="$HOME/empty file.txt" 
    [claudio@nowhere ~]$ [[ $(ls -ld "$ISDIR" | cut -c1) == 'd' ]] && 
        echo "YES, $ISDIR is a directory." || 
        echo "Sorry, $ISDIR is not a directoy"
    Sorry, /home/claudio/empty file.txt is not a directory

Or for something completely useless:

[ -d . ] || echo "No"

Here's a very pragmatic idiom:

(cd $dir) || return # Is this a directory,
                    # and do we have access?

I typically wrap it in a function:

can_use_as_dir() {
    (cd ${1:?pathname expected}) || return
}

Or:

assert_dir_access() {
    (cd ${1:?pathname expected}) || exit
}

The nice thing about this approach is that I do not have to think of a good error message.

cd will give me a standard one line message to standard error already. It will also give more information than I will be able to provide. By performing the cd inside a subshell ( ... ), the command does not affect the current directory of the caller. If the directory exists, this subshell and the function are just a no-op.

Next is the argument that we pass to cd: ${1:?pathname expected}. This is a more elaborate form of parameter substitution which is explained in more detail below.

Tl;dr: If the string passed into this function is empty, we again exit from the subshell ( ... ) and return from the function with the given error message.


Quoting from the ksh93 man page:

${parameter:?word}

If parameter is set and is non-null then substitute its value; otherwise, print word and exit from the shell (if not interactive). If word is omitted then a standard message is printed.

and

If the colon : is omitted from the above expressions, then the shell only checks whether parameter is set or not.

The phrasing here is peculiar to the shell documentation, as word may refer to any reasonable string, including whitespace.

In this particular case, I know that the standard error message 1: parameter not set is not sufficient, so I zoom in on the type of value that we expect here - the pathname of a directory.

A philosophical note:

The shell is not an object oriented language, so the message says pathname, not directory. At this level, I'd rather keep it simple - the arguments to a function are just strings.


Check if the directory exists, else make one:

[ -d "$DIRECTORY" ] || mkdir $DIRECTORY

Actually, you should use several tools to get a bulletproof approach:

DIR_PATH=`readlink -f "${the_stuff_you_test}"` # Get rid of symlinks and get abs path
if [[ -d "${DIR_PATH}" ]] ; Then # Now you're testing
    echo "It's a dir";
fi

There isn't any need to worry about spaces and special characters as long as you use "${}".

Note that [[]] is not as portable as [], but since most people work with modern versions of Bash (since after all, most people don't even work with command line :-p), the benefit is greater than the trouble.


I find the double-bracket version of test makes writing logic tests more natural:

if [[ -d "${DIRECTORY}" && ! -L "${DIRECTORY}" ]] ; then
    echo "It's a bona-fide directory"
fi

As per Jonathan's comment:

If you want to create the directory and it does not exist yet, then the simplest technique is to use mkdir -p which creates the directory — and any missing directories up the path — and does not fail if the directory already exists, so you can do it all at once with:

mkdir -p /some/directory/you/want/to/exist || exit 1

Use the file program. Considering all directories are also files in Linux, issuing the following command would suffice:

file $directory_name

Checking a nonexistent file: file blah

Output: cannot open 'blah' (No such file or directory)

Checking an existing directory: file bluh

Output: bluh: directory


Git Bash + Dropbox + Windows:

None of the other solutions worked for my Dropbox folder, which was weird because I can Git push to a Dropbox symbolic path.

#!/bin/bash

dbox="~/Dropbox/"
result=0
prv=$(pwd) && eval "cd $dbox" && result=1 && cd "$prv"
echo $result

read -p "Press Enter To Continue:"

You'll probably want to know how to successfully navigate to Dropbox from Bash as well. So here is the script in its entirety.

https://pastebin.com/QF2Exmpn


DIRECTORY=/tmp

if [ -d "$DIRECTORY" ]; then
    echo "Exists"
fi

Try online


To check more than one directory use this code:

if [ -d "$DIRECTORY1" ] && [ -d "$DIRECTORY2" ] then
    # Things to do
fi

file="foo" 
if [[ -e "$file" ]]; then echo "File Exists"; fi;

You can use test -d (see man test).

-d file True if file exists and is a directory.

For example:

test -d "/etc" && echo Exists || echo Does not exist

Note: The test command is same as conditional expression [ (see: man [), so it's portable across shell scripts.

[ - This is a synonym for the test builtin, but the last argument must, be a literal ], to match the opening [.

For possible options or further help, check:

  • help [
  • help test
  • man test or man [

  1. A simple script to test if a directory or file is present or not:

     if [ -d /home/ram/dir ]   # For file "if [ -f /home/rama/file ]"
     then
         echo "dir present"
     else
         echo "dir not present"
     fi
    
  2. A simple script to check whether the directory is present or not:

     mkdir tempdir   # If you want to check file use touch instead of mkdir
     ret=$?
     if [ "$ret" == "0" ]
     then
         echo "dir present"
     else
         echo "dir not present"
     fi
    

    The above scripts will check if the directory is present or not

    $? if the last command is a success it returns "0", else a non-zero value. Suppose tempdir is already present. Then mkdir tempdir will give an error like below:

    mkdir: cannot create directory ‘tempdir’: File exists


There are great solutions out there, but ultimately every script will fail if you're not in the right directory. So code like this:

if [ -d "$LINK_OR_DIR" ]; then
if [ -L "$LINK_OR_DIR" ]; then
    # It is a symlink!
    # Symbolic link specific commands go here
    rm "$LINK_OR_DIR"
else
    # It's a directory!
    # Directory command goes here
    rmdir "$LINK_OR_DIR"
fi
fi

will execute successfully only if at the moment of execution you're in a directory that has a subdirectory that you happen to check for.

I understand the initial question like this: to verify if a directory exists irrespective of the user's position in the file system. So using the command 'find' might do the trick:

dir=" "
echo "Input directory name to search for:"
read dir
find $HOME -name $dir -type d

This solution is good because it allows the use of wildcards, a useful feature when searching for files/directories. The only problem is that, if the searched directory doesn't exist, the 'find' command will print nothing to standard output (not an elegant solution for my taste) and will have nonetheless a zero exit. Maybe someone could improve on this.


[ -d ~/Desktop/TEMPORAL/ ] && echo "DIRECTORY EXISTS" || echo "DIRECTORY DOES NOT EXIST"

Have you considered just doing whatever you want to do in the if rather than looking before you leap?

I.e., if you want to check for the existence of a directory before you enter it, try just doing this:

if pushd /path/you/want/to/enter; then
    # Commands you want to run in this directory
    popd
fi

If the path you give to pushd exists, you'll enter it and it'll exit with 0, which means the then portion of the statement will execute. If it doesn't exist, nothing will happen (other than some output saying the directory doesn't exist, which is probably a helpful side-effect anyways for debugging).

It seems better than this, which requires repeating yourself:

if [ -d /path/you/want/to/enter ]; then
    pushd /path/you/want/to/enter
    # Commands you want to run in this directory
    popd
fi

The same thing works with cd, mv, rm, etc... if you try them on files that don't exist, they'll exit with an error and print a message saying it doesn't exist, and your then block will be skipped. If you try them on files that do exist, the command will execute and exit with a status of 0, allowing your then block to execute.


if [ -d "$Directory" -a -w "$Directory" ]
then
    #Statements
fi

The above code checks if the directory exists and if it is writable.


Just as an alternative to the '[ -d ]' and '[ -h ]' options, you can make use of stat to obtain the file type and parse it.

#! /bin/bash
MY_DIR=$1
NODE_TYPE=$(stat -c '%F' ${MY_DIR} 2>/dev/null)
case "${NODE_TYPE}" in
        "directory") echo $MY_DIR;;
    "symbolic link") echo $(readlink $MY_DIR);;
                 "") echo "$MY_DIR does not exist";;
                  *) echo "$NODE_TYPE is unsupported";;
esac
exit 0

Test data:

$ mkdir tmp
$ ln -s tmp derp
$ touch a.txt
$ ./dir.sh tmp
tmp
$ ./dir.sh derp
tmp
$ ./dir.sh a.txt
regular file is unsupported
$ ./dir.sh god
god does not exist

[[ -d "$DIR" && ! -L "$DIR" ]] && echo "It's a directory and not a symbolic link"

N.B: Quoting variables is a good practice.

Explanation:

  • -d: check if it's a directory
  • -L: check if it's a symbolic link

(1)

[ -d Piyush_Drv1 ] && echo ""Exists"" || echo "Not Exists"

(2)

[ `find . -type d -name Piyush_Drv1 -print | wc -l` -eq 1 ] && echo Exists || echo "Not Exists"

(3)

[[ -d run_dir  && ! -L run_dir ]] && echo Exists || echo "Not Exists"

If an issue is found with one of the approaches provided above:

With the ls command; the cases when a directory does not exists - an error message is shown

[[ `ls -ld SAMPLE_DIR| grep ^d | wc -l` -eq 1 ]] && echo exists || not exists

-ksh: not: not found [No such file or directory]


The below find can be used,

find . -type d -name dirname -prune -print

This answer wrapped up as a shell script

Examples

$ is_dir ~                           
YES

$ is_dir /tmp                        
YES

$ is_dir ~/bin                       
YES

$ mkdir '/tmp/test me'

$ is_dir '/tmp/test me'
YES

$ is_dir /asdf/asdf                  
NO

# Example of calling it in another script
DIR=~/mydata
if [ $(is_dir $DIR) == "NO" ]
then
  echo "Folder doesnt exist: $DIR";
  exit;
fi

is_dir

function show_help()
{
  IT=$(CAT <<EOF

  usage: DIR
  output: YES or NO, depending on whether or not the directory exists.

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$1" ]
then
  show_help
fi

DIR=$1
if [ -d $DIR ]; then 
   echo "YES";
   exit;
fi
echo "NO";

From script file myScript.sh:

if [ -d /home/ec2-user/apache-tomcat-8.5.5/webapps/Gene\ Directory ]; then
   echo "Directory exists!"
   echo "Great"
fi

Or

if [ -d '/home/ec2-user/apache-tomcat-8.5.5/webapps/Gene Directory' ]; then
   echo "Directory exists!"
   echo "Great"
fi

If you want to check if a directory exists, regardless if it's a real directory or a symlink, use this:

ls $DIR
if [ $? != 0 ]; then
        echo "Directory $DIR already exists!"
        exit 1;
fi
echo "Directory $DIR does not exist..."

Explanation: The "ls" command gives an error "ls: /x: No such file or directory" if the directory or symlink does not exist, and also sets the return code, which you can retrieve via "$?", to non-null (normally "1"). Be sure that you check the return code directly after calling "ls".


Note the -d test can produce some surprising results:

$ ln -s tmp/ t
$ if [ -d t ]; then rmdir t; fi
rmdir: directory "t": Path component not a directory

File under: "When is a directory not a directory?" The answer: "When it's a symlink to a directory." A slightly more thorough test:

if [ -d t ]; then 
   if [ -L t ]; then 
      rm t
   else 
      rmdir t
   fi
fi

You can find more information in the Bash manual on Bash conditional expressions and the [ builtin command and the [[ compound commmand.


Type this code on the Bash prompt:

if [ -d "$DIRECTORY" ]; then
    # If true this block of code will execute
fi

Remember to always wrap variables in double quotes when referencing them in a Bash script. Kids these days grow up with the idea that they can have spaces and lots of other funny characters in their directory names. (Spaces! Back in my days, we didn't have no fancy spaces! ;))

One day, one of those kids will run your script with $DIRECTORY set to "My M0viez" and your script will blow up. You don't want that. So use this.

if [ -d "$DIRECTORY" ]; then
    # Will enter here if $DIRECTORY exists, even if it contains spaces
fi

Using the -e check will check for files and this includes directories.

if [ -e ${FILE_PATH_AND_NAME} ]
then
    echo "The file or directory exists."
fi

To check if a directory exists you can use a simple if structure like this:

if [ -d directory/path to a directory ] ; then
# Things to do

else #if needed #also: elif [new condition]
# Things to do
fi

You can also do it in the negative:

if [ ! -d directory/path to a directory ] ; then
# Things to do when not an existing directory

Note: Be careful. Leave empty spaces on either side of both opening and closing braces.

With the same syntax you can use:

-e: any kind of archive

-f: file

-h: symbolic link

-r: readable file

-w: writable file

-x: executable file

-s: file size greater than zero

In kind of a ternary form,

[ -d "$directory" ] && echo "exist" || echo "not exist"

And with test:

test -d "$directory" && echo "exist" || echo "not exist"

Shorter form:

# if $DIR is a directory, then print yes
[ -d "$DIR" ] && echo "Yes"

More features using find

  • Check existence of the folder within sub-directories:

      found=`find -type d -name "myDirectory"`
      if [ -n "$found" ]
      then
          # The variable 'found' contains the full path where "myDirectory" is.
          # It may contain several lines if there are several folders named "myDirectory".
      fi
    
  • Check existence of one or several folders based on a pattern within the current directory:

      found=`find -maxdepth 1 -type d -name "my*"`
      if [ -n "$found" ]
      then
          # The variable 'found' contains the full path where folders "my*" have been found.
      fi
    
  • Both combinations. In the following example, it checks the existence of the folder in the current directory:

      found=`find -maxdepth 1 -type d -name "myDirectory"`
      if [ -n "$found" ]
      then
          # The variable 'found' is not empty => "myDirectory"` exists.
      fi
    

Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to shell

Comparing a variable with a string python not working when redirecting from bash script Get first line of a shell command's output How to run shell script file using nodejs? Run bash command on jenkins pipeline Way to create multiline comments in Bash? How to do multiline shell script in Ansible How to check if a file exists in a shell script How to check if an environment variable exists and get its value? Curl to return http status code along with the response docker entrypoint running bash script gets "permission denied"

Examples related to unix

Docker CE on RHEL - Requires: container-selinux >= 2.9 What does `set -x` do? How to find files modified in last x minutes (find -mmin does not work as expected) sudo: npm: command not found How to sort a file in-place How to read a .properties file which contains keys that have a period character using Shell script gpg decryption fails with no secret key error Loop through a comma-separated shell variable Best way to find os name and version in Unix/Linux platform Resource u'tokenizers/punkt/english.pickle' not found

Examples related to posix

How to make parent wait for all child processes to finish? Kill all processes for a given user What is the proper #include for the function 'sleep()'? What is /dev/null 2>&1? How to kill all processes with a given partial name? What can lead to "IOError: [Errno 9] Bad file descriptor" during os.system()? How to use nanosleep() in C? What are `tim.tv_sec` and `tim.tv_nsec`? Converting year and month ("yyyy-mm" format) to a date? CRON job to run on the last day of the month Checking if a file is a directory or just a file