60

I need to create a shell script that checks for the presence of a file and if it doesn't exist, creates it and moves on to the next command, or just moves on to the next command. What I have doesn't do that.

#!/bin/bash

# Check for the file that gets created when the script successfully finishes.
if [! -f /Scripts/file.txt]
then
: # Do nothing. Go to the next step?
else
mkdir /Scripts # file.txt will come at the end of the script
fi

# Next command (macOS preference setting)
defaults write ...

Return is

line 5: [!: command not found
mkdir: /Scripts: File exists

No idea what to do. Every place a Google search brings me indicates something different.

3
  • 6
    Put your code into shellcheck.net and review the suggestions it makes Nov 15, 2017 at 20:17
  • 13
    Any reason you can't just touch the file and skip the conditional?
    – kirkpatt
    Nov 15, 2017 at 21:52
  • Other answers below address the syntax error (missing the space between [ and !), but it's probably helpful to point out here that [ is an actual command on Unix. A Unix command requires some whitespace between the command name and its arguments. Yes, it's a Bash builtin, too, but there's also a binary at /usr/bin/[ on most systems. Nov 21, 2018 at 23:57

6 Answers 6

67

Possibly simpler solution, no need to do explicit tests, just use:

mkdir -p /Scripts
touch /Scripts/file.txt

If you don't want the "modification" time of an existing file.txt to be changed by touch, you can use touch -a /Scripts/file.txt to make touch only change the "access" and "change" times.

3
  • 7
    If you don’t care about the mtime, you could use >>/Scripts/file.txt. This will open the file for appending and create it if it doesn’t exist.
    – kojiro
    Nov 17, 2017 at 12:24
  • @kojiro awesome suggestion. No need to modify the file time (any of them) if you are just trying to ensure it exists. Nov 12, 2020 at 13:01
  • 1
    this should not be a correct answer: touch create/update the file even IF ALREADY EXISTS. The question is about if does not exist.
    – Raffaello
    Mar 16, 2021 at 13:25
41

You are getting the error because there is no space between [ and ! however there are also some flaws in your code. First you are checking if the file does not exist, and if not you are doing nothing. If the file DOES exist you are making a directory (but not doing anything to create the file).

You also don't need the null operation, you should be able to simply do:

#! /bin/bash -
if [[ ! -e /Scripts/file.txt ]]; then
    mkdir -p /Scripts
    touch /Scripts/file.txt
fi

[command2]

This is checking if /Scripts/file.txt does not exist it will create the /Scripts directory and then the file.txt file. You could also check for the existence of the directory separately if you wanted. Additionally notice I am using -e instead of -f as you asked simply to check for the existence of a file which is what -e will do where -f checks that it is a "regular file" http://tldp.org/LDP/abs/html/fto.html

2
  • what is the file does not exists but the directory does?
    – Ant
    Nov 16, 2017 at 12:37
  • 3
    @Ant: mkdir -p : create all the necessary directories, without complaining if one already exists. ex: mkdir /home/user1/tmp : will create this hierarchy , independant from the fact the /home , or /home/user1, directories existed beforehand . touch will also just update the time of an existing file, or create a new one if no file existed. One could even get rid of the if ... then part, as it is quite ok to just launch those 2 commands, if you want to ensure those directories exist and that file exist within it. Nov 16, 2017 at 13:02
32

To start with, shell script is not bash script, so let's make your code more general:

#!/bin/sh

Every Posix system must have that file; bash is strictly optional.

No need to test if the directory exists, just

dir=/Scripts
mkdir -p $dir

To create the file if it doesn't exist,

filename=$dir/file.txt
test -f $filename || touch $filename

Or if you prefer,

filename=$dir/file.txt
if [ ! -f $filename ]
then
    touch $filename
fi
6

You have a syntactical error. You require spaces before and after the [ and ].

4
#!/bin/bash

# Check for the file that gets created when the script successfully finishes.
CHECKFILE="/Scripts/file.txt"

CHECKDIR=$( dirname "$CHECKFILE" )

# The directory with the file must exist
mkdir -p "$CHECKDIR"
if [ ! -f "$CHECKFILE" ]; then
    # What to do if the file is not there
fi
touch "$CHECKFILE"

The above assumes that there are no "tricks" such as creating a directory called /Scripts/file.txt (which could be a way of forcing the script to always enter the if branch). If the "file" is a directory, the -f test will fail and the touch command will not change anything.

1

my approach

#!/bin/sh

# input might contains spaces and other characters
FILEPATH="/tmp/some where/the file.blah"

# extract the file + dir names
FILE="`basename "${FILEPATH}"`"
DIR="`dirname "${FILEPATH}"`"

# create the dir, then the file
mkdir -p "${DIR}" && touch "${DIR}/${FILE}"

# show result
ls -l "$FILEPATH"

output

  ./dofile.sh
  -rw-r--r-- 1 jmullee jmullee 0 Nov 15 21:23 /tmp/some where/the file.blah

.

3
  • 1
    You're not checking if the file exists
    – PiedPiper
    Nov 15, 2017 at 21:31
  • @piepdpiper he does, this is impliciit in touch and mkdir -p
    – xenoid
    Nov 16, 2017 at 9:16
  • same ides with more checking looks like: D=/boot/bork ; F=fnord ; mkdir -p "$D" && { touch "$D/$F" && echo "created $D/$F OK" ; } || { echo "couldn't create dir '$D'" ; }
    – jmullee
    Nov 17, 2017 at 20:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .