Square bracket ( test condition ) stuff in shell and advance case statement - shell

I have two questions. Lets talk about the simpler one 1st and then we'll talk about the case statement.
consider this simple if else
if fgrep -q '= ' sf
then
echo "blanks in file"
else
echo "no blanks"
fi
[[ `fgrep -q '= ' sf` ]] && echo "blanks there"
# rc=$?
# echo "rc is $rc"
the if condition works like a charm. I am trying to rewrite the same stuff using [[ test condition and it does not exactly work. What is wrong in my test condition.
now the 2nd Question
Actually here is what I am trying to do .
There is this kinda case statement I have
source "/path"
# die is a function that will output standard error and return 1
opta=false
optb=false
while getopts ":abf:" opt; do
case $opt in
a ) $optb && die "Cannot specify option a after specifying option b"
opta=true
;;
b ) $opta && die "Cannot specify option b after specifying option a"
optb=true
;;
f) # Pl see the note below for f
\?) die "Invalid option: -$OPTARG. Abort"
;;
esac
done
shift $(($OPTIND - 1))
test $# -eq 0 && die "You must supply SID"
test $# -eq 1 || die "Too many command-line arguments"
SID=$1
the f is a file option. So 1st two are incompatible if -f is used.
myshell -f /path/file1 -a 500
above is not allowed. I can manage this part so don't worry about it too much
Here is the rock that's stumbling me off path .
-f accepts a file path. The file is an manual override file that has various parameters that will override ones set using the source command . So when It comes to f option it should do the following
check if
file path is valid
2.
if it is valid check if there IS "= "viz. equal to followed by blank- then quit.In other words I dont want blank values || ones that ^blank
3
if both these conditions are met then if the search string s parameter is set in the file then there cannot be any positional parameters passed to the command .
e.g. of manual override file below
p1=v1
p2=v2
s=v3
p3=
# some parameters like p3 need to be set in that case it will take the defaults from the source file if those aren't set
In other words e.g the below command
myshell.ksh -f /path/file1 500
is valid if value of s is NOT set in file1 else it should quit giving an error that positional parameter was supplied when override value was already applied in file -f file1
4
export all the parameters that are set in file1 as overrides to the source file parameters.
e.g. source file has
p1=sv1
and in file1 p1 is set then export p1=v1 from file1
I can manage #4. I just need some insight into $3 for the most. 2,1 and 4 won't harm

Question 1
The [[...]] have no business being there. Use:
fgrep -q '= ' sf && echo "blanks there"
The above runs fgrep on sf. If fgrep indicates success, then the echo command is run.
Question 2
If /path/file1 contains a setting for the s variable and there are positional parameters on the command line, then we are supposed to report an error:
grep '^s=' /path/file1 && [ "$#" -gt 0 ] && echo "ERROR: file has s parameter set and there are positional arguments"
The above checks for two conditions to be true and, if they are both true, it prints an error message. The first condition is:
grep '^s=' /path/file1
The is true if the file /path/file1 has a line that begins with the characters s=. (^ signifies the beginning of a line.) The second condition is:
[ "$#" -gt 0 ]
This return true if the number of positional parameters is greater than zero. If both of those conditions are true, then the echo statement above is executed.
Question 2: Alternate Approach
In this case, we suppose that the variable $filepath has the path and name of the file, such as /path/file1, which contains shell commands for setting variables. The following checks to see if that file is readable. If it is, then it sources all the commands in that file. The next line checks to see if s has been set. If it has and if there are still positional parameters, then it prints a message:
[ -r "$filepath" ] && source "$filepath" # Set all override variables
[ -n "$s" ] && [ "$#" -gt 0 ] && echo "message"
In the test ([...]) statements, note that $filepath and $s are enclosed in double-quotes. This prevents an error should either value be empty. It also prevents errors if the value of filepath contains spaces.
To source a file, it needs not merely to exist but also to be readable. Therefore, the first test above checks for readability (-r) instead of mere existence (-f).
MORE
To check if the source file has any uncommented lines with variables set to empty values:
grep -qE '^[^#]+=$' file1 && echo "message"
In the above ^ matches the beginning of a line.
The regular expression works because [^#] matches any character that is not a hash sign. Since plus sign means one or more of the preceding character, the [^#]+ means a string of one or more characters, none of which are hash signs. Outside of square brackets, a ^ matches the start of a line. So, ^[^#]+ matches any string of non-hash characters starting at the beginning of the line. ^[^#]+= matches if those characters are then followed by an equal sign. Since $ matches the end of a line, then ^[^#]+=$ matches if the line starts with one or more non-hash characters, followed by an equal sign, followed by nothing (the end of the line). Thus, it matches if some variable has its value set to nothing.

Related

Bash if statement not working properly

I have a bash statement to test a command line argument. If the argument passed to the script is "clean", then the script removes all .o files. Otherwise, it builds a program. However, not matter what is passed (if anything), the script still thinks that the argument "clean" is being passed.
#!/bin/bash
if test "`whoami`" != "root" ; then
echo "You must be logged in as root to build (for loopback mounting)"
echo "Enter 'su' or 'sudo bash' to switch to root"
exit
fi
ARG=$1
if [ $ARG == "clean" ] ; then
echo ">>> cleaning up object files..."
rm -r src/*.o
echo ">>> done. "
echo ">>> Press enter to continue..."
read
else
#Builds program
fi
Answer for first version of question
In bash, spaces are important. Replace:
[ $ARG=="clean" ]
With:
[ "$ARG" = "clean" ]
bash interprets $ARG=="clean" as a single-string. If a single-string is placed in a test statement, test returns false if the string is empty and true if it is non-empty. $ARG=="clean" will never be empty. Thus [ $ARG=="clean" ] will always return true.
Second, $ARG should be quoted. Otherwise, if it is empty, then the statement reduces to `[ == "clean" ] which is an error ("unary operator expected").
Third, it is best practices to use lower or mixed case for your local variables. The system uses upper-case shell variables and you don't want to accidentally overwrite one of them.
Lastly, with [...], the POSIX operator for equal, in the string sense, is =. Bash will accept either = or == but = is more portable.
first:
Every string must double quoted or will error absent argument.
second:
for string used only = or != not a == and also -n and -z commands.
third:
you may combine conditions by -a and -o commands but newer used enclose in () yous conditions so not to get error. Logical operators acts through operators presidence, fist calculate -o operator and then -a! For example
[ -n "$1" -a $1 = '-h' -o $1 = '--help' ] && { usage; exit 0; }
will work when passed to script at least 1 argument and is -h or --help. All spaces must be!!! Bush do short cycle logical evaluations. So don't trouble for case when $1 don't exist in second condition because of result of this expression is determined in first one. next don't calculate in this case. But if your argument may contains space symbols you need it double quote. You must do it also in command line too! Else you get error in script or split your arguments in two or more parts.
Operator == isn't used in test. For numbers(not siring) used -eq or -ne commands. See man 1 test for full descriptions. test EXPRESSION... equivalent of [ EXPRESSIONS... ]. More shirt form of test.

What does the operator != mean in a shell script?

In this i have an exclamation mark in my second if statement why is this used.
#!/bin/bash
name=$1
if [ "$name" = "" ]
then echo -n "Enter a name to search for: "
read name
fi
grep -i cheryl ~uli101/uli101/phonebook
grep -i $name ~uli101/uli101/phonebook
if [ "$?" != "0" ]
then echo -n "Name '$name' not in directory "
fi
The special shell parameter $? contains the exit code from the last command run. Every command you run from the shell reports a numeric status back to the shell when it finishes running; in general, a value of 0 means the command succeeded, and a nonzero value means it failed.
The grep command searches a file for lines matching a pattern. If it finds any matching lines, it prints them out, but it also exits with status 0 if it found at least one match, and a nonzero status if it didn't find any.
The syntax [ expression ] is a command that evaluates the given expression (usually a comparison of some sort) to see if it's true or not. Really, it's just another shell command, that exits with status 0 if the expression is true and 1 if it's false; the if construct in the shell decides what to do based on the value of $?.
And the != operator means 'is not equal to', so [ $? != 0 ] is checking to see if $? is not equal to zero.
Putting all that together, the above code checks to see if the grep found a match or not.
The origin of != is the C family of programming languages, in which the exclamation point generally means "not". In bash, a ! at the start of a command will invert the exit status of the command, turning nonzero values to zero and zeroes to one. So you could also "move the exclamation point" and rewrite the above expression like this:
if ! [ $? == 0 ]
However, since if itself operates based on exit status, all of the above code is doing extra work. You can skip the middleman and just test grep directly:
if ! grep -i "$name" ~uli101/uli101/phonebook; then
echo "Name '$name' not in directory."
fi
Note that I put double quotes around $name, which prevents any spaces in the value from separating it into multiple arguments to grep.
[ "$?" != "0" ] means "$? not equal to 0". See the full list of Bash test operators here.
Note that $? will be set to 0 if grep finds a match, and 1 otherwise.

Bash: How to set a variable from argument, and with a default value

It is pretty clear that with shell scripting this sort of thing can be accomplished in a huge number of ways (more than most programming languages) because of all the different variable expansion methods and programs like test and [ and [[, etc.
Right now I'm just looking for
DIR=$1 or .
Meaning, my DIR variable should contain either what is specified in the first arg or the current directory.
What is the difference between this and DIR=${1-.}?
I find the hyphen syntax confusing, and seek more readable syntax.
Why can't I do this?
DIR="$1" || '.'
I'm guessing this means "if $1 is empty, the assignment still works (DIR becomes empty), so the invalid command '.' never gets executed."
I see several questions here.
“Can I write something that actually reflects this logic”
Yes. There are a few ways you can do it. Here's one:
if [[ "$1" != "" ]]; then
DIR="$1"
else
DIR=.
fi
“What is the difference between this and DIR=${1-.}?”
The syntax ${1-.} expands to . if $1 is unset, but expands like $1 if $1 is set—even if $1 is set to the empty string.
The syntax ${1:-.} expands to . if $1 is unset or is set to the empty string. It expands like $1 only if $1 is set to something other than the empty string.
“Why can't I do this? DIR="$1" || '.'”
Because this is bash, not perl or ruby or some other language. (Pardon my snideness.)
In bash, || separates entire commands (technically it separates pipelines). It doesn't separate expressions.
So DIR="$1" || '.' means “execute DIR="$1", and if that exits with a non-zero exit code, execute '.'”.
How about this:
DIR=.
if [ $# -gt 0 ]; then
DIR=$1
fi
$# is the number of arguments given to the script, and -gt means "greater than", so you basically set DIR to the default value, and if the user has specified an argument, then you set DIR to that instead.
I use a simple helper function to make such assignments look cleaner. The function below accepts any number of arguments, but returns the first one that's not the empty string.
default_value() {
# Return the first non-empty argument
while [[ "$1" == "" ]] && [[ "$#" -gt "0" ]]; do
shift
done
echo $1
}
x=$(default_value "$1" 0)

Bash - if and for statements

I am little unfamiliar with the 'if...then...fi' and the 'for' statements syntax.
Could anyone explain what the "$2/$fn" and "/etc/*release" in the code snippets below mean?...specifically on the use of the forward slash....and the asterisk...
if [ -f "$filename" ]; then
if [ ! -f "$2/$fn" ]; then
echo "$fn is missing from $2"
missing=$((missing + 1))
fi
fi
and
function system_info
{
if ls /etc/*release 1>/dev/null 2>&1; then
echo "<h2>System release info</h2>"
echo "<pre>"
for i in /etc/*release; do
# Since we can't be sure of the
# length of the file, only
# display the first line.
head -n 1 $i
done
uname -orp
echo "</pre>"
fi
} # end of system_info
...thx for the help...
/etc/*release : here the * will match any number of any characters, so any thing /etc/0release , /etc/asdfasdfr_release etc will be matched. Simply stated, it defined all the files in the /etc/ directory which ends with the string release.
The $2 is the 2nd commandline argument to the shell script, and $fn is some other shell variable. The "$2/$fn" after the variable substitutions will make a string, and the [ -f "$2/$fn" ] will test if the string formed after the substitution forms a path to a regular file which is specified by the -f switch. If it is a regular file then the body of if is executed.
In the for loop the loop will loop for all the files ending with the string release in the directory /etc (the path). At each iteration i will contain the next such file name, and for each iteration the first 1 line of the file is displayed with the head command by getting the file name from variable i within the body.
It is better to check the manual man bash and for if condition check man test . Here is a good resource: http://tldp.org/LDP/Bash-Beginners-Guide/html/
The forward slash is the path separator, and the * is a file glob character. $2/$fn is a path where $2 specifies the directory and $fn is the filename. /etc/*release expands to the space separated list of all the files in /etc whose name ends in "release"
Dollar sign marks variable. The "-f" operator means "file exsists".
So,
[ -f "$filename" ]
checks if there is file named the same as value contained in $filename variable.
Simmilar, if we assume that $2 = "some_folder", and $fn = "some_file", expression
[ ! -f "$2/$fn" ]
returns true if file some_folder/some_file doesn't exsist.
Now, about asterisk - it marks "zero or more of any character(s)". So, expression:
for i in /etc/*release; do
will iterate trough all folders named by that pattern, for example:
/etc/release, /etc/666release, /etc/wtf_release...
I hope this helps.

How do I use a file grep comparison inside a bash if/else statement?

When our server comes up we need to check a file to see how the server is configured.
We want to search for the following string inside our /etc/aws/hosts.conf file:
MYSQL_ROLE=master
Then, we want to test whether that string exists and use an if/else statement to run one of two options depending on whether the string exists or not.
What is the BASH syntax for the if statement?
if [ ????? ]; then
#do one thing
else
#do another thing
fi
From grep --help, but also see man grep:
Exit status is 0 if any line was selected, 1 otherwise;
if any error occurs and -q was not given, the exit status is 2.
if grep --quiet MYSQL_ROLE=master /etc/aws/hosts.conf; then
echo exists
else
echo not found
fi
You may want to use a more specific regex, such as ^MYSQL_ROLE=master$, to avoid that string in comments, names that merely start with "master", etc.
This works because the if takes a command and runs it, and uses the return value of that command to decide how to proceed, with zero meaning true and non-zero meaning false—the same as how other return codes are interpreted by the shell, and the opposite of a language like C.
if takes a command and checks its return value. [ is just a command.
if grep -q ...
then
....
else
....
fi
Note that, for PIPE being any command or sequence of commands, then:
if PIPE ; then
# do one thing if PIPE returned with zero status ($?=0)
else
# do another thing if PIPE returned with non-zero status ($?!=0), e.g. error
fi
For the record, [ expr ] is a shell builtin† shorthand for test expr.
Since grep returns with status 0 in case of a match, and non-zero status in case of no matches, you can use:
if grep -lq '^MYSQL_ROLE=master' ; then
# do one thing
else
# do another thing
fi
Note the use of -l which only cares about the file having at least one match (so that grep returns as soon as it finds one match, without needlessly continuing to parse the input file.)
†on some platforms [ expr ] is not a builtin, but an actual executable /bin/[ (whose last argument will be ]), which is why [ expr ] should contain blanks around the square brackets, and why it must be followed by one of the command list separators (;, &&, ||, |, &, newline)
just use bash
while read -r line
do
case "$line" in
*MYSQL_ROLE=master*)
echo "do your stuff";;
*) echo "doesn't exist";;
esac
done <"/etc/aws/hosts.conf"
Below code sample should work:
(echo "hello there" | grep -q "AAA") && [ $? -eq 0 ] && echo "hi" || echo "bye"

Resources