what does this bash if statement mean? - bash

What does the following bash script mean:
if [ -d "directory name" -a ! -L "directory name" ]; then
# do something
fi
I can understand up to here:
if [ -d "directory name"
but I'm lost after that. Extra consideration if, in addition to explanation, docs that explain -a ! -L

The -L operator tests whether its argument is a symbolic link. It can also be written as -h.
The ! is the logical negation operator, and -a is logical "and".
So this:
if [ -d "directory name" -a ! -L "directory name" ]; then
means "if whatever is a directory and is not a symbolic link". (-d will return true if the target is a symbolic link to a directory.
The [ syntax is actually a synonym for the test command. Either man test or info test on your system should show you the documentation. [ is also a built-in command in bash, so info bash will also show you the documentation; search for
`test'

-d is "directory exists," but you seem to know that.
-a is "logical and."
! is "expression is false"
-L is "file exists and is a symbolic link (same as -h)"
So in english this would read
If "directory name" exists and is a directory and "directory name" exists and is not a symobolic link, then...
The documentation you want is man test.

I believe the second portion is:
...and (-a) "directory name" is not (!) a symbolic link (-L)

The -a is an extension to the POSIX standard version of test to serve as a boolean AND operator. Its use is discouraged by the standard itself (see the Application Usage section) in favor of two separate test commands using the regular shell && operator.
if [ -d "directory name" ] && [ ! -L "directory name" ]; then
# do something
fi
The linked page also defines the -d, ! and -L operators:
-d pathname
True if pathname resolves to an existing directory entry for a directory. False if pathname cannot be resolved, or if pathname resolves to an existing directory entry for a file that is not a directory.
! expression
True if expression is false. False if expression is true.
-L pathname
True if pathname resolves to an existing directory entry for a symbolic link. False if pathname cannot be resolved, or if pathname resolves to an existing directory entry for a file that is not a symbolic link. If the final component of pathname is a symbolic link, that symbolic link is not followed.

[ -d FILE ] - True if FILE exists and is a directory.
[ -a File] - True if FILE exists.
[ -L FILE ] - True if FILE exists and is a symbolic link.
[ ! EXPR ] - True if EXPR is false.

Related

Bash: Is -e equivalent to -f || -d?

In my projects I use the pattern
if [ -f "$path" ] || [ -d "$path" ]; then
echo "$path exists"
fi
to check whether $path exists on the file system, regardless of whether it's a file or directory. Is this exactly equivalent to just doing
if [ -e "$path" ]; then
echo "$path exists"
fi
? What about for special files such as symbolic links or devices? Are there any platform dependent details to be aware of? I use Bash, but I would like to know about subtle differences between shells.
No, it's not. -e pathname only tests if pathname resolves to an existing directory entry. -f and -d, on the other hand, also tests if the entry is for a regular file and a directory, respectively.
For example, if pathname resolves to a FIFO (or a block special file, or a character device, or a socket, etc.), [ -e pathname ] returns true; but [ -f pathname ] || [ -d pathname ] returns false.
As for symbolic links, neither -e, -f, nor -d differentiates them from entries they resolve to. If pathname names a regular file, a directory, or a symbolic link to a regular file or a directory, both [ -e pathname ] and [ -f pathname ] || [ -d pathname ] return true. And if pathname names a symbolic link to an entry that doesn't exist (i.e. a broken symbolic link), both return false.

Command is_dir not found

I'm trying to check if a folder exists. If it doesn't, I create it.
I have this code:
if [ $(is_dir "$contaniningdir/run") = "NO"]; then
mkdir "$containingdir/run"
fi
However, I'm getting:
is_dir: command not found
So how what's the correct way of doing this?
You should use
if [ ! -d "$DIRECTORY" ]; then
# your mkdir and other stuff ...
fi
as per this question/answer.. Another relevant question/answer is here.
One of the comments also mentions an important notice:
One thing to keep in mind: [ ! -d "$DIRECTORY" ] will be true either
if $DIRECTORY doesn't exist, or if does exist but isn't a directory.
For more you should probably check that other question's page.
is_dir is a PHP function that you probably mixed with bash unintentionally :)
bash is capable of checking for the existence of a directory without external commands:
if [ ! -d "${containingdir}/run" ]; then
mkdir "${containingdir}/run"
fi
! is negation, -d checks if the argument exists and is a directory

bash test for existence of files on SFTP server

The issue that I have is with the line: "does not work" - below. The last line does indeed work - but I need to understand why the second to last line does not. I need to check for file existence on the remote server.
Have a need to check for existence for files at the following location:
/home/remoteuser/files/
and when the files are processed, they are moved to:
/home/remoteuser/logs/archive/
Would like to create an alert if the files exist at - in other words, the files were not processed:
/home/remoteuser/logs/
Found the following page and seems to be what I am looking for:
http://www.cyberciti.biz/tips/find-out-if-file-exists-with-conditional-expressions.html
Testing this and I know there are files there, but does not work:
ssh remoteuser#1.2.3.4 [ ! -f /home/remoteuser/logs/archive/*.* ] && echo "File does not exist in the root" >> /home/localuser/files/dirlist.txt
Because we know this works and does indeed list files on the local server:
ssh remoteuser#1.2.3.4 ls -l /home/remoteuser/logs/archive/*.* >> /home/localuser/files/dirlist.txt
Wildcards and test construct in Bash
You cannot use the wildcards in the [ command to test the existence of multiple files. In fact, the wildcards will be expanded and all the files will be passed to the test. Te results is that it would complain that "-f" is given too many arguments.
Try this in any non empty directory to see the output:
[ ! -f *.* ]
The only situation in which the above command does not fail is when there is only one file matching the expression, in your case a non hidden file of the form "*.*" in /home/remoteuser/logs/archive/
Using Find
A possible solution is to use find in combination with grep:
ssh name#server find /path/to/the/files -type f -name "\*.\*" 2>/dev/null | grep -q . && echo "Not Empty" || echo "Empty"
find search for regular files (-type f) whose names are in the form . (-name) and return false if nothing is found, then "grep -q ." return 1 or 0 if something is found or not.
Your goal can be accomplished with only shell builtins -- and without any uses of those builtins which depend on their behavior when passed invalid syntax (as the [ ! -e *.* ] approach does). This removes the dependency on having an accessible, working find command on your remote system.
Consider:
rmtfunc() {
set -- /home/remoteuser/logs/*.* # put contents of directory into $# array
for arg; do # ...for each item in that array...
[ -f "$arg" ] && exit 0 # ...if it's a file that exists, success
done
exit 1 # if nothing matched above, failure
}
# emit text that defines that function into the ssh command, then run same
if ssh remoteuser#host "$(declare -f rmtfunc); rmtfunc"; then
echo "Found remote logfiles"
else
echo "No remote logfiles exist"
fi
ANSWER:
Did find the following about the use of -e for a regular file.
http://www.cyberciti.biz/faq/unix-linux-test-existence-of-file-in-bash/
Even though it says "too many arguments" it does seem to test out OK.
ssh remoteuser#1.2.3.4 [ ! -e /home/remoteuser/logs/archive/*.zip ] && echo "File does not exists in the root" >> /home/localuser/files/dirlist.txt || echo "File does exists in the root" >> /home/localuser/files/dirlist.txt
Your script will work simply using double parenthesis:
ssh remoteuser#1.2.3.4 [[ ! -f /home/remoteuser/logs/archive/*.* ]] && echo "File does not exist in the root" >> /home/localuser/files/dirlist.txt
From man bash
Word splitting and pathname expansion are not performed on the words between the [[ and ]].

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/username/ and I am running it as root as it's necessary for the icon copying lines):
cd /home/username/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.
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.
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.

Shell script file existence test fails for broken symbolic link

This Bourne shell script fails to detect the existence of a broken symbolic link. It returns false and doesn't echo yet /usr/bin/firefox.real is a file that exists but as a broken symbolic link. Why?
FIREFOX="/usr/bin/firefox.real"
[ -e "$FIREFOX" ] && echo "exists"
The reason is that internally, bash will call fstat(), not lstat() when you test with -e, so it checks the file itself, not the symbolic link.
Use -h to check for existence of a link (even broken):
[ -h "$FIREFOX" ] && echo "exists"
As per man test:
-h FILE
FILE exists and is a symbolic link (same as -L)

Resources