In POSIX shell, how to increment a variable each run? [duplicate] - shell

Can I create a Bash script with persistent variable-values?
For example, I initialize a variable with 0 when the script runs for the first time (during a specific time limit), and the variable increases automatically with every time the script is running.

You can't, but you can use a file to do it
#!/bin/bash
valuefile="/tmp/value.dat"
# if we don't have a file, start at zero
if [ ! -f "$valuefile" ]; then
value=0
# otherwise read the value from the file
else
value=$(cat "$valuefile")
fi
# increment the value
value=$((value + 1))
# show it to the user
echo "value: ${value}"
# and save it for next time
echo "${value}" > "$valuefile"

I'm afraid you have to save the state in a file somewhere. The trick is to put it somewhere the user will be able to write to.
yourscriptvar=0
if [ -e "$HOME/.yourscriptvar" ] ; then
yourscriptvar=$(cat "$HOME/.yourscriptvar")
fi
# do something in your script
#save it output the file
echo $yourscriptvar > "$HOME/.yourscriptvar"

I had the same problem a few days ago and I've written my own tool to do the work because there is not other way to do something similar.
gvar is a pure Bash key-value store where each user has a different collection of data. The records are stored in the user's home directory.
Here it is the most interesting functions from the source code:
get_variable() {
cat $FILE | grep -w $1 | cut -d'=' -f2
}
set_variable() {
echo $1=$2 >> $FILE
}
remove_variable() {
sed -i.bak "/^$1=/d" $FILE
}

Related

Bash script to check if output of cmd increases over a period of time and if so run

I am looking to compare the output of a file in between time-frames.
Example:
Run command and it outputs a numerical value 1 to 1000 (1-1000). I need to run the same command 15 min later and see if it has increased by 20 and if so send email.
I am thinking this is very simple, however I cannot get my head around where to even start.
I would think that I would have to run the command and output to file and when the command runs again in 15 min compare those values. I would think that I would need to check if the output file exists and then create if not.
I am new to scripting and could use the help. I got the below but just am stuck beyond. I am looking to make this as simple as possible
#!/bin/bash
today="date '+%Y_%m_%d'"
command="echo "select count(status) from alarm where status ='open'" | my-db"
# the command above will return the following for example
#count(status)
#34
$command > /tmp/$today.dblogout
You could simply keep the last known (last retrieved) value in a temporary file, and every time the script is executed, check for how much the value has increased since last run (updating the last known value along the way).
For example:
#!/bin/bash
store=/tmp/last_value
threshold=20
last=$(<"$store")
current=$(my-db <<<"select count(status) from alarm where status = 'open'" | tee "$store")
if (( current - last > threshold )); then
echo "Sending mail..."
fi
I made something similar to alert me if my disk is getting full:
#!/bin/sh
THRESHOLD=85
datafile=/var/run/df_slash_var_pcent_avail.txt
# get previous value:
prev=$(tail -1 $datafile | cut -d\ -f2)
# get current value
curr=$(($(df /var --output=pcent | tail -1 | cut -d% -f1) + 0))
# store current value
echo "$(date +%s) $curr" >> $datafile
# if getting worse, alert
[[ $curr > $THRESHOLD ]] && [[ $curr > $prev ]] && smsme "/var grew from ${prev}% to ${curr}%"
This script is pretty straightforward. Feel free to ask if sth is not clear.
A couple of options
If alerts table has a date column you can write an SQL query to get the count for you.
Use bash to get the values
#!/bin/bash
# Store date in a variable, note the $( ) surrounding the command
today="$(date '+%Y_%m_%d')"
old=0
# read the old value from file if it exists
if [ -f /tmp/$today.dblogout ]; then
old="$(< /tmp/$today.dblogout)"
fi
# store just the value without any header
new=$(echo "select count(status) from alarm where status ='open'" | my-db)
# the command above will return the following for example
#34
# response is not empty
if [ -z "$new" ]; then
echo "old count: $old, new count: $new"
else
echo "new value is empty. old value: $old"
fi
# store the new value
echo "$new" > /tmp/$today.dblogout

How to make option to run one directory or all directory in shell script?

I want to make the decision for Shell script. Either to run only one directory or all directory. This is my coding:
#!/bin/sh
function_run ()
{
python Declare.py
for a in $1
do
echo $a
# Loop to call the file.
for i in 1 2 3 4 5
do
# Cut the row and column
grep -v '^#' $a/result*.txt | tr -s ' ' | cut -d ' ' -f 6 | cat > pos.txt
done
python Calculate.py $a/pos.txt $a/neg.txt $a/pos$i.txt $a/neg$i.txt
python Graph.py $a/output*.py $a/output1.py
python allMax.py "$a" $a/output1.py $a/maxList.py
done
python allMaxGraph.py maxList.py maxTPRlist.py max1TPRlist.py
}
echo "Which KO you want to run?(If all just put ALL): "
read input
a=K*
if [ $input = 'ALL' ]
then
function_run $a
elif [ -d $input ]
then
function_run $input
else
echo "File $input does not exist"
fi
But when I want to run for all directory and I input ALL. But only one directory work and the script stop. It not run for all directory.
Your main problem is that you are reusing the variable name a inside the function_run(). Change the local variable name or use local a.
You have more problems.
Start using quotes around variables, such as
a="K*"
if [ "$input" = "ALL" ]
You have a check for an existing dir. That check is skipped when you use ALL. Move the check to the function.
I do not understand your function. You are looping with $i, but never use the different values.
Your function is parsing the input with for a in $1. Use $* for all parameters.
When you think you are finished, also test with a directory name with a space inside (mkdir "Know what").

getting local variable value outside in bash script

I am new to bash script trying to read a file and if the matching string exists, i am putting something in the variable value. i guess it is considering it as a local variable and i am not able to use the value in later part of code.
How to get this value outside?? Please give me all the possible ways
if(...)
then
..........
elif (file exists)
then
cat file | while read line
do
if [ "$line" = "something" ]
then
value="correct"
fi
done
elif()
.........
fi
echo "value is $value"
output:
value is
The while loop is being run in a separate process because it's on the right-side of the pipe, so it can't modify its parent environment. Use redirection instead:
while read line
do
# ...
done < file
Just define value earlier in your code, for instance on your first line:
value=""
if (file exists)
then
cat file | while read line
do
if [ "$line" = "something" ]
then
value="correct"
fi
done
elif()
.........
fi
echo "value is $value"

Reading from an archive using Bourne Shell

I am trying to use Bourne shell scripting for the first time ever, and I cannot seem to figure out determining how to save text from a file to internal script variables. The file format is as follows:
acc.text
Jason Bourne 213.4
Alice Deweger 1
Mark Harrington 312
The current script that I have (which might be ENTIRELY incorrect as I am simply creating it in NotePad++ without using an actual shell console) is as follows:
#!/bin/sh
process_file()
{
FILE = $1;
SALARYARRAY;
NAMEARRAY;
COUNTER = 0;
while read line
do
$NAMEARRAY[$COUNTER] =
$SALARYARRAY[$COUNTER] =
$COUNTER + 1;
echo $NAMEARRAY[$COUNTER]:$SALARYARRAY[$COUNTER];
done < "$FILE"
order_Map
}
# Function is not complete as of now, will later order SALARYARRAY in descending order
order_Map()
{
i = 0;
for i in $COUNTER
do
if ($SALARYARRAY[
done
}
##
# Main Script Body
#
# Takes a filename input by user, passes to process_file()
##
PROGRAMTITLE = "Account Processing Shell Script (APS)"
FILENAME = "acc.$$"
echo $PROGRAMTITLE
echo Please specify filename for processing
read $FILENAME
while(! -f $FILE || ! -r $FILE)
do
echo Error while attempting to write to file. Please specify file for processing:
read $FILENAME
done
echo Processing the file...
process_file $FILENAME
I have fixed some of your script. You need to cut out the name and salary fields from each line before storing them into the array.
#!/bin/bash
process_file()
{
file=$1;
counter=0
while read line
do
#the name is the first two fields
name=`echo $line | cut -d' ' -f1,2`
NAMEARRAY[$counter]="$name"
#the salary is the third field
salary=`echo $line | cut -d' ' -f3`
SALARYARRAY[$counter]="$salary"
echo ${NAMEARRAY[$counter]}:${SALARYARRAY[$counter]}
counter=$(($counter+1))
done < $file
}
##
# Main Script Body
#
# Takes a filename input by user, passes to process_file()
##
PROGRAMTITLE="Account Processing Shell Script (APS)"
echo $PROGRAMTITLE
echo -n "Please specify filename for processing: "
read FILENAME
if [ -r $FILENAME ] #check that the file exists and is readable
then
process_file $FILENAME
else
echo "Error reading file $FILENAME"
fi
Given the file format you specified, each record has three fields first last and amount, then:
i=0
while read fistname lastname amount; do
NAMEARRAY[$i]="$firstname $lastname"
SALARYARRAY[$i]=$amount
i = `expr $i + 1`
done < "$FILE"
The shell read built-in, automatically splits input. See the variable IFS in the man page for sh(1). If you have data after the amount field, and you wish to ignore it, just create another variable after amount; but don't use it. It will collect everything after the 1st 3 fields into the extra variable.
You specified Bourne shell, so I used some pretty antiquated stuff:
i=`expr $x + 1`
is usually written
let $((i++)) # ksh, bash (POSIX, Solaris, Linux)
On modern systems, /bin/sh is usually ksh or something pretty compatible. You can probably use let $((i++))

How can I get my bash script to work?

My bash script doesn't work the way I want it to:
#!/bin/bash
total="0"
count="0"
#FILE="$1" This is the easier way
for FILE in $*
do
# Start processing all processable files
while read line
do
if [[ "$line" =~ ^Total ]];
then
tmp=$(echo $line | cut -d':' -f2)
count=$(expr $count + 1)
total=$(expr $total + $tmp)
fi
done < $FILE
done
echo "The Total Is: $total"
echo "$FILE"
Is there another way to modify this script so that it reads arguments into $1 instead of $FILE? I've tried using a while loop:
while [ $1 != "" ]
do ....
done
Also when I implement that the code repeats itself. Is there a way to fix that as well?
Another problem that I'm having is that when I have multiple files hi*.txt it gives me duplicates. Why? I have files like hi1.txt hi1.txt~ but the tilde file is of 0 bytes, so my script shouldn't be finding anything.
What i have is fine, but could be improved. I appreciate your awk suggestions but its currently beyond my level as a unix programmer.
Strager: The files that my text editor generates automatically contain nothing..it is of 0 bytes..But yeah i went ahead and deleted them just to be sure. But no my script is in fact reading everything twice. I suppose its looping again when it really shouldnt. I've tried to silence that action with the exit commands..But wasnt successful.
while [ "$1" != "" ]; do
# Code here
# Next argument
shift
done
This code is pretty sweet, but I'm specifying all the possible commands at one time. Example: hi[145].txt
If supplied would read all three files at once.
Suppose the user enters hi*.txt;
I then get all my hi files read twice and then added again.
How can I code it so that it reads my files (just once) upon specification of hi*.txt?
I really think that this is because of not having $1.
It looks like you are trying to add up the totals from the lines labelled 'Total:' in the files provided. It is always a good idea to state what you're trying to do - as well as how you're trying to do it (see How to Ask Questions the Smart Way).
If so, then you're doing in about as complicated a way as I can see. What was wrong with:
grep '^Total:' "$#" |
cut -d: -f2 |
awk '{sum += $1}
END { print sum }'
This doesn't print out "The total is" etc; and it is not clear why you echo $FILE at the end of your version.
You can use Perl or any other suitable program in place of awk; you could do the whole job in Perl or Python - indeed, the cut work could be done by awk:
grep "^Total:" "$#" |
awk -F: '{sum += $2}
END { print sum }'
Taken still further, the whole job could be done by awk:
awk -F: '$1 ~ /^Total/ { sum += $2 }
END { print sum }' "$#"
The code in Perl wouldn't be much harder and the result might be quicker:
perl -na -F: -e '$sum += $F[1] if m/^Total:/; END { print $sum; }' "$#"
When iterating over the file name arguments provided in a shell script, you should use '"$#"' in place of '$*' as the latter notation does not preserve spaces in file names.
Your comment about '$1' is confusing to me. You could be asking to read from the file whose name is in $1 on each iteration; that is done using:
while [ $# -gt 0 ]
do
...process $1...
shift
done
HTH!
If you define a function, it'll receive the argument as $1. Why is $1 more valuable to you than $FILE, though?
#!/bin/sh
process() {
echo "doing something with $1"
}
for i in "$#" # Note use of "$#" to not break on filenames with whitespace
do
process "$i"
done
while [ "$1" != "" ]; do
# Code here
# Next argument
shift
done
On your problem with tilde files ... those are temporary files created by your text editor. Delete them if you don't want them to be matched by your glob expression (wildcard). Otherwise, filter them in your script (not recommended).

Resources