Starting bash scripting - linux

I need to teach myself bash scripting. I'm reading this ebook and it has the following code:
#!/bin/bash
# hello.sh
# This is my first shell script!
declare -rx SCRIPT="hello.sh"
declare -rx who="/usr/bin/who"
declare -rx sync="/bin/sync"
declare -rx wc="/usr/bin/wc"
# sanity checks
if test -z "$BASH" ; then
printf "$SCRIPT:$LINENO: please run this script with the BASH shell\n" >&2
exit 192
fi
if test ! -x "$who" ; then
printf "$SCRIPT:$LINENO: The command $who is not available - aborting\n" >&2
exit 192
fi
if test ! -x "$sync" ; then
printf "$SCRIPT:$LINENO: the command $sync is not available - aborting\n">&2
exit 192
fi
if test ! -x "$wc" ; then
printf "$SCRIPT:$LINENO: the command $wc is not available - aborting\n" >&2
exit 192
fi
USERS = `$who | $wc -l`
if [ $USERS -eq 0 ] ; then
$sync
fi
exit 0
When I run it, I get the following error:
hello.sh: line 32: USERS: command not found
hello.sh: line 33: [: -eq: unary operator expected
I don't really know what I'm doing wrong. Am I not allowed to assign USERS to a the output of a command line in that fashion? If I run that line in the command line, it doesn't work either. Any ideas?
Thanks

remove the spaces around the assignment =:
USERS=`$who | $wc -l`
Or it will be interpreted as a command USERS with the two parameters = and `%who | $wc -l`

Replace
USERS = `$who | $wc -l`
with
USERS=`$who | $wc -l`

In Bash (in fact in many shells) you can't have spaces between a variable name and the symbol =
in this case you need to write
USERS=`command`
or
USERS=$(command)
A variable sometimes act as a C++ Macro. If the variable USERS are empty and you type this:
if [ $USERS -eq 0 ] ; then
it will be interpreted like
if [ -eq 0 ] ; then
and the -eq is not a unary operator. to make it right you need write:
if [ "$USERS" -eq 0 ] ; then
to be interpreted
if [ "" -eq 0 ] ; then

Related

Linux Bash script isnt printing out correctly

GOAL: My goal in this assignment is to create a script that will take in a student id as an input and will output a matching student's name OR an error message saying there is none by that name in this class. Im fairly new to Linux and it is kinda tough for me but I would love all the help I can get. Thanks!
Screenshot Page 1 of assignment
Screenshot Page 2 of assignment
My script is printing off everyones name in the file rather than just the one I am searching for.
#!/bin/bash
# findName.sh
searchFile="/acct/common/CSCE215-Fall17"
if [[ $1 = "" ]] ; then
echo "Sorry that person is not in CSCE215 this semester"
exit 2
fi
while read LINE
do
firstNameIndex=0
middleNameIndex=1
lastNameIndex=2
userIDIndex=3
IFS=', ' read -r -a lineArray <<< "$LINE"
if [[ $1 -eq ${lineArray[$userIDIndex]} ]] ; then
echo ${lineArray[$firstNameIndex]} ${lineArray[$middleNameIndex]} ${lineArray[$lastNameIndex]}
fi
done < "$searchFile"
VERSION 3:
Here is how I would do it with grep. This prevents you from looping through the input file.
#!/bin/bash
searchFile="sample.txt"
function notincourse()
{
echo "Sorry that person is not in CSCE215 this semester"
exit 2
}
# Verify arguments, 1 argument, name to search for
if [ $# -ne 1 ]
then
echo "findName.sh <NAME>"
exit 1
else
searchfor=$1
fi
# Verify if the name is in the file
nameline=$(grep $searchfor $searchFile)
#if [ $(echo $nameline | wc -l) -eq 0 ]
if [ $? -eq 1 ]
then
notincourse
else
idvalue=$(echo $nameline | cut -d',' -f1)
if [ "$idvalue" == "$searchfor" ]
then
IFS=', ' read -r -a lineArray <<< "$nameline"
echo ${lineArray[1]} ${lineArray[2]} ${lineArray[3]}
else
notincourse
fi
fi
I tried if with the following test input file:
111, firstname1, middlename1, lastname1
222, firstname2, middlename2, lastname2
333, firstname3, middlename3, lastname3
VERSION 3: it now verifies that the id is indeed the first word in the line. I realized that if the student id is someown included in his name (ya, but better safe than sorry!) my grep would return true!
One line of code to change:
if [[ "$1" == "${lineArray[$userIDIndex]}" ]] ; then

Read string and convert to INT (BASH)

I have a simple script in Bash to read a number in a file and then compare it with a different threshold. The output is this:
: integer expression expected
: integer expression expected
OK: 3
My code is this:
#!/bin/bash
wget=$(wget http://10.228.28.8/ -O /tmp/wget.txt 2>/dev/null)
output=$(cat /tmp/wget.txt | awk 'NR==6')
#output=7
echo $output
if [ $output -ge 11 ];then
echo "CRITICAL: $output"
exit 2
elif [ $output -ge 6 ] && [ $output -lt 11 ];then
echo "WARNING: $output"
exit 1
else
echo "OK: $output"
exit 0
fi
rm /tmp/wget.txt
I know what is the problem, I know that I'm reading a string and I try to compare a int. But I don't know how can I do to read this file and convert the number to read in a int var..
Any ideas?
The problem occurs when $output is the empty string; whether or not you quote the expansion (and you should), you'll get the integer expression required error. You need to handle the empty string explictly, with a default value of zero (or whatever default makes sense).
wget=$(wget http://10.228.28.8/ -O /tmp/wget.txt 2>/dev/null)
output=$(awk 'NR==6' < /tmp/get.txt)
output=${output:-0}
if [ "$output" -ge 11 ];then
echo "CRITICAL: $output"
exit 2
elif [ "$output" -ge 6 ];then
echo "WARNING: $output"
exit 1
else
echo "OK: $output"
exit 0
fi
(If you reach the elif, you already know the value of $output is less than 11; there's no need to check again.)
The problem also occurs, and is consistent with the error message, if output ends with a carriage return. You can remove that with
output=${output%$'\r'}
There are a couple of suggestions from my side regarding your code.
You could explicitly tell bash the output is an integer
declare -i output # See [1]
Change
output=$(cat /tmp/wget.txt | awk 'NR==6') # See [2]
may be better written as
output=$(awk 'NR==6' /tmp/wget.txt )
Change
if [ $output -ge 11 ]
to
if [ "0$output" -ge 11 ] # See [4]
or
if (( output >= 11 )) # Better See [3]
References
Check bash [ declare ].
Useless use of cat. Check [ this ]
Quoting [ this ] answer :
((...)) enable you to omit the dollar signs on integer and array variables and include spaces around operators for readability. Also empty variable automatically defaults to 0 in such a statement.
The zero in the beginning of "0$output" help you deal with empty $output
Interesting
Useless use of cat is a phrase that has been resounding in SO for long. Check [ this ]
[ #chepner ] has dealt with the empty output fiasco using [ bash parameter expansion ] in his [ answer ], worth having a look at.
A simplified script:
#!/bin/bash
wget=$(wget http://10.228.28.8/ -O /tmp/wget.txt 2>/dev/null)
output=$(awk 'NR==6' </tmp/wget.txt )
output="$(( 10#${output//[^0-9]} + 0 ))"
(( output >= 11 )) && { echo "CRITICAL: $output"; exit 2; }
(( output >= 6 )) && { echo "WARNING: $output"; exit 1; }
echo "OK: $output"
The key line to cleanup any input is:
output="$(( 10#${output//[^0-9]} + 0 ))"
${output//[^0-9]} Will leave only digits from 0 to 9 (will remove all non-numeric chars).
10#${output//[^0-9]} Will convert output to a base 10 number.
That will correctly convert numbers like 0019
"$(( 10#${output//[^0-9]} + 0 ))" Will produce a zero for a missing value.
Then the resulting number stored in output will be compared to limits and the corresponding output will be printed.
In BASH, It is a good idea to use double brackets for strings:
if [[ testing strings ]]; then
<whatever>
else
<whatever>
fi
Or double parenthesis for integers:
if (( testing ints )); then
<whatever>
else
<whatever>
fi
For example try this:
var1="foo bar"
if [ $var1 == 'foo bar' ]; then
echo "ok"
fi
Result:
$ bash: [: too many arguments
Now, this:
var2="foo bar"
if [[ $a == "foo bar" ]]; then
echo "ok"
fi
Result:
ok
For that, your code in BASH:
if [[ $output -ge 11 ]]; then
echo "CRITICAL: $output"
exit 2
elif [[ $output -ge 6 ]]; then
echo "WARNING: $output"
exit 1
else
echo "OK: $output"
exit 0
fi

Bash scripts errors

I'm trying to run those scripts but I keep receiving errors messages:
1-
#!/bin/bash
filename=$1
if [ -f $filename ]
then
owner=`stat -c %U $filename`
grep $owner /etc/passwd
if [ $? -eq 0 ]; then
perm=`stat -c %a $filename | head -c 1`
if [ $perm -gt 3 ]; then
cat $filename | grep NOTE
fi
fi
fi
the error message is :
stat: missing operand Try `stat --help' for more information.
2-
#!/bin/bash
NoSum=$1
sum=0
echo "Please enter $NoSum values one at a time"
for (( i=1; i<=$NoSum; i++ ))
do
echo "Next Value?"
read num
let "a = $sum + $num"
sum=$a
done
echo "The sum is : $sum"
the error message is:
Please enter values one at a time ./scr3: line 6: ((: i<=: syntax
error: operand expected (error token is "<=") The sum is : 0
3-
#!/bin/bash
dir=$1
if [ -d $dir ]
then
perm=`stat -c %a $dir | head -c 1`
if [ $perm -gt 5 ]; then
cd $dir
for file in $dir/*
do
if ! [ -x "$file" ]
then
echo "$file"
fi
done
fi
fi
the error message is:
stat: missing operand Try `stat --help' for more information. ./scr4:
line 8: [: -gt: unary operator expected
any idea how to fix them ?
Nothing is wrong about the programs.You are not supplying the command line arguments.You must run it as
1 and 3:
./script.sh <filename>
2:
./script.sh <number>
$1 stands for the first command line argument
You need to quote variables in bash to prevent word-splitting issues, both in test brackets [] and most of the time in other use.
So your first script would be
#!/bin/bash
filename="$1"
if [ -f "$filename" ]
then
owner="`stat -c %U "$filename"`"
grep "$owner" /etc/passwd
if [ $? -eq 0 ]; then
perm="`stat -c %a "$filename" | head -c 1`"
if [ "$perm" -gt 3 ]; then
cat "$filename" | grep NOTE
fi
fi
fi
The others have similar erros

Bash, How to check command output and proceed

I have a bash script that rsyncs my files to my remote server. I want to check if there are lines with "import ipdb;ipdb.set_trace()" before syncing, but I can't. This is my script:
#!/bin/bash;
var="$1" ;
if [ "$var" == "main" -o "$var" == "all" ] ; then
echo "*** checking of ipdb lines, first ***" ;
res=$(ack-grep --type=python "import ipdb" -c -l) ;
if [ $res ] ; then
echo $res ;
else
rsync -Paz --exclude ".*" -e "ssh -i /home/chris/.ssh/thishost-rsync-key" this_pc remote_pc
fi
fi
I always get this type of error:
*** checking of ipdb lines, first ***
sync.sh: line 10: [: too many arguments
sending incremental file list
Line 10 in my script is this one: if [ $res ] ; then
What am I doing wrong?
When you do
res=$(ack-grep --type=python "import ipdb" -c -l) ;
the variable $res is set to contain the output from the command(s) inside $(...).
If you want the resulting exit status use $? instead, otherwise use e.g.
if [ ! -z "$res" ] ...
to check if $res is not empty (meaning that there was output from the command).
Instead of :
if [ $res ] ; then
Use:
if [ ! -z "$res" ] ; then
-z => return True if the length of string is zero.

How to write shell script?

I want to write a shell that runs until something is written to a file (by another process). I have written this:
PID_FILE=log.txt
DONE=0
while [$DONE -eq 0]
do
cat $PID_FILE | while read LINE
do
if [$LINE -neq ""]; then
echo "Do stuff here"
$DONE=1
fi
done
done
echo "DONE"
echo "">$PID_FILE
but I get
test.sh: 3: test.sh: [0: not found
DONE
This line:
while [$DONE -eq 0]
Needs spaces around the square brackets:
while [ $DONE -eq 0 ]
As does this one:
if [$LINE -neq ""]; then
Like this:
if [ $LINE -neq "" ]; then
It helps when you know that \[ is a command. See Why should be there a space after '[' and before ']' in the Bash Script for an explanation.

Resources