I am currently writing a script that will list all specific files in a directory. What I need the script to do is to verify that the directory is accessible. I am currently using this bit of code:
# variable used to get the file permissions of the given directory
perm=$(stat -c %a "$dir_name")
if [ "$perm" != "755" -o "$perm" != "777" ]; then
echo ERROR: "Directory $dir_name cannot be accessed check permissions"
echo USAGE: "ass2 <directory>"
exit 3
fi
This will work for checking if they have those specific octal permissions, but I was wondering if there is any other way to check if the directory is accessible or not, and to return an error if it isn't.
Use Bash Conditional Expressions
On Unix and Linux, practically everything is a file...including directories! If you don't care about execute or write permissions, you can simply check whether a directory is readable with the -r test. For example:
# Check if a directory is readable.
mkdir -m 000 /tmp/foo
[[ -r /tmp/foo ]]; echo $?
1
You can also check whether a file is a traversable directory in a similar way. For example:
# Check if variable is a directory with read and execute bits set.
dir_name=/tmp/bar
mkdir -m 555 "$dir_name"
if [[ -d "$dir_name" ]] && [[ -r "$dir_name" ]] && [[ -x "$dir_name" ]]; then
: # do something with the directory
fi
You can make the conditionals as simple or as complex as you like, but you don't have to compare octals or parse stat just to check permissions. Bash conditionals can do the job directly.
Related
How do I test a directory in a while loop where if the directory doesn't exist, then it does the following blank things. I'm trying to prompt the user for input when the directory doesn't exist so the user can retry. Once they get it right, then the script exits.
My script has an infinite loop and I don't know how to fix it. Here is what my bash script looks like:
#!/bin/bash
source_dir=$1
dest_dir=$2
while [[ ! -d "$source_dir" ]]
do
echo "This is not a directory. Enter the source directory: "
read "$source_dir"
done
while [[ ! -d "$dest_dir" ]]
do
echo "This is not a directory. enter the destination directory: "
read "$dest_dir"
done
Certainly,
read "$source_dir"
does not make sense. To understand this, assume that your script is called with the parameter FOO. Hence, you first set source_dir to FOO, and the test [[ -d $source_dir ]] will do a [[ -d FOO ]] and the directory FOO does not exist. Therefore you perform your read command, which will parameter-expand into read FOO. This means to read one line from STDIN and store it into the variable FOO.
If you want to change the value of the variable source_dir, you have to do a
read source_dir
How can I prevent directory traversal attacks in a bash script, where arguments contain directory names?
Example:
$STAGE=$1
$APP=$2
deploy.sh dist/ /opt/apps/"$STAGE"/"$APP"
The $STAGE and $APP variables are set from outside. An attacker could change this to an arbitrary path with "..".
I know the usual solution is to compare the directory string with the result of a function that returns the absolute path. But I couldn't find a ready solution and don't want to come up with my own.
Something like this?
#! /bin/bash
STAGE=$1
APP=$2
expectedParentDir="/opt/apps/"
function testDir(){
arg=$1
if [[ ! -f $arg ]]
then
echo "File $arg does not exist."
exit 1
fi
rpath=$(realpath $arg)
if [[ $rpath != ${expectedParentDir}* ]]
then
echo "Please only reference files under $expectedParentDir directory."
exit 2
fi
}
testDir /opt/apps/"$STAGE"/"$APP"
... deploy ...
Example Call
test.sh "../../etc/" "passwd"
Please only reference files under /opt/apps/ directory.
------------
test.sh "../../etc/" "secret"
File /opt/apps/../../etc//secret does not exist.
Test existence of file with -f or use -d if target must be a directory
Use realpath to resolve path
Use == ${expectedParentDir}* to find out if resolved path starts with expected string
The script should be run as a user that only has permissions to access the necessary directories.
I want to know if my file exists in any of the sub directories below. The sub directories are created in the steps above in my shell script, the below code always tells me the file do not exist (even if it does) and I want the path to be printed as well.
#!/bin/bash
....
if ! [[ -e [ **/**/somefile.txt && -s **/**/somefile.txt ]]; then
echo "===> Warn: somefile.txt was not created in the following path: "
# I want to be able to print the path in which file is not generated
exit 1
fi
I know the file name is somefile.txt which is to be created in all sub-directories, but the subdirectory names change a lot.. Hence globbing.
#!/bin/bash
shopt -s extglob ## enable **, which by default has no special behavior
for d in **/; do
if ! [[ -s "$d/somefile.txt" ]]; then
echo "===> WARN: somefile.txt was not created (or is empty) in $d" >&2
exit 1
fi
done
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.
I have a shell (ksh) script. I want to determine whether a certain directory is present in /tmp, and if it is present then I have to delete it. My script is:
test
#!/usr/bin/ksh
# what should I write here?
if [[ -f /tmp/dir.lock ]]; then
echo "Removing Lock"
rm -rf /tmp/dir.lock
fi
How can I proceed? I'm not getting the wanted result: the directory is not removed when I execute the script and I'm not getting Removing Lock output on my screen.
I checked manually and the lock file is present in the location.
The lock file is created with set MUTEX_LOCK "/tmp/dir.lock" by a TCL program.
In addition to -f versus -d note that [[ ]] is not POSIX, while [ ] is. Any string or path you use more than once should be in a variable to avoid typing errors, especially when you use rm -rf which deletes with extreme prejudice. The most portable solution would be
DIR=/tmp/dir.lock
if [ -d "$DIR" ]; then
printf '%s\n' "Removing Lock ($DIR)"
rm -rf "$DIR"
fi
For directory check, you should use -d:
if [[ -d /tmp/dir.lock ]]; then
echo "Removing Lock"
rm -rf /tmp/dir.lock
fi