Linux: Bash: what does mkdir return - linux

I want to write a simple check upon running mkdir to create a dir. First it will check whether the dir already exists, if it does, it will just skip. If the dir doesn't exist, it will run mkdir, if mkdir fails (meaning the script could not create the dir because it does not have sufficient privileges), it will terminate.
This is what I wrote:
if [ ! -d "$FINALPATH" ]; then
if [[ `mkdir -p "$FINALPATH"` -ne 0 ]]; then
echo "\nCannot create folder at $FOLDERPATH. Dying ..."
exit 1
fi
fi
However, the 2nd if doesn't seem to be working right (I am catching 0 as return value for a successful mkdir). So how to correctly write the 2nd if? and what does mkdir returns upon success as well as failure?

The result of running
`mkdir -p "$FINALPATH"`
isn't the return code, but the output from the program. $? the return code. So you could do
if mkdir -p "$FINALPATH" ; then
# success
else
echo Failure
fi
or
mkdir -p "$FINALPATH"
if [ $? -ne 0 ] ; then
echo Failure
fi

The shorter way would be
mkdir -p "$FINALPATH" || echo failure
also idiomatic:
if mkdir -p "$FINALPATH"
then
# .....
fi
Likewise you can while .....; do ....; done or until ......; do ......; done

Just for completeness, you can exit by issuing:
mkdir -p "$FINALPATH" || { echo "Failure, aborting..." ; exit 1 ; }
Braces are necessary, or else exit 1 would execute in both cases.
Or you can create an abort function like:
errormsg()
{
echo "$1"
echo Aborting...
{ exit 1 ; }
}
And then just call it by issuing:
mkdir -p "$FINALPATH" || errormsg "Failure creating $FINALPATH"
Edited:
Braces, not parenthesis, as parenthesis only exit the subshell.
( Thanks #Charles Duffy )
A function to write a message and exit

Related

Shell Mount and check directionairy existence

Just looking for some help with my mounting shell script, wondering if anyone could advice me on how to make it check for the directory at the mount point exists and is empty, or is created by the script if it does not exist
#!/bin/bash
MOUNTPOINT="/myfilesystem"
if grep -qs "$MOUNTPOINT" /proc/mounts; then
echo "It's mounted."
else
echo "It's not mounted."
mount "$MOUNTPOINT"
if [ $? -eq 0 ]; then
echo "Mount success!"
else
echo "Something went wrong with the mount..."
fi
fi
Your use of grep will return any mountpoint that contains the string /myfilesystem in... e.g: both of these:
/myfilesystem
/home/james/myfilesystem
Prefer to use something more prescriptive like the following:
mountpoint -q "${MOUNTPOINT}"
You can use [ to test if a path is a directory:
if [ ! -d "${MOUNTPOINT}" ]; then
if [ -e "${MOUNTPOINT}" ]; then
echo "Mountpoint exists, but isn't a directory..."
else
echo "Mountpoint doesn't exist..."
fi
fi
mkdir -p will create all parent directories, as necessary:
mkdir -p "${MOUNTPOINT}"
Finally, test if a directory is empty by exploiting bash's variable expansion:
[ "$(echo ${MOUNTPOINT}/*)" != "${MOUNTPOINT}/*" ]
It's also a good idea to run scripts with some level of 'safety'. See the set built-in command: https://linux.die.net/man/1/bash
-e Exit immediately if a pipeline (which may consist of a single simple command), a
list, or a compound command (see SHELL GRAMMAR above), exits with a non-zero
status.
-u Treat unset variables and parameters other than the special parameters "#" and "*"
as an error when performing parameter expansion.
In full: (note bash -eu)
#!/bin/bash -eu
MOUNTPOINT="/myfilesystem"
if [ ! -d "${MOUNTPOINT}" ]; then
if [ -e "${MOUNTPOINT}" ]; then
echo "Mountpoint exists, but isn't a directory..."
exit 1
fi
mkdir -p "${MOUNTPOINT}"
fi
if [ "$(echo ${MOUNTPOINT}/*)" != "${MOUNTPOINT}/*" ]; then
echo "Mountpoint is not empty!"
exit 1
fi
if mountpoint -q "${MOUNTPOINT}"; then
echo "Already mounted..."
exit 0
fi
mount "${MOUNTPOINT}"
RET=$?
if [ ${RET} -ne 0 ]; then
echo "Mount failed... ${RET}"
exit 1
fi
echo "Mounted successfully!"
exit 0
Here is how can you check directory exist and it is empty:
if [ -d /myfilesystem ] && [ ! "$(ls -A /myfilesystem/)" ]; then
echo "Directory exist and it is empty"
else
echo "Directory doesnt exist or not empty"
fi

cp command can't parse a path with wildcard in it

I have a function I wrote in bash that copies files.
It was written so it would be less painful for us to turn our batch scripts that use xcopy to bash scripts. This is because the copy commands in Linux work a little bit different.
The function does several things:
It creates a path to the target directory if it doesn't exist yet.
It uses cp to copy files
it uses cp -r to copy directories.
it uses rsync -arv --exclude-from=<FILE> to copy all the files and folders in a gives directory except the files/folders listed in FILE
The problem is, that when I try to copy files with * it gives me an error:
cp: cannot stat 'some dir with * in it': No such file or directory.
I found out that I can instead write something like that: cp "<dir>/"*".<extension>" "<targetDir>" and the command itself works. But when I try to pass that to my function, it gets 3 arguments instead of 2.
How can I use the cp command in my function while being able to pass a path with wildcard in it? meaning the argument will have double quotes in the beginning of the path and in the end of them, for example: Copy "<somePath>/*.zip" "<targetDir>"
function Copy {
echo "number of args is: $#"
LastStringInPath=$(basename "$2")
if [[ "$LastStringInPath" != *.* ]]; then
mkdir -p "$2"
else
newDir=$(dirname "$2")
mkdir -p "newDir"
fi
if [ "$#" == "2" ]; then
echo "Copying $1 to $2"
if [[ -d $1 ]]; then
cp -r "$1" "$2"
else
cp "$1" "$2"
fi
if [ $? -ne 0 ]; then
echo "Error $? while trying to copy $1 to $2"
exit 1
fi
else
rsync -arv --exclude-from="$3" "$1" "$2"
if [ $? -ne 0 ]; then
echo "Error $? while trying to copy $1 to $2"
exit 1
fi
fi
}
Okay, so I couldn't solve this with the suggestions I was given. What was happening is either the * was expanding before it was sent to function or it wouldn't expand at all inside the function. I tried different methods and eventually I decided to rewrite the function so it would instead support multiple arguments.
The expansion of the wild card happens before it sent to my function, and the copy function does all the actions it was doing before while supporting more than one file/dir to copy.
function Copy {
argumentsArray=( "$#" )
#Check if last argument has the word exclude, in this case we must use rsync command
if [[ ${argumentsArray[$#-1],,} == exclude:* ]]; then
mkdir -p "$2"
#get file name from the argument
excludeFile=${3#*:}
rsync -arv --exclude-from="$excludeFile" "$1" "$2"
if [ $? -ne 0 ]; then
echo "Error while to copy $1 to $2"
exit 1
fi
else
mkdir -p "${argumentsArray[$#-1]}"
if [[ -d $1 ]]; then
cp -r "${argumentsArray[#]}"
if [ $? -ne 0 ]; then
exit 1
fi
else
cp "${argumentsArray[#]}"
if [ $? -ne 0 ]; then
exit 1
fi
fi
fi
}

Php script as daemon

I am new to php daemons. I am using the below script to fire Daemon.php script. But i am getting error while executing this below bash script via shell
The error is,
exit: 0RETVAL=0: numeric argument required
Please help me resolve this error
#!/bin/bash
#
# /etc/init.d/Daemon
#
# Starts the at daemon
#
# chkconfig: 345 95 5
# description: Runs the demonstration daemon.
# processname: Daemon
# Source function library.
#. /etc/init.d/functions
#startup values
log=/var/log/Daemon.log
#verify that the executable exists
test -x /home/godlikemouse/Daemon.php || exit 0RETVAL=0
#
# Set prog, proc and bin variables.
#
prog="Daemon"
proc=/var/lock/subsys/Daemon
bin=/home/godlikemouse/Daemon.php
start() {
# Check if Daemon is already running
if [ ! -f $proc ]; then
echo -n $"Starting $prog: "
daemon $bin --log=$log
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $proc
echo
fi
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc $bin
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f $proc
echo
return $RETVAL
}
restart() {
stop
start
}
reload() {
restart
}
status_at() {
status $bin
}
case "$1" in
start)
start
;;
stop)
stop
;;
reload|restart)
restart
;;
condrestart)
if [ -f $proc ]; then
restart
fi
;;
status)
status_at
;;
*)
echo $"Usage: $0 {start|stop|restart|condrestart|status}"
exit 1
esac
exit $?
exit $RETVAL
This line produces the error:
test -x /home/godlikemouse/Daemon.php || exit 0RETVAL=0
If you want to set the the value of RETVAL to 0 you first need to remove the 0 as you can not have variables that start with a number.
Then you remove the value set from the second statement so it will exit in case Daemon.php does not exist.
test -x /home/godlikemouse/Daemon.php || exit
You can also remove the 2 empty echo statements inside the start and stop functions as the do nothing.
There are also errors in the case statement. You need to quote the case options and can remove the last exit block as the exit $? will trigger the exit before.
case "$1" in
"start")
start
;;
"stop")
stop
;;
"reload"|"restart")
restart
;;
"condrestart")
if [ -f $proc ]; then
restart
fi
;;
"status")
status_at
;;
There is several syntax and logic errors in this script presented. To highlight several:
echo $"Usage (should be just echo "Usage ..." since the string in ".." is not a variable
Double exit statements, the second one for $RETVAL is never ran.
exit 0RETVAL is not the same as exit $RETVAL, and one should just be using exit 1 instead to denote an error, exit 0 means the script ran correctly
$prog is defined but never used
test -x is to check for executable bit enabled in the given path. test -f is safer when testing for a file, test -d safer for testing directories, and test -L is safer when testing symlinks. Combine the test -f and test -x to ensure there is no race conditions or worst. (example: (test -f /home/godlikemouse/Daemon.php && test -x /home/godlikemouse/Daemon.php) || exit 1))
Further details on creating sysv init scripts can be read at http://refspecs.linuxbase.org/LSB_3.0.0/LSB-generic/LSB-generic/iniscrptact.html and bash scripting can be read at http://www.tldp.org/LDP/abs/html/index.html. It is strongly encouraged to learn both before writing system control programs such as init scripts.

Bash unexpected EOF in my script to wget an archive and extract it, using error checking functions

I wrote I simple bash script to wget an archive from iblocklist.com and extract it to my transmission blocklists dir. After it failed to run several times I found that the .gz archive being pushed by iblocklist was corrupt but the .zip was not so I decided to implement some error catching and an alternate means of accomplishing the task. After rewriting the script, I get unexpected EOF error and I cannot find where the problem lies. I'm by no means an advanced user of bash but I can usually accomplish what I want through trial and error and google. Not today. I've looked for the obvious missing }, fi, and ;'s but it looks good to me. Not sure if it matters but on this machine I'm running a Backtrack linux distro that more or less forces you to be root at all times. I'm a beginner so please be gentle :)
#!/bin/bash
function test {
"$#"
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "error with $1";
fi
return $STATUS
}
function askyn {
read -p "The operation failed. Try alternate means? [Y/n] " -n 1 -r
if [[ "$REPLY" =~ ^[Yy] ]] || [[ "$REPLY" = "" ]]; then YN=1;
else YN=0; fi
return $YN
}
function cleanup {
if [ $ALT == 0 ]; then {
test rm /root/scripts/.lvl1/dl/level1.gz
if [ $STATUS -ne 0 ]; then {
echo Removal of archive failed
}fi
}else {
test rm /root/scripts/.lvl1/dl/level1.zip
if [ $STATUS -ne 0 ]; then {
echo Removal of archive failed
}fi
}fi
return
}
ALT=0
YN=-1
test wget "http://list.iblocklist.com/?list=bt_level1&fileformat=p2p&archiveformat=gz" -O /root/scripts/.lvl1/dl/level1.gz
if [ $STATUS -ne 0 ]; then { #wget failed first try
askyn
if [ $YN == 1 ]; then ALT=1;else exit;fi #prompt for alternate; exit if not
}else { #wget worked first try
test file-roller -e /root/.config/transmission/blocklists /root/scripts/.lvl1/dl/level1.gz
if [ $STATUS -ne 0 ]; then { #file-roller failed to extract the list
askyn
if [ $YN == 1 ]; then ALT=1;else exit;fi #prompt for alternate; exit if not
}else { #everything worked first try
echo Download and extraction successful
cleanup
}fi
}fi
if [ $ALT == 1 ]; then { #try to wget .zip
test wget "http://list.iblocklist.com/?list=bt_level1&fileformat=p2p&archiveformat=zip" -O /root/scripts/.lvl1/dl/level1.zip
if [ $STATUS -ne 0 ]; then { #wget of .zip failed
echo Alternate means failed. Exiting.
exit
}else { #wget of .zip worked
test unzip -o -d /root/.config/transmission/blocklists /root/scripts/.lvl1/dl/level1.zip #try to unzip .zip
if [ $STATUS -ne 0 ]; then { #unzip failed
echo Alternate means failed. Exiting.
exit
}else { #everything worked second try
echo Download and extraction successful using alternate means
cleanup
}fi
}fi
}fi
The problem is your use of }fi to terminate an if. The token }fi is not a fi. So the shell at the end of the file has a lot of open ifs missing their fis.
You are in dire need of reading the shell manual, understanding shell grammar, and asking your local shell guru. The script as written is fubar, even with the syntax errors corrected.
Using braces as you have is a slightly unusual style, but there's nothing wrong with it. However, the shell is a bit picky about whitespace:
if cmd; then {
: commands
} else {
: other commands
} fi
Note the space between } and fi.
Also, you are working way too hard. Instead of explicitly checking $? all the time and writing error messages, just do things like:
if rm /root/scripts/.lvl1/dl/level1.gz; then
: # perform some commands if the remove succeeded
else
: # perform some commands on failure, but DO NOT PRINT AN ERROR MESSAGE
fi
The rational for not printing an error message is that the rm command should have already printed one. If all you plan on doing is exiting when a command fails, you can simplify things and just do:
rm /p/a/t/h || exit 1
and you can simplify even further by just doing:
#!/bin/sh -e
rm /p/a/t/h
By setting -e, the shell will immediately exit whenever any command fails.

A "try" shell script

So, the idea is to have a script that tries to run a command, and if the command fails it shows up any warnings/errors. My try:
$ cat try.sh
#! /bin/sh
tempfile=`tempfile 2>/dev/null` || tempfile=/tmp/temp$$
trap 'rm -f $tempfile >/dev/null 2>&1' 0
trap 'exit 2' 1 2 3 15
echo "$#"
if ! "$#" >$tempfile 2>&1; then
cat $tempfile;
false;
fi
Do you think that this script is ok (wrt portability and functionality)?
Some changes I would make:
Use "$#" as Steve Emmerson suggested
Don't redirect stdout of tempfile to /dev/null; that's what you're trying to capture in the variable!
Consider mktemp; it is more portable.
Capture and exit with actual exit code of command, so information is not lost.
E.g., without error checks,
tempfile=`mktemp 2>/dev/null || echo /tmp/tempfile$$`
[ -w "$tempfile" ] || { echo "Can't make tempfile" >&2; exit 1; }
"$#" 2> $tempfile
rc=$?
case $rc in
0) ;;
*) cat "$tempfile" >&2 ;;
esac
rm -f "$tempfile"
exit $rc
I would enclose the $# in double quotes in the "if" statement in order to preserve word boundaries.

Resources