Monitor folders using unix bash - bash

I need a shell script that will monitor all the folders given in the command
and will notify the user if a certain file will be created inside them (the name of
the file will be read from keyboard).
I am allowed to use simple commands, so not inotify...
this is what i managed to do so far:
#!/bin/bash
echo "Please enter a file you want to monitor: "
read file_monitor
#this is an infinite while
while [ 1 ] ; do
#using test -e to search for the file
test -e $file_monitor && echo "The file has been created!"
sleep 5
done
I have to find a way to stop the while when the file has been created, and also to search for the file in the folders given in the command line. Can someone help me, please?

To exit the loop, use break:
test -e $file_monitor && echo "The file has been created!" && break
I would prefer to break first, and echo after the loop, or as #mkemp6 suggested, directly use the test as the condition for the loop.
To check the folders, simply loop through them, and check the file in each one.
break [n]
Exit from within a for, while, until, or select loop. If n is specified, break n
levels. n must be ≥ 1. If n is greater than the number of enclosing loops, all
enclosing loops are exited.

while ! test -e "$file_monitor"; do sleep 5; done
But you are much better off using something like inotify to monitor the appropriate directories.

#!/bin/bash
arr=$#
for i in $arr; do
if [ ! -d $i ]
then echo "The parameter $i is no a directory!"
exit 1
fi
done
echo -n "Please give file you want to monitor: "
read file_monitor
a=1
while [ $a -eq 1 ]; do
for i in $arr; do
cd $i
test -e $file_monitor && echo "The file has been created" && a=$((a+1))
cd ..
done
done
So this is what I have managed to do.

Related

How to execute multiple commands in the while loop condition?

I want to create a directory with increasing numbers every time I run the script. My current solution is:
COUNTER=1
while mkdir $COUNTER; (( $? != 0 ))
do
COUNTER=$((COUNTER + 1))
done
Is separating the commands in the while condition with a ;(semicolon) the best practice?
The very purpose of while and the shell's other control statements is to run a command and examine its exit code. You can use ! to negate the exit code.
while ! mkdir "$COUNTER"
do
COUNTER=$((COUNTER + 1))
done
Notice also the quoting; see further Why is testing "$?" to see if a command succeeded or not, an anti-pattern?
As such, if you want two commands to run and only care about the exit code from the second, a semicolon is the correct separator. Often, you want both commands to succeed; then, && is the correct separator to use.
You don't need to test the exit status, just check if the directory exists already and increment. Here is one way
#!/usr/bin/env bash
counter=1
while [[ -e $counter ]]; do
((counter++))
done
if ! mkdir "$counter"; then ##: mkdir failed
echo failed ##: execute this code
fi
POSIX sh shell.
#!/usr/bin/env sh
counter=1
while [ -e "$counter" ]; do
counter=$((counter+1))
done
if ! mkdir "$counter"; then ##: If mkdir failed
echo failed ##: Execute this code
fi
The bang ! negates the exit status of mkdir.
See help test | grep '^[[:blank:]]*!'
Well if you're just going to negate the exit status of mkdir inside the while loop then you might as well use until, which is the opposite of while
counter=1
until mkdir "$COUNTER"; do
:
COUNTER=$((COUNTER + 1))
done

Bash - Getting an N number of arguments and creating an N amount of .txt files

I am learning for an approaching exam and I've been learning more and more about bash.
One of the question goes as follows:
- Get at least 11 arguments (else, give an stderr output and exit)
- The first argument is going to be a directory
- The other arguments shall become .txt files in that directory
I can't seem to figure out how to create an N amout of text files ( N=number of arguments).
I have already tried a for loop like in the shown code, but i cannot seem to really figure it out. I should mention that this is shell is being ran in a Linux Subsystem on Windows 10 ( if it's in any way important ).
#!/bin/bash
count=$#
if [ $# -lt 11 ]
then
>&2 echo "Didn't receive enough arguments."
exit 1
fi
if [ ! -r $1 ]
then
echo "Creating file..."
mkdir $1
echo "...done."
fi
cd $1
for i in {1..$count}
do
echo $i
echo >> $i.txt
done
The output should be simple, a directory with the name of $1, in it, N amout of text files. Something similar to this:
-$2.txt
-$3.txt
-$4.txt
.
.
.
-$N.txt
The following
#!/bin/bash
count=5
for i in {0..5}
do
echo $i
done
will output:
1
2
3
4
5
and the following
for i in {0..$count}
do
echo $i
done
will output:
{1..5}
The reason for this is the order in which things occur in bash. Brace expansion occurs before variables are expanded.
So, you should change your for loop for the solution to work. A solution using seq like the following will make your script work.
for i in $(seq 1 $count)
do
echo "$i"
echo >> $i.txt
done
You can also use a c style for loop
for ((i=0; i<=count; i++));
There are many other ways. Please see the below references for more:
Reference1 Reference 2
I'm not clear why files are named 1.txt, 2.txt, .... To me, The other arguments shall become .txt files in that directory sounds like the files should be named according to the respective argument. That can be done in a loop like so:
#! /bin/sh
if [ $# -lt 11 ]
then
>&2 echo "Didn't receive enough arguments."
exit 1
fi
# errors given
mkdir "$1" && cd "$1" || exit 1
while [ $# -ge 2 ]; do
shift
touch "$1.txt"
done

Make script that reads argument from command line

I am running quantum chemical calculations by providing the command molcas -f file.input. I now have need for putting the molcas -f into a script that also tails the last 100 lines of the generated file.log, for me to quickly confirm that everything finished the way it's supposed to. So I want to run the script run.sh:
#!/bin/bash
molcas -f [here read the file.input from command line]
tail -100 [here read the file.log]
The question is how I can make the script read the argument I give, and then find on its own the output file (which has the same filename, but with a different extension).
Follow-up
Say I have a bunch of numbered files file-1, file-2, ..., file-n. I would save time if I instead of running
./run.sh file-1.input file-1.log
I run
./run.sh n n
or
./run.sh n.input n.log
assuming that the actual filename and placement of the number n is given in the script. Can that be done?
With this code:
#!/bin/bash
molcas -f "$1"
tail -100 "$2"
You will need to execute the script run.sh as follows:
./run.sh file.input file.log
to be hornest I have/had no clue over molcas, so I jumed to this side to get basic understandings.
The syntax shoould look like this ...
#!/bin/bash
# waiting for input
read -p "Enter a filename (sample.txt): " FILE
# checking for existing file
if [ -e "$FILE" ]; then
read -p "Enter a command for moculas: " CMD
else
echo "Sorry, \"${FILE}\" was not found. Exit prgramm."
exit 1
fi
# I am not sure how this command works.
# maybe you have to edit this line by your self.
molcas $FILE -f "$CMD"
# checking for programm-errors
ERRNO=$?
if [ "$ERRNO" != "" ] && [ "$ERRNO" -gt 0 ]; then
echo "Molcas returned an error. Number: ${ERRNO}"
exit 1
fi
# cuts off the fileending (For example: sample.txt gets sample)
FILENAME="${FILE%.*}"
# checking other files
ERRFILE="${FILENAME}.err"
tail -n 100 $ERRFILE
LOGFILE="${FILENAME}.log"
tail -n 100 $LOGFILE
exit 0
I would have posted more, but its not clear what to do with this data.
Hope this helps a bit.

shell scripting, how to run different code based on selection?

I have a shell script as below...
#!/bin/ksh
set_logging() {
if [ -f "$LOG_FILE" ]
then
mv $LOG_FILE $LOG_FILE.$SYNCDATE
else
touch $LOG_FILE
fi
# set logging for stdout and stderr (run this if user selects start)
exec >> $LOG_FILE
exec 2>&1
# run this if user selects dry
#exec something else
#exec something else
}
set_logging
# menu
if [[ ! -n $1 ]] ; then
clear
echo ""
echo "What are you trying to do?"
echo "start"
echo "dry"
echo "help"
echo ""
exit 99
fi
case "$1" in
start) run_tar;;
dry) nothing_here_yet;;
help) print_help;;
*) clear
echo "Your syntax is incorrect!"; exit 99;;
esac
This is a snipped of the actual script, running on AIX 7.1 UNIX OS.
What I am trying to do is based on user selection on the menu, change what parts get executed within the set_logging() block (you'll notice I commented the code I want to run if user selects different menu option just to help this example).
Again, this is an example to keep it simple so I understand and can use it in different parts of my shell script.
Anyone able to help?
I'm not totally clear of some details. If you put the following code at the top where the comments are, then it won't really work because $1 might not be set. It needs to go after the test if $1 is empty. For what you describe, you don't need something as complex as a case statement. You can do it with an if statement like:
if [[ "$1" = "dry" ]] ; then
exec something
exec somethingelse
fi
You want the $1 inside quotes. Otherwise, if it is not set, $1 disappears entirely and the statement becomes invalid. Likewise with the if test that you have, you should put the $1 inside double quotes.
You can use = or == depending upon lots of factors. Generally = and == are equivalent with a few exceptions (e.g. very old bourne shell does not have ==, etc.)
Last [[ ! -n $1 ]] is probably better written [[ -z "$1" ]]

how to check for file with wildcards existance in while loop in shell script

I'm writing a bash shell script where I want to implement a sleep sequence as long as a file exists. Now, in the simplest case this is something like:
while [ -f fileName ]
do
echo "waiting"
sleep 1
done
Now, in my situation the first issue is the following: I want to use wildcards in the fileName. This can be resolved by something like:
fileName="$path/$prefix*$suffix"
while [ -f $fileName ]
do
echo "waiting"
sleep 1
done
However, this fails if more than one file matching the fileName
./testWait.sh: line 11: [: /home/nrc11/egsnrc/BEAM_TB_jaws/130318110457_s.lock: binary operator expected
how do I resolve this?
Here is the (semi) specific example:
#!/bin/bash -u
timeStamp="130318110457"
echo "prefix of files: $timeStamp"
beamDir=$(awk '{ print $0 }' $timeStamp/$timeStamp.beamDir)
file="$EGS_HOME$beamDir/$timeStamp*.lock"
echo $file
while [ -f $file ]
do
echo "waiting"
sleep 1
done
You can use grep on the expanded wildcard to verify that it returns something else than an asterisk after the time stamp:
while echo $file | grep "^$EGS_HOME$beamDir/$timeStamp"'[^*]' ; do
echo "Waiting..."
sleep 1
done
I am very user about this, I trying doing this long back, I used some thing called inotify, a package in linux, try it out, it actually makes your life more easier.

Resources