> ## Content Index
> Fetch the complete content index at: https://www.ctrl.blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# XDG Base Directory shell scripting tutorial
- URL: https://www.ctrl.blog/entry/xdg-basedir-scripting/
- Published: 2018-03-08T03:35:00.000Z
- Updated: 2026-08-23T22:25:59.000Z
- Description: Learn how to work with XDG Base Directory variables and defaults in your Bash scripts. Write well-behaved scripts that respect user preferences.
- Author: Daniel Aleksandersen
- Tags: Linux, Open Standards

I’ve mentioned the XDG Base Directory specification a few times over the last months, and people have been curious about how to comply with the specification within their own shell-scripts and programs.

The incoming web traffic to my article “[Flatpak, Steam Cloud, and XDG Base Directories](https://www.ctrl.blog/entry/flatpak-steamcloud-xdg/)” reveals that there’s interest in a rudimentary tutorial on how to meet the requirements of the [XDG Base Directories Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html?ref=ctrl.blog) from shell scripting. Please read that article first for an introduction to the XDG Base Directory specification, and a concrete example of the kind of problems that can be caused by not following the specification.

According to the XDG Base Directory Specification, applications should store their data in one of four locations according to the type of data. Applications should discover this location by reading environmental variables, or falling back to a predefined set of default paths only if these variables aren’t set in the environment.

The below table shows the XDG Base Directory environmental variables, their default path, and what type of data goes in which directory. Your users will appreciate you for putting data in the right directories.

| Env. variable      | Default path        | Purpose                                                                                                                    |
| ------------------ | ------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| $XDG\_CACHE\_HOME  | $HOME/.cache/       | Temporary non-persistent data that might be needed later. Cache data can be recreated from other local or downloaded data. |
| $XDG\_CONFIG\_HOME | $HOME/.config/      | Program runtime configuration data.                                                                                        |
| $XDG\_DATA\_HOME   | $HOME/.local/share/ | Unique program-specific user-created data.                                                                                 |
| $XDG\_STATE\_HOME  | $HOME/.local/state/ | Program state, logs, history, current cursor/reading position, etc.                                                        |

Reading an environmental variable from a shell script is quite easy; the only trouble is ensuring that the default paths are used when the variables are unset. The XDG Base Directory specifications defaults should be used in this situation.

The below script assigns the default variables to the XDG Base Directory environmental variables only if they haven’t already been set in the environment. To override these system-wide, you can prefix these commands with the `export` command and put them in your shell profile.

```shell
XDG_CACHE_HOME:="$HOME/.cache"
XDG_CONFIG_HOME:="$HOME/.config"
XDG_DATA_HOME:="$HOME/.local/share"
XDG_STATE_HOME:="$HOME/.local/state"
```

This will let you use the XDG Base Directory variables as pointers to these directories directly in your script. Note that there’s no guarantee that the directories exist, so you’ll also have to test that they do exist and you may need to create them before you can store any data in them.

Applications traditionally create their own unique sub-directory underneath the base directories where they keep their own data. You’ll want to store variables for your sub-directories without modifying the XDG Base Directory environments themselves.

The below script creates four variables for your application’s sub-directories inside the XDG Base Directories without assigning anything to the XDG Base Directory variables themselves:

```shell
PROGRAM_NAME="my-app"

CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/$PROGRAM_NAME"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/$PROGRAM_NAME"
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/$PROGRAM_NAME"
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/$PROGRAM_NAME"

mkdir $CACHE_DIR $CONFIG_DIR $DATA_DIR $STATE_DIR
chmod 700 $CACHE_DIR $CONFIG_DIR $DATA_DIR $STATE_DIR
```

The two example scripts in this article targets any modern POSIX-compatible shell such as `bash`, `sh`, and `zsh`. They may not work with non-POSIX-compatible shells like `tcsh/C shell`.

There are more directories in the XDG Base Directory specification, and you may be able to obtain several more for common directories like Desktop, Documents, and Downloads by sourcing `$(XDG_CONFIG_HOME:-$HOME/.config)/user-dirs.dirs`. This file is available in almost all graphical desktop environments where the [xdg-user-dirs program](https://freedesktop.org/wiki/Software/xdg-user-dirs/?ref=ctrl.blog) is used.

**Update** (2022-08-07): Added the new `XDG_STATE_HOME` directory as introduced in the XDG Base Directory Specification version 0.8.

#### Sources

- [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html?ref=ctrl.blog), version 0.8, 2021-04-08, FreeDesktop.org
- [Shell Command Language](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3%5Fchap02.html?ref=ctrl.blog#tag%5F18%5F05) (POSIX.1-2008), 2016 Edition, The Open Group Base Specifications Issue 7, IEEE and The Open Group
- [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.7.html?ref=ctrl.blog), version 0.7, 2014-04-02, FreeDesktop.org