Semaphore thinks lock file already exists - bash

I'm using link to create a semaphore - the idea is to lock out of writing to a db.
Here I have a script to create a table in a database:
#!/bin/bash
if [ "$#" -lt 3 ]; then
echo "Not enough parameters"
exit 1
elif [ "$#" -gt 3 ]; then
echo "Too many parameters"
exit 1
fi
if [ ! -d "$1" ]; then
echo "That database doesn't exist!"
exit 1
fi
./P.sh $1
if [ -f "$1/$2.txt" ]; then
echo "That table already exists!"
./V.sh $1
exit 1
else
touch "$1/$2.txt"
fi
./V.sh $1
echo "$3" > "$1/$2.txt"
echo "Ok, table created"
exit 0
Here's my P file:
#!/bin/bash
if [ -z "$1" ]; then
echo "Usage $0 mutex-name"
exit 1
elif [ ! -e "$1" ]; then
echo "Target for the lock must exist"
exit 2
else
while ! ln "$1" "$1.lock"; do
sleep 1
done
exit 0
fi
and my V:
#! /bin/bash
if [ -z "$1" ]; then
echo "Usage $1 mutex-name"
exit 1
else
rm "$1.lock"
exit 0
fi
let's say I create a table by running ./create_table people footballers age,height
This should create a file footballers.lock (created by P) and then once the writing has happened the V should remove it. But for some reason the P thinks that the .lock file already exists, and it definitely doesn't.
Can anyone spot what's going wrong?

Found it - you can't use ln on directories...

Related

Single variable but multiple values

I want to write a program which will check if soft links exist or not
#!/bin/bash
file="/var/link1"
if [[ -L "$file" ]]; then
echo "$file symlink is present";
exit 0
else
echo "$file symlink is not present";
exit 1
fi
There will be link2, link3, link4, link5.
Do I have to write the same script n number of times for n number of links or can this be achieved in one script?
Also I want to have the exit 0 and exit 1 so that I can use for monitoring purpose.
You can use a function:
checklink() {
if [[ -L "$1" ]]; then
echo "$1 symlink is present";
return 0
else
echo "$1 symlink is not present";
return 1
fi
}
file1="/var/link1"
file2="/var/link2"
file3="/var/link3"
file4="/var/link4"
for f in "${file1}" "${file2}" "${file3}" "${file4}"; do
checklink "$f" || { echo "Exit in view of missing link"; exit 1; }
done
echo "All symlinks checked"
exit 0

Bash : Check if PID exists with further logic

Question: How do you check if a PID exists and use the result within an IF statement in bash?
Things I've tried
if [ "$(ps -p $pid)" -eq 0 ]; then
echo "Running"
else
echo "Not Running"
fi
if [ "$(kill -0 $pid)" -eq 0 ]; then
echo "Running"
else
echo "Not Running"
fi
Neither of these evaluate correctly no matter how I redirect STDOUT/STDER
How do you check if a PID exists and use the result within an if statement?
You can capture the output in a variable and then check the exit status:
output=$(ps -p "$pid")
if [ "$?" -eq 0 ]; then
echo "Found"
echo "$output"
fi
Just remember that $? is getting reset every time you run a command, so something like the following wont work:
output=$(ps -p "$pid")
echo "$output"
# Now $? will be refering to the exit status of echo
if [ "$?" -eq 0 ]; then
echo "Found"
fi
One can also stick everything together in the if statement:
if output=$(ps -p "$pid")
then
echo "Found: $output"
fi
Make it dynamic by passing the pid you want to check:
#!/usr/local/bin/bash
if ps -p $1 > /dev/null;
then
echo "running"
else
echo "not running"
fi
Example runs:
What's your host OS?
If you have /proc then this may work for you:
if [ -d "/proc/$pid" ]; then
echo "Running"
else
echo "Not running"
fi

Check if arguments[files] exist or not in bash

This is part of my script:
#!/bin/bash
USAGE(){
echo "Usage: ./`basename $0` <File1> <File2>"
}
if [ "$#" -ne "2" ]; then
USAGE
exit 1
fi
if [ ! -f "$1" ]; then
echo "The file \"$1\" does not exist!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
I want to check if file1 does not exist prints:
The file "file1" does not exist!
if file2 does not exist prints:
The file "file2" does not exist!
If both does not exist prints:
The files "file1" and "file2" don't exist!
How can I do that?
I want to know what the most logical (STANDARD) method is.
Of course you can do that... . There are many ways to obtain this. The most simple maybe:
if [ ! -f "$1" ] && [ ! -f "$2" ]; then
echo "The files \"$1\" and \"$2\" do not exist!"
exit 1
else
if [ ! -f "$1" ]; then
echo "The file \"$1\" does not exist!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
fi
If you don't want to do the checks two time, you can work with variables; something like this:
if [ ! -f "$1" ]; then
NOT1=1
fi
if [ ! -f "$1" ]; then
NOT2=1
fi
if [ -n "$NOT1" ] && [ -n "$NOT2" ]
....
You could do it like this so you only have to test each file once:
status=""
for file; do
[ -f "$file" ]
status+=$?
done
case $status in
11)
echo "The files \"$1\" and \"$2\" do not exist!"
exit 1
;;
10)
echo "The file \"$1\" does not exist!"
exit 1
;;
01)
echo "The file \"$2\" does not exist!"
exit 1
;;
esac
The logical is
if [ ! -f "$1" ]; then
if [ ! -f "$2" ]; then
echo "The files \"$file1\" and \"$file2\" don't exist!"
exit 1
else
echo "The file \"$1\" does not exist!"
exit 1
fi
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
The readable is
if [ ! -f "$1" -a ! -f "$2" ]; then
echo "The files \"$file1\" and \"$file2\" don't exist!"
exit 1
fi
if [ ! -f "$1" ]; then
echo "The file \"$1\" does not exist!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "The file \"$2\" does not exist!"
exit 1
fi
The readable is preferred.
Also not testing for existence twice as Chris Maes said could be logical too.
Simplified for Bash:
#!/bin/bash
if [[ $# -ne 2 ]]; then
echo "Usage: ${0##*/} <File1> <File2>"
elif [[ ! -f $1 && ! -f $2 ]]; then
echo "The files \"$1\" and \"$2\" don't exist!"
elif [[ ! -f $1 ]]; then
echo "The file \"$1\" does not exist!"
elif [[ ! -f $2 ]]; then
echo "The file \"$2\" does not exist!"
fi
You can use this additional condition:
if [ ! -f "$1" ] && [ ! -f "$2" ]; then
echo "Both files does not exist!"
exit 1
fi
I'm not sure you need that though. The two conditions you have, for first and second file, will be informative enough.
Alternatively, you can use just one condition by using logical OR ||. I'd suspect users wouldn't have an issue with understanding their misues of the script:
if [ ! -f "$1" ] || [ ! -f "$2" ]; then
USAGE
exit 1
fi

Bash script errors: i in for loop gives 'no such file or directory'

Long story short, I need to write a shell script. The script will take a single command line argument which will be a directory path.
The script will then read each of the files in that directory and output it to standard output; the output will be in HTML and will be a table.
The files will be in this format:
owner sysadmin group
admin ajr
loc S-309
ser 18r97
comment noisy fan
What I have so far:
PATH=/bin:/usr/bin
cd "$#"
if [ test $? -ne 0]
then
exit 1
fi
filenames=$(ls "$#")
for i in $filenames
do
while read item value
do
if [ $item="owner" ] || [ $item="admin" ] || [ $item="loc" ] || [ $item="ser"]
then
a[$item]=$value
fi
done < i
done
echo '<html>'
echo '<body>'
echo '<table border=1>'
echo '<tr><th>hostname</th><th>location</th><th>Admin</th><th>Serial Number</th><th>owner</th><tr>'
for i in filename
do
echo '<tr><td>'$i'</td><td>'${i[loc]}'</td><td>${i[admin]}'</td><td>'${i[ser]}'</td><td>'${i[owner]}'</td><tr>'
done
echo
echo '</table>'
echo '</body>'
echo '</html>'
The HTML isn't my main concern since I am just following a format given, with each of the values going in between. However, I am getting an error that I have no idea why:
invrep: line 10: i: no such file or directory
but I am using it in a loop. Why is it giving me this error?
Also to confirm, the directory that I used exists; I'm not sure if that has to do with anything though.
Caveat Lector: the code in the question has been edited. The code I commented on may not be the code you can see.
Not directly the problem (chepner diagnosed that in his comment), but:
cd "$#"
if [ test $? -ne 0]
then
exit 1
fi
has a variety of problems. You don't verify that there's only one argument, and you pass all the arguments that are given to cd, which may just quietly ignore the surplus. The test line should use either [ or test but not both. If you use [, the last argument must be ] so you're missing a space:
if test $? -ne 0
if [ $? -ne 0 ]
However, you could short circuit that paragraph by:
cd "$#" || exit 1
(or you could drop the 1 even, though I'd leave it there).
You might want to consider:
case $# in
1) cd "$1" || exit 1;;
*) echo "Usage: $0 directory" >&2; exit 1;;
esac
This verifies that a single argument was passed and that it names a directory you can cd to.
Your looping code also has problems. The while loop should be redirected from "$i" once you've fixed things up:
filenames=$(ls "$#")
for i in $filenames
do
while read item value
do
if [ $item="owner" ] || [ $item="admin" ] || [ $item="loc" ] || [ $item="ser"]
then
a[$item]=$value
fi
done < $i
# Print HTML here!! Not after this loop
done
Your HTML loop has a lot of problems too — notably using $i as an array instead of $a.
PATH=/bin:/usr/bin
case $# in
1) cd "$1" || exit 1;;
*) echo "Usage: $0 directory" >&2; exit 1;;
esac
echo '<html>'
echo '<body>'
echo '<table border=1>'
echo '<tr><th>hostname</th><th>location</th><th>Admin</th><th>Serial Number</th><th>owner</th><tr>'
filenames=$(ls "$#")
for i in $filenames
do
while read item value
do
if [ $item="owner" ] || [ $item="admin" ] || [ $item="loc" ] || [ $item="ser"]
then
a[$item]=$value
fi
done < $i
echo "<tr><td>$i</td><td>${a[loc]}</td><td>${a[admin]}</td><td>${a[ser]}</td><td>${a[owner]}</td><tr>"
done
echo '</table>'
echo '</body>'
echo '</html>'
And that still doesn't fix the problem with using ls to generate a list of file names. For that, given the rest of the script, lose filenames altogether and use for file in * instead. You then need to quote $i in the I/O redirection, too.
PATH=/bin:/usr/bin
case $# in
1) cd "$1" || exit 1;;
*) echo "Usage: $0 directory" >&2; exit 1;;
esac
echo '<html>'
echo '<body>'
echo '<table border=1>'
echo '<tr><th>hostname</th><th>location</th><th>Admin</th><th>Serial Number</th><th>owner</th><tr>'
for i in *
do
while read item value
do
if [ $item = "owner" ] || [ $item = "admin" ] ||
[ $item = "loc" ] || [ $item="ser"]
then
a[$item]=$value
fi
done < "$i"
echo "<tr><td>$i</td><td>${a[loc]}</td><td>${a[admin]}</td><td>${a[ser]}</td><td>${a[owner]}</td><tr>"
done
echo '</table>'
echo '</body>'
echo '</html>'
(Also fixed spacing in the if statement in the loops. The code is still not very elegant, but it is somewhat related to the original code.)
filenames=$(ls "$#")
is wrong, and should never be used by anyone. See the first entry in http://mywiki.wooledge.org/BashPitfalls, or the entire page http://mywiki.wooledge.org/ParsingLs.
If your argument list is a set of directories, the inner loop would look more like this:
declare -A a
for dir in "$#"; do
for i in "$dir/"*; do
while read -r item value; do
case $item in
owner|admin|loc|ser)
a[$item]=$value
;;
esac
done <"$i"
done
done

Checking for the correct number of arguments

How do i check for the correct number of arguments (one argument). If somebody tries to invoke the script without passing in the correct number of arguments, and checking to make sure the command line argument actually exists and is a directory.
#!/bin/sh
if [ "$#" -ne 1 ] || ! [ -d "$1" ]; then
echo "Usage: $0 DIRECTORY" >&2
exit 1
fi
Translation: If number of arguments is not (numerically) equal to 1 or the first argument is not a directory, output usage to stderr and exit with a failure status code.
More friendly error reporting:
#!/bin/sh
if [ "$#" -ne 1 ]; then
echo "Usage: $0 DIRECTORY" >&2
exit 1
fi
if ! [ -e "$1" ]; then
echo "$1 not found" >&2
exit 1
fi
if ! [ -d "$1" ]; then
echo "$1 not a directory" >&2
exit 1
fi
cat script.sh
var1=$1
var2=$2
if [ "$#" -eq 2 ]
then
if [ -d $var1 ]
then
echo directory ${var1} exist
else
echo Directory ${var1} Does not exists
fi
if [ -d $var2 ]
then
echo directory ${var2} exist
else
echo Directory ${var2} Does not exists
fi
else
echo "Arguments are not equals to 2"
exit 1
fi
execute it like below -
./script.sh directory1 directory2
Output will be like -
directory1 exit
directory2 Does not exists
You can check the total number of arguments which are passed in command line with "$#"
Say for Example my shell script name is hello.sh
sh hello.sh hello-world
# I am passing hello-world as argument in command line which will b considered as 1 argument
if [ $# -eq 1 ]
then
echo $1
else
echo "invalid argument please pass only one argument "
fi
Output will be hello-world

Resources