Bash shell if operator - zsh

I write a function like this to .commands file, and I imported .commands in .zshrc
function testA() {
echo "start"
if [ "$1" == "a" ]; then
echo "ok"
fi
echo "end"
}
And when I run testA a in terminal
start
testA:2: = not found
What is the problem here?

Chapter 12 – "Conditional Expressions" of the zsh documentation states:
A conditional expression is used with the [[ compound command to test attributes of files and to compare strings.
This means, changing your conditional expression to use [[ ... ]] instead of [ ... ] should make it work:
function testA() {
echo "start"
if [[ "$1" == "a" ]]; then
echo "ok"
fi
echo "end"
}

You can simplify the problem to simply type a [a == a] or echo ==. This will also produce this error. The reason is that a = has specific meaning to zsh, unless it is followed by a white space.
You have three possible workarounds:
Either quote that parameter, i.e.
[ $1 "==" a ]
or use a single equal sign, i.e.
[ $1 = a ]
or use [[, which introduces a slightly different parsing context:
[[ $1 == a ]]

$ cat fun.sh
function test() {
echo "start"
if [ "a" == "a" ]; then
echo "ok"
fi
echo "end"
}
Source script file
$ source fun.sh
Output:
$ test
start
ok
end

Related

Loop over environment variables in POSIX sh

I need to loop over environment variables and get their names and values in POSIX sh (not bash). This is what I have so far.
#!/usr/bin/env sh
# Loop over each line from the env command
while read -r line; do
# Get the string before = (the var name)
name="${line%=*}"
eval value="\$$name"
echo "name: ${name}, value: ${value}"
done <<EOF
$(env)
EOF
It works most of the time, except when an environment variable contains a newline. I need it to work in that case.
I am aware of the -0 flag for env that separates variables with nul instead of newlines, but if I use that flag, how do I loop over each variable? Edit: #chepner pointed out that POSIX env doesn't support -0, so that's out.
Any solution that uses portable linux utilities is good as long as it works in POSIX sh.
There is no way to parse the output of env with complete confidence; consider this output:
bar=3
baz=9
I can produce that with two different environments:
$ env -i "bar=3" "baz=9"
bar=3
baz=9
$ env -i "bar=3
> baz=9"
bar=3
baz=9
Is that two environment variables, bar and baz, with simple numeric values, or is it one variable bar with the value $'3\nbaz=9' (to use bash's ANSI quoting style)?
You can safely access the environment with POSIX awk, however, using the ENVIRON array. For example:
awk 'END { for (name in ENVIRON) {
print "Name is "name;
print "Value is "ENVIRON[name];
}
}' < /dev/null
With this command, you can distinguish between the two environments mentioned above.
$ env -i "bar=3" "baz=9" awk 'END { for (name in ENVIRON) { print "Name is "name; print "Value is "ENVIRON[name]; }}' < /dev/null
Name is baz
Value is 9
Name is bar
Value is 3
$ env -i "bar=3
> baz=9" awk 'END { for (name in ENVIRON) { print "Name is "name; print "Value is "ENVIRON[name]; }}' < /dev/null
Name is bar
Value is 3
baz=9
Maybe this would work?
#!/usr/bin/env sh
env | while IFS= read -r line
do
name="${line%%=*}"
indirect_presence="$(eval echo "\${$name+x}")"
[ -z "$name" ] || [ -z "$indirect_presence" ] || echo "name:$name, value:$(eval echo "\$$name")"
done
It is not bullet-proof, as if the value of a variable with a newline happens to have a line beginning that looks like an assignment, it could be somewhat confused.
The expansion uses %% to remove the longest match, so if a line contains several = signs, they should all be removed to leave only the variable name from the beginning of the line.
Here an example based on the awk approach:
#!/bin/sh
for NAME in $(awk "END { for (name in ENVIRON) { print name; }}" < /dev/null)
do
VAL="$(awk "END { printf ENVIRON[\"$NAME\"]; }" < /dev/null)"
echo "$NAME=$VAL"
done

Unix Scripting if statement correction

im trying to check if my script ran correctly if not, to echo a message, however when i use this if statement, it produces an error on line '9' (if [ $? -eq 0 ]) Saying that a ' is missing.
#!/bin/bash
name=$1
if ["$name" = ""]
then
echo -n "Enter a name to search for: "
read name
fi
if [ $? -eq 0 ]
then
echo "Incorrect Input"
fi
if ["$name" = ""]
is wrong because you must have a space between the [ and the expression (and before the ] as well).
There's nothing obviously wrong with the syntax of
if [ $? -eq 0 ]
but it's always dubious to check exit codes with arithmetic, since that's what if does directly. If your script is as you show it above, the second if statement should always be true. The fact that the previous if statement's expression was false doesn't matter, because the if statement yields zero if no condition tested true. For example:
$ if false; then echo hi; fi; echo $?
0

Shell script arguments

I just started writing shell scripts in Unix so, I am a total newbie
I want to read the arguments given when the user run the script
ex:
sh script -a abc
I want to read for argument -a user gave abc.
My code so far:
if ( $1 = "-a" )
then var=$2
fi
echo $var
I get an error.
Bash uses an external program called test to perform boolean tests, but that program is used mostly via its alias [.
if ( $1 = "-a" )
should become
if [ $1 = "-a" ]
if you use [ or
if test $1 = "-a"
#!/bin/sh
if [ $1 = "-a" ]; then
var=$2
fi
echo $var
You shoud be careful of the space between if and [

Location=$1? what does it mean?

#!/bin/bash
LOCATION=$1
FILECOUNT=0
DIRCOUNT=0
if [ "$#" -lt "1" ]
then
echo "Usage: ./test2.sh <directory>"
exit 0
fi
I don't actually get what the If statement is saying can anyone help me to explain this?Thank you
$1 refers to the first argument of the bash file. In this case, you can pass your directory path by issuing the following command:
# ./test2.sh /path/of/your/directory
#!/bin/bash
LOCATION=$1 #first argument of the script
FILECOUNT=0
DIRCOUNT=0
if [ "$#" -lt "1" ] #if the number of argument(s) ($#) is less than 1
then
echo "Usage: ./test2.sh <directory>"
exit 0
fi
You can read this article for more information about parameter passing.
Hope it helps.
$1 is the first argument that is passed to the bash script. If you start the script like ./test2.sh argument1 argument2 the $1 will refer argument1.
The if-statement checks, if the count of arguments (that's the $#) is smaller than 1, then it will output the usage statement (as it seems you can't run the script without any argument).

Validating Variables in Shell Script

What is the best way to make sure that all the environment variables I need for my script have been set?
Currently, I have multiple if-statements which is not very neat:
if [ -z "$VAR1" ]
then
echo VAR1 not set
exit
fi
if [ -z "$VAR2" ]
then
echo VAR2 not set
exit
fi
if [ -z "$VAR3" ]
then
echo VAR3 not set
exit
fi
Is there a better way?
You can use indirection:
vars="FOO BAR"
for var in $vars
do
[[ -z ${!var} ]] &&
echo "Variable ${var} is empty" ||
echo "The value of variable ${var} is ${!var}"
done
Use a for loop in (set of variables u want). Won't that work?
I have this function in my shell library:
# Check that a list of variables are set to non-null values.
# $#: list of names of environment variables. These cannot be variables local
# to the calling function, because this function cannot see them.
# Returns true if all the variables are non-null, false if any are null or unset
varsSet()
{
local var
for var ; do
eval "[ -n \"\$$var\" ] || return 1"
done
}
I use it in code like this:
varsSet VAR1 VAR2 VAR3 || <error handling here>
you can short them a lot:
[ -z "$FOO" ] && echo "FOO is empty"
[ -z "$BAR" ] && echo "BAR is empty"
a better way:
${FOO:?"FOO is null or not set"}
${BAR:?"BAR is null or not set"}
Of course if the number of variables you are going to test is not low, looping as suggested #Aviator maybe useful to avoid repeating code.
Upon #Aviator answer, I'd like to suggest to define a well commented variable containing a list of your variables-to-be-tested. This way you don't make your code cryptic.
TEST_FOR_IS_SET="FOO BAR BAZ"

Resources