Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I check whether a file or file directory exist in bash?

I currently have this bash script (which is located in my home directory, i.e., /home/fusion809/ and I am running it as root as it's necessary for the icon copying lines):

cd /home/fusion809/Pictures/Icon*

declare -a A={Arch,Debian,Fedora,Mageia,Manjaro,OpenSUSE}
declare -a B={Adwaita,Faenza,gnome,Humanity}

for i in $A; do
  for j in $B; do
    if test -e /usr/share/icons/$j/scalable ; else
        mkdir /usr/share/icons/$j/scalable/
    fi
    if test -e /usr/share/icons/$j/scalable/$i.svg ; else
      cp -a $i*.svg /usr/share/icons/$j/scalable/$i.svg
    fi
  done
done

What I want this script to do is to copy icons from my Pictures/Icons and logos directory to the scalable theme (specified in $B) subdirectories in /usr/share/icons. Before it does this, however, I'd like it to create a scalable directory in these theme subdirectories if it does not already exist. The problem is that the else part of the conditionals is not being read properly, as I keep receiving this error:

./copyicon.sh: line 8: syntax error near unexpected token `else'
./copyicon.sh: line 8: `    if test -e /usr/share/icons/$j/scalable ; else'

If you're wondering why the test -e ... in the conditional it's based on a textbook on bash scripting I've been following.

like image 873
Josh Pinto Avatar asked Jun 23 '15 00:06

Josh Pinto


People also ask

What is the command to check whether it is a file or directory?

The -e flag is to check whether the files or the directories exist or not. The -f flag is to check whether the ordinary files (not directories) exist or not. Finally, the -d flag is to check whether this is a directory or not.


2 Answers

Checking file and/or directory existence

To check whether a file exists in bash, you use the -f operator. For directories, use -d. Example usage:

$ mkdir dir
$ [ -d dir ] && echo exists!
exists!
$ rmdir dir
$ [ -d dir ] && echo exists!
$ touch file
$ [ -f file ] || echo "doesn't exist..."
$ rm file
$ [ -f file ] || echo "doesn't exist..."
doesn't exist...

For more information simply execute man test.

A note on -e, this test operator checks whether a file exists. While this may seem like a good choice, it's better to use -f which will return false if the file isn't a regular file. /dev/null for example is a file but nor a regular file. Having the check return true is undesired in this case.

A note on variables

Be sure to quote variables too, once you have a space or any other special character contained in a variable it can have undesired side effects. So when you test for existence of files and directories, wrap the file/dir in double quotes. Something like [ -f "/path/to/some/${dir}/" ] will work while the following would fail if there is a space in dir: [ -f /path/to/some/${dir}/ ].

Fixing the syntax error

You are experiencing a syntax error in the control statements. A bash if clause is structured as following:

if ...; then
    ...
fi

Or optional with an else clause:

if ...; then
    ...
else
    ...
fi

You cannot omit the then clause. If you wish to only use the else clause you should negate the condition. Resulting in following code:

if [ ! -f "/usr/share/icons/$j/scalable" ]; then
    mkdir "/usr/share/icons/$j/scalable/"
fi

Here we add an exclamation point (!) to flip the expression's evaluation. If the expression evaluates to true, the same expression preceded by ! will return false and the other way around.

like image 129
ShellFish Avatar answered Sep 27 '22 16:09

ShellFish


You can't skip the then part of the if statement, easiest solution would be to just negate the test

if [[ ! -e /usr/share/icons/${j}/scalable ]] ; then
    mkdir /usr/share/icons/${j}/scalable/
fi
if [[ ! -e /usr/share/icons/${j}/scalable/${i}.svg ]] ; then
  cp -a ${i}*.svg /usr/share/icons/${j}/scalable/${i}.svg
fi

I left it with -e (exists), but you might consider using -d for directories or -f for files and some error handling to catch stuff (e.g. /usr/share/icons/$j/scalable/ exists, but is a file and not a directory for whatever reason.) I also noticed that in your original code you are potentially trying to copy multiple files into one:

cp -a $i*.svg /usr/share/icons/$j/scalable/$i.svg

I left it that way in my example in case you are sure that it is always only one file and are intentionally renaming it. If not I'd suggest only specifying a target directory.

like image 25
RSchulze Avatar answered Sep 27 '22 16:09

RSchulze