Script not working getting a "Not Valid Identifier" - linux

Am doing this for a class, but am having issues with it. I am new to Linux and really having a hard time. Am trying input 3 values (M, R, T), figure out if they are greater, less than or equal to 2000 and print a statement. Not sure I am doing it right. I get the questions and can input, but am not sure if it is completely working.
#!/bin/sh
clear
echo -n "What is the value of M?"
read $M
sleep 3
echo -n "What is the value of R?"
read $R
echo -n "What is the value of T?"
read $T
A=$M+$R+$T
if [ $A > "2000" ]
then
echo "A is over 2000"
else
echo "A is 2000 or less"
fi

There's a few things wrong here. Firstly, read takes the name of the variable without the $. Secondly, you can specify a prompt on the same line, so no need for all the separate echos. Thirdly, in order to do a numerical comparison, you should be using -gt:
#!/bin/sh
clear
read -p "What is the value of M?" M
sleep 3
read -p "What is the value of R?" R
read -p "What is the value of T?" T
A=$((M+R+T)) # different syntax here too
if [ "$A" -gt 2000 ]
then
echo "A is over 2000"
else
echo "A is 2000 or less"
fi
If you are using bash, another way to compare integers in bash is to use an arithmetic context:
if (( A > 2000 ))
Remember to change the shebang to #!/bin/bash if you want to use bash features.

Related

Comparing value string and fixed value

I wrote small script under Debian Linux 11 that should check how many instances of application is currently running and what is power usage of GPU cards.
I save it under name test , and she is started every time I access instance over SSH
#!/bin/sh
clear
a=$(nvidia-smi -q -i 0 | grep "Power Draw" | cut -c45-50)
b=$(nvidia-smi -q -i 1 | grep "Power Draw" | cut -c45-50)
c=$(nvidia-smi -q -i 2 | grep "Power Draw" | cut -c45-50)
d=$(nvidia-smi -q -i 3 | grep "Power Draw" | cut -c45-50)
zet=$( echo "$a" + "$b" + "$c" + "$d" | bc -l )
echo "SYSTEM DRAW:" "$zet"
if [ "${zet}" -gt 150 ]; then
echo WARRNING - SYSTEM DRAW LOW
else
echo OK
fi
sleep 8
exit
All I need to add is this line
x=${x%.*}
That convert decimal number in number without decimals and script works perfect.
You could add set -x right before the part you want to debug, which will show you a debug of what is happening in bash. and stop it by inserting after that set +x
like:
set -x
power=$150
echo "SYSTEM DRAW :" $total
if [ $total \> $power ] ; then # escape > otherwise it redirects output
I do not think you are setting the value of $150
The script might be failing if one of the compared values is not set.. so you should initialize your variables to be let' say equal to 0 as a default at the beginning of your script, or via bash
like:
power=${150:-10} # if $150 does not have a value or empty, the default value of $power will be set to `10`
So many possible issues but you can compare possibly decimal values using bc:
if [ "$(echo "$total > $power" | bc)" = 1 ]; then
One problem is that [ (and [[) do string comparisons, and you want a numeric comparison. For that you want to use ((, so something like
if (( $total > 150 )); then
echo "WARNING..."
else
echo "OK"
fi
will work better. Otherwise a total of 1000 would print ok, and 90 would give a warning.
Other problems:
$150 gives you the value of a variable called 150 -- you probably want to remove the $
Outside of special forms like [[ and ((, a > will do an output redirection, rather than being a 'normal' argument to a command such as [.
As the comments recommend, use shellcheck, however, I think your intention is not what you wrote.
Try this, create a script (i.e. myscripy)
#! /bin/bash
power=$150
echo "power=$power"
then run it
./myscript abc
and prints
power=abc50
which is probably very different than what you expect.
That is because power will take the first argument's value ($1) and append 50.
If you wanted argument number 150 (also very unlikely), you should write
power=${150}
but if you want just the number
power=150
edit
based on the comment, use
zet=$(bc <<<"$a+$b+$c+$d")
to calculate zet if the values are floating points.
For the comparison use
if [ "$(bc <<<"$zet>150")" == 1 ]; then
...
fi

I want to pipe the output of one script to different script that will process the output of the first script independently?

I have a very trivial bash script taking input from the user in the first step and then echo an output. I want to run the same script in different shells and let the first shell take input and echo its output and send it to the input of the other shell, and let the both of shells continue executing normally after that.
I have read many answers about exporting variables from shell to shell, like getting the name of the shell using tty and then redirect the output of the first terminal session to the second terminal session, that works only when executing single commands, but not mid executing of the two scripts.
This is the first script:
answer="n"
while [ "$answer" != 'y' ];do
echo "enter the first value :"
read first
echo "the output is: "
echo 6
echo "enter value of A:"
read A
echo "do you want to exit"
read answer
done
The second script is the same:
answer="n"
while [ "$answer" != 'y' ];do
echo "enter the first value :"
read first
echo "the output is: "
echo 6
echo "enter value of A:"
read A
echo "do you want to exit"
read answer
done
I want the first script running in the first terminal to output the number 6 and then pipe the number to the second script to be placed in the variable first and then let the two scripts continue executing in their respective terminals.
A named pipe is the appropriate tool. Thus, in the first script:
#!/usr/bin/env bash
my_fifo=~/.my_ipc_fifo
mkfifo "$my_fifo" || exit
exec {out_to_fifo}>"$my_fifo" || exit
answer="n"
while [ "$answer" != 'y' ];do
echo "enter the first value :"
read first
echo "the output is: "
echo 6 # one copy for the user
printf '%s\0' 6 >&$out_to_fifo # one copy for the other program
echo "enter value of A:"
read A
printf '%s\0' "$A" >&$out_to_fifo
echo "do you want to exit"
read answer
done
...and in the second:
#!/usr/bin/env bash
my_fifo=~/.my_ipc_fifo
exec {in_from_fifo}<"$my_fifo" || exit # note that the first one needs to be started first!
while IFS= read -r -d '' first <&"$in_from_fifo"; do
echo "Read an input value from the other program of: $first"
read -r -d '' second <&"$in_from_fifo"
echo "Read another value of: $second"
read -p "Asking the user, not the FIFO: Do you want to exit? " exit_answer
case $exit_answer in [Yy]*) exit;; esac
done

bash shell script concatenate string with period char

I am trying to create following string
Beta-3.8.0
but shell script always omits the . period char no matter what I do.
echo "$readVersion"
if [ -z $readVersion ]
then
echo "readVersion is empty"
exit 1
fi;
IFS=.
set $readVersion
newVersion=$(echo "$2 + 1" | bc)
newBranch="Beta-$1.$newVersion.$3"
echo $newBranch
prints:
3.8.0
Beta-3 9 0
I have also tried
newBranch='Beta-'$1'.'$newVersion'.'$3
or
newBranch="Beta-{$1}.{$newVersion}.{$3}"
although this seems printing the right value echo "$1.$newVersion.$3" why not variable doesnt work ?
I need the variable to use later on in the script...
You can save and restore the IFS once you are done.
oldIFS=$IFS
IFS=.
set $readVersion
newVersion=$(echo "$2 + 1" | bc)
IFS=$oldIFS
newBranch="Beta-$1.$newVersion.$3"
echo "$newBranch"
Or you can quote when printing:
echo "$newBranch"
The former is a better idea IMO since it conveys your intention and would make the rest of the code use the "correct" IFS. The latter just circumvents the problem.

How do I deal with empty user input in a Bash script?

When the script asks me for input, I get an error if I just press Return without typing in anything. How do I fix this?
Here's the script:
#!/bin/bash
SUM=0
NUM=0
while true
do echo -n "Pruefungspunkte eingeben ('q' zum Beenden): "
read SCORE
if test "$SCORE" == "q"
then echo "Durchschnittspunktzahl: $AVERAGE."
break
else SUM=`expr $SUM + $SCORE`
NUM=`expr $NUM + 1`
AVERAGE=`expr $SUM / $NUM`
fi
done
How about using good bash practices?
#!/bin/bash
sum=0
num=0
while true; do
read -erp "Pruefungspunkte eingeben ('q' zum Beenden): " score
if [[ $score = q ]]; then
echo "Durchschnittspunktzahl: $average."
break
elif [[ $score =~ ^-?[[:digit:]]+$ ]]; then
((sum+=10#$score))
((++num))
((average=sum/num))
else
echo "Bad number"
fi
done
Good practice:
don't use capitalized variable names
use the [[ builtin instead of the test builtin
don't use backticks, use (( to invoke shell arithmetic
to make sure the user inputs a number, check that a number was really entered. The line
elif [[ $score =~ ^-?[[:digit:]]+$ ]]; then
just does that (see regular expressions). Incidentally it completely solves your original problem, since an empty input will not pass through this test
to prevent problems if a user enters 09 instead of 9, force bash to interpret the input in radix 10. That's why I'm using (10#$score) instead of just score.
Use read with the -p (prompt) option, instead of the clumsy combo echo -n / read
This version is much more robust and well-written than yours. Yet, it still has problems:
will break if user needs large numbers
as shell arithmetic is used, only integers can be used. Moreover, the average given by this program is rounded: if you want the average of 1 and 2 you'll have 1.
To fix both problems, you'll probably want to use bc or dc. But that will be the purpose of another question. Or not.
Initialise $SCORE beforehand or handle empty input like you do in q case.
[[ -z "$SCORE" ]] && echo "\$SCORE is zero, e.g. \"\""
This will test if the variable SCORE is empty string.
You should also set AVERAGE=0 at the beginning.

looking for a command to tentatively execute a command based on criteria

I am looking for a command (or way of doing) the following:
echo -n 6 | doif -criteria "isgreaterthan 4" -command 'do some stuff'
The echo part would obviously come from a more complicated string of bash commands. Essentially I am taking a piece of text from each line of a file and if it appears in another set of files more than x (say 100) then it will be appended to another file.
Is there a way to perform such trickery with awk somehow? Or is there another command.. I'm hoping that there is some sort of xargs style command to do this in the sense that the -I% portion would be the value with which to check the criteria and whatever follows would be the command to execute.
Thanks for thy insight.
It's possible, though I don't see the reason why you would do that...
function doif
{
read val1
op=$1
val2="$2"
shift 2
if [ $val1 $op "$val2" ]; then
"$#"
fi
}
echo -n 6 | doif -gt 3 ls /
if test 6 -gt 4; then
# do some stuff
fi
or
if test $( echo 6 ) -gt 4; then : ;fi
or
output=$( some cmds that generate text)
# this will be an error if $output is ill-formed
if test "$output" -gt 4; then : ; fi

Resources