How To Check If a Directory Exists In a Shell Script

How do I check if a directory exists in a shell script under Linux or Unix like operating systems? How can I check if a directory exists in a shell script on Unix-like system?

To check if a directory exists in a shell script and is a directory use the following syntax:
[ -d "/path/to/dir" ] && echo "Directory /path/to/dir exists."
## OR ##
[ ! -d "/path/to/dir" ] && echo "Directory /path/to/dir DOES NOT exists."
## OR combine both of them in a single go: ##
[ -d "/path/to/dir" ] && echo "Directory /path/to/dir exists." || echo "Error: Directory /path/to/dir does not exists."
Tutorial details
Difficulty level Easy
Root privileges No
Requirements Bash/Linux
Est. reading time 3 mintues

Checking If a Directory Exists In a Bash Shell Script

The following version also check for symbolic link:

[ -d "/path/to/dir" ] && [ ! -L "/path/to/dir" ] && echo "Directory /path/to/dir exists." || echo "Error: Directory /path/to/dir exists but point to $(readlink -f /path/to/dir)."
## OR ##
[ -d "/path/to/dir" ] && [ ! -h "/path/to/dir" ] && echo "Directory /path/to/dir exists." || echo "Error: Directory /path/to/dir exists but point to $(readlink -f /path/to/dir)."

The && is AND list operator. The syntax is:
foo && bar
The bar command is executed if, and only if, foo returns an exit status of zero (success). Similarly we have the || (OR) list and the syntax is:
cmd1 || cmd2
The cmd2 is executed if, and only if, cmd1 returns a non-zero exit status. The return status of AND and OR lists is the exit status of the last command executed in the list.

Using if..else..if

Finally, you can use the traditional bash syntax as follows:

if [ -d "/path/to/dir" ] 
    echo "Directory /path/to/dir exists." 
    echo "Error: Directory /path/to/dir does not exists."


### Check if a directory does not exist ###
if [ ! -d "/path/to/dir" ] 
    echo "Directory /path/to/dir DOES NOT exists." 
    exit 9999 # die with error code 9999

Shell script examples to see if a ${directory} exists or not

The following script also demos the use of readlink command to print value of a symbolic link or canonical file name.

# dirtest.bash - Demo script by nixCraft under GPL v2.x+
# -------------------------------------------------------
[ $# -eq 0 ] && { echo "Usage: $0 dir-name"; exit 1; }
if [ -d "$dir" -a ! -h "$dir" ]
   echo "$dir found and setting up new Apache/Lighttpd/Nginx jail, please wait..."
   # __WWWJailSetup "" "setup"
   echo "Error: $dir not found or is symlink to $(readlink -f ${dir})."

Save and run it as follows:

$ chmod +x dirtest.bash
$ ./dirtest.bash
$ ./dirtest.bash /home/httpd
$ ./dirtest.bash /var/www

Sample outputs:

Fig.01: Shell script in action

In this example, create directories if does not exists:
# Purpose: Setup jail and copy files
# Author: nixCraft <> under GPL v2.x+
# Category : Core
# Override : No
# Parameter(s) : d => domain name
#                action => setup or update
        local d="$1"
        local action="${2:setup}"       # setup or update???
        local index="<html><head><title>$d</title></head><body><h2>$d</h2></body></html>" # default index.html
        local J="$(_getJailRoot $d)/$d" # our sweet home 
        local _i=""
        [ "$action" == "setup" ] && echo "* Init jail config at $J..." || echo "* Updating jail init config at $J..."
        __init_domain_config "$d"
        [ "$action" == "setup" ] && echo "* Setting up jail at $J..." || echo "* Updating jail at $J..."
        [ ! -d "$J" ] &&  $_mkdir -p "$J"
        for _i in $J/{etc,tmp,usr,var,home,dev,bin,lib64}
                [ ! -d "$_i" ] &&  $_mkdir -p "$_i"
        for _i in $_lighttpd_webalizer_base/$d/stats/{dump,out}
                [ ! -d "$_i" ] &&  $_mkdir -p "$_i"
        for _i in $_lighttpd_webalizer_prepost_base/$d/{pre.d,post.d}
                [ ! -d "$_i" ] &&  $_mkdir -p "$_i"
## truncated 


Remember when we say “FILE”, it means directory too.

Use the following to check file/directory types and compare values:

  1. -L "FILE" : FILE exists and is a symbolic link (same as -h)
  2. -h "FILE" : FILE exists and is a symbolic link (same as -L)
  3. -d "FILE" : FILE exists and is a directory
  4. -w "FILE" : FILE exists and write permission is granted
  5. -x "FILE" : FILE exists and execute (or search) permission is granted
  6. -r "FILE" : FILE exists and read permission is granted
  7. -s "FILE" : FILE exists and has a size greater than zero


We learned how to check if a directory exists in a shell script using the test command and other methods under Linux/Unix bash. See the following resources for more information:
man test
man bash
man readlink
man stat


🐧 Get the latest tutorials on Linux, Open Source & DevOps via RSS feed or Weekly email newsletter.

🐧 11 comments so far... add one

CategoryList of Unix and Linux commands
Disk space analyzersdf duf ncdu pydf
File Managementcat cp mkdir tree
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Modern utilitiesbat exa
Network UtilitiesNetHogs dig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg glances gtop jobs killall kill pidof pstree pwdx time vtop
Searchingag grep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
11 comments… add one
  • Gilles Apr 14, 2013 @ 20:32

    I sometime use “[ -x $dir/. ]”, to check at the same time if directory exist and if the calling process has traverse rights on it.

  • chukaman Aug 23, 2013 @ 6:18

    “exits” “exits” “exits” all over the place where you had intended to write “exists”. you might want to change that.

  • Peter Mar 1, 2016 @ 14:31

    i love this one:

    [[ -d dir ]] || mkdir dir

    • Apr 21, 2016 @ 2:08

      Isn’t that same as `mkdir -p dir`?

      • 🐧 Vivek Gite May 2, 2017 @ 12:29

        You can do a little bit customization like:
        [[ -d dir ]] || { echo "Dir not found...creating..."; mkdir dir; /path/to/call/something/init; }

  • Curtis Sep 29, 2017 @ 2:47

    I really like this one for setting a variable to a file path only if it exists. From what I can tell, this is probably amongst the most efficient methods of doing such a task.

    Lets assume we have the following:

    Using bash’s builtin “type” the “-P” does a PATH search, only printing if the filename is found within the path, irrespective of the executable state:

    $ VAR1=$(PATH=/path/to type -P file)
    $ echo $VAR1

    What is really neat about this is that we can use it to see if the file exists in several places, using the order of the temporary PATH to indicate preferences.

    Let’s add a couple files for an example:

    Now if we want to set “file” as a variable, and know it should be in one of three places, we can do…

    $ VAR2=$(PATH=/path/to/other:/path/to:/other/directory type -P file)
    $ echo $VAR2

    But what if /path/to takes precedence? An example is any program that gives configs in /etc priority over /usr/lib if identically named (ie. systemd, modprobe.d, etc.). We can use the PATH order to match by level of significance…

    $ VAR3=$(PATH=/path/to:/path/to/other:/other/directory type -P file)
    $ echo $VAR3

    Bash’s builtin “type” is quite nice in that upon failure, it prints nothing. So you don’t even need to throw out stderr with “2>/dev/null”!

    $ VAR4=$(/path/to:/path/to/other:/other/directory type -P baz)
    $ echo $VAR4


    Even if you did “echo $FARTBRAINS” it still creates a nice \n for you, even if you’ve never even remotely thought about using that variable name. So VAR4 is essentially unset. Neat!

    Usually I’m a zsh user. Zsh’s “whence” and “which” come closest, but cannot search beyond executables and print a short fail message (that I’d otherwise not mind). I’m sure other shells might behave similarly, but those are the two I’ve tested. I script with bash anyway.

    Unfortunately, I have not come up with a way to do directories in a similar manner. I really searched through the bash shell builtin documentation and google-fu’ed the best I could, but I cannot figure anything out that is that efficient.

    Hopefully someone smarter than me can prove there is a way. I don’t know if I’ll ever check this page again… and don’t know why I just put so much work into this comment. But I hope you enjoyed!

  • Henrik Olander Mar 10, 2021 @ 8:26

    Hi, there is an error in your example, if the dir does not exist at all (and no link) the example below still prints the “Error……” (the OR part)

    [[ -d "/tmp/ttt" && ! -L "/tmp/ttt" ]] && echo "Directory /tmp/ttt" || echo "Error: Directory /tmp/ttt exists but point to $(readlink -f /tmp/ttt)."
    For no dir at all:
    Error: Directory /tmp/ttt exists but point to /tmp/ttt.
    • Kris May 23, 2021 @ 2:32

      Yep, I concur, it’s incorrect/incomplete in that sense. I’ve made a quick alteration that I believe works with a bit more clarity:

      [ -d "$pathToDir" ] && [ ! -L "$pathToDir" ] && printf "Dir:\nDirectory exists: $pathToDir" \
      || \
      [ -L $pathToDir ] && printf "Symlink:\n$pathToDir\nPoints to:\n$(readlink -f $pathToDir)\n" \
      || \
      echo "Dir:\nDirectory does not exist: $pathToDir"\;

      • Kris May 23, 2021 @ 2:39

        Slight improvement for stdin / stdout clarity:

        [ -d "$aptLocalRepo" ] && [ ! -L "$aptLocalRepo" ] && printf "Dir:\nDirectory exists: $aptLocalRepo\n" || \
        [ -L $aptLocalRepo ] && printf "Symlink:\n$aptLocalRepo\nPoints to:\n$(readlink -f $aptLocalRepo)\n" || \
        printf "Dir:\nDirectory does not exist: $aptLocalRepo\n"

  • Serg May 4, 2021 @ 7:52

    Second example is a typo – you need a space between the opening bracket and the bang, like this

    [  ! -d "/path/to/dir" ] && echo "Directory /path/to/dir DOES NOT exists."

    The current code would cause “bash: [!: command not found” error message

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre> for code samples. Still have questions? Post it on our forum