Why is my zshrc function not working when adding arguments? - macos

I am trying to make a function to start, stop or restart from any directory
The code works fine without any arguments but when adding any argument I get the webserver:6: = not found error, when testing the variables everything looks like it should work
function webserver {
#echo $USERNAME
'echo $1
if [ "$1" != "" ]
then
if [ "$1" == "start"]
then
/Users/$USERNAME/start.sh
fi
if [ "$1" == "stop"]
then
/Users/$USERNAME/stop.sh
fi
if [ "$1" == "restart"]
then
/Users/$USERNAME/restart.sh
fi
else
echo "Invalid arguments! Valid arguments are : start stop restart"
fi
}
Why is this code not working?

There's a single-quote before the echo $1 command, which'll cause different trouble. Is that a typo?
The mains problem is that your comparison syntax is wrong; in a [ ] test, use a single = for string equality test, and you need spaces between each syntactic element, including before the final ].
if [ "$1" == "start"] # Bad, will give errors
if [ "$1" = "start" ] # Good, will work as expected
Also, I'd replace that series of if statements with a either a case statement, or a single if ... elif ... elif, since only one branch will ever be taken.
case "$1" in
start)
/Users/$USERNAME/start.sh ;;
stop)
/Users/$USERNAME/stop.sh ;;
restart)
/Users/$USERNAME/restart.sh ;;
*) # This is the case equivalent of "else"
echo "Invalid arguments! Valid arguments are : start stop restart" ;;
esac

like Gordon said, the syntax for zsh is wrong with only one [ ].
according to "==" logical operator and zsh version 5.7.x (installed using Homebrew)
Simple answer: a == is a logical operator only inside [[ … ]] constructs.
And it works also in ksh and bash.
When used outside a [[ … ]] construct a =cmd becomes a filename expansion operator but only in zsh
$ echo ==
zsh: = not found

Related

What's wrong with my bash script? It cannot specify my OS type

#!/bin/bash
if [ ["$OSTYPE" == "linux-gnu"*] ]; then
SCRIPT_PATH=$(dirname $(realpath -s $0))
elif [ ["$OSTYPE" == "darwin"*] ]; then
SCRIPT_PATH=$(dirname $(pwd))
echo "mac!!"
else
echo "Unknown OS!"
exit
fi
I want to write a bash script to specify the OS type.
But on my MacOS, the result shows "Unknown OS!", which is wrong.
I tried echo $OSTYPE in terminal, it shows darwin20.0.
So I wonder what's the problem in my code?
The case statement is specifically intended for comparing a single string against various patterns, and doing different things depending on which it matches:
#!/bin/bash
case "$OSTYPE" in
"linux-gnu"* )
script_path="$(dirname "$(realpath -s "$0")")" ;;
"darwin"* )
script_path="$(dirname "$(pwd)")" ;;
* )
echo "Unknown OS!" >&2
exit 1 ;;
esac
Notes: each pattern is delimited with a ) at the end. You can also put a ( at the beginning, but most people don't bother. Each case ends with a double semicolon. The * case at the end will match anything that didn't match an earlier pattern, so it functions like an else clause in an if ... elif ... statement.
Some other changes I made:
It's a good idea to double-quote variable references and command substitutions (e.g. "$(realpath -s "$0")" instead of just $(realpath -s $0)) to avoid weird parsing problems with some characters (mostly spaces) in values. (There are some places where it's safe to leave the double-quotes off, but it's not worth trying to remember where they are.)
Since there are a whole bunch of all-caps names with special functions, it's safest to use lower- or mixed-case names (e.g. script_path instead of SCRIPT_PATH) to avoid conflicts.
Error and status messages (like "Unknown OS!") should generally be sent to standard error instead of standard output. I used >&2 to redirect the message to standard error.
When a script (or function, or program, or whatever) exits after an error, it should return a nonzero exit status to indicate that it failed. Different codes can be used to indicate different problems, but 1 is commonly used as a generic "something went wrong" code, so I used exit 1 here.
And I recommend using shellcheck.net to scan your scripts for common mistakes. It'll save you a lot of trouble.
Make sure you have no spaces between your opening and closing brackets, i.e., [[ and ]] vs [ [ and ] ] and you may get rid of the quotes in your patterns:
#!/usr/bin/env bash
OSTYPE=linux-gnu-123
if [[ "$OSTYPE" == linux-gnu* ]]; then
echo "linux"
elif [[ "$OSTYPE" == darwin* ]]; then
echo "mac"
else
echo "Unknown OS!"
fi
Also, use https://www.shellcheck.net/ to verify your scripts.
The problem is your attempt checking wildcard expressions via =="..."*. This needs to be done via grep. Try something like this:
#!/usr/bin/env bash
# define method
function checkOS() {
local os="$OSTYPE";
if [[ "$os" == "msys" ]]; then
echo "windows";
elif ( echo "$os" | grep -Eq "^darwin.*$" ); then
echo "mac";
elif ( echo "$os" | grep -Eq "^linux-gnu.*$" ); then
echo "linux";
else
echo "Unknown OS!" >> /dev/stderr;
exit 1;
fi
}
# try method
os="$( checkOS )";
echo -e "Current OS is \033[1m${os}\033[0m.";

Bash == not found

I have a small bash script:
#!/bin/bash
if [[ "$#" == "pull" ]]
then
# stuff
elif [[ "$#" == "push" ]]
then
# stuff
else
echo "Command not recognised"
fi
it's located in /usr/bin/local and I made it executable. However whenever I run it I get script:1: == not found
Any ideas?
This is macOS if that matters.
Don't use [[, not defined by POSIX. Instead use [
Don't use ==, use =
Don't use $#, use $1
Don't use double quotes in this situation for pull and push, matter of fact don't use them at all
Don't use Bash when sh will do
Updated script:
#!/bin/sh
if [ "$1" = pull ]
then
# stuff
elif [ "$1" = push ]
then
# stuff
else
echo 'Command not recognised'
fi
Sticking with bash as your interpreter, your only issue is with your uses of "$#", which in tests like bash's [[ and POSIX's [ and test, expands to all arguments surrounded by quotes (just like "$*"). You probably want "$1" to test just the first argument.
You can also consider using a case (switch) statement:
#!/bin/bash
case "$1" in
( pull ) echo "you said pull" ;;
( push ) echo "you said push" ;;
( * ) echo "Command '$1' is not recognised" ;;
esac
(The above code will work in bash, sh, and zsh. I assume you still require bash due to other aspects of your code.)

getopts in bash, script was working before and now I'm baffled

So I have a couple of getopts in my bash script. Here's an example of a working one.
FOUND=
SEARCH=
COUNT=0
while getopts "ips:flenkc" OPTION
do
case $OPTION in
i)
FOUND=1
let "COUNT++"
;;
p)
FOUND=2
let "COUNT++"
;;
s)
FOUND=3
SEARCH=$OPTARG
let "COUNT++"
;;
esac
done
Later on a case statement that checks to see if count=1 (meaning, only one of the following, i, p, and s, are used in the call) Not important except that it determines the main action being done.
Now the getopts thing in question. This was working before, and now it's not. The goal is to make it so that if someone wants to input data, they can do so with the following bash command.
./programname -i -f Mary -l Sue -e smary#email.com -n 555-555-5555
Where, when -i is used, we must have -f, -l, -e, and -n (for first name, last name, e-mail, and number).
The code I was using: Warning, code is full of syntax errors. If you're learning bash, I highly recommend you do not use anything you see here in my post.
if [ $FOUND == "1" ]
then
echo "You have chosen to insert things."
FIRST=
LAST=
EMAIL=
NUMBER=
while getopts "if:l:e:n:" OPTION
do
case $OPTION in
f)
FIRST=$OPTARG
;;
l)
LAST=$OPTARG
;;
e)
EMAIL=$OPTARG
;;
n)
NUMBER=$OPTARG
;;
esac
done
if [[ -z $FIRST ]] || [[ -z $LAST ]] || [[ -z $EMAIL ]] || [[ -z $NUMBER ]]
echo "Error!!! Some input is missing!!!"
usage // display usage
exit 1
fi
echo -e $FIRST"\t"$LAST"\t"$EMAIL"\t"$NUMBER >> contacts
fi
Before this program would work, but now, not even a single thing is making it to input for FIRST, LAST, EMAIL, and NUMBER (in my attempts to change the code to see if it was making it to certain steps).
What am I doing wrong with the getopts? It was working fine before, but now.... it's not working at all!
One thing worth noting up front: if your script has already called getopts once, another getopts call will start AFTER all options and therefore effectively do nothing; reset OPTIND to 1 before each subsequent getopts calls to have them reprocess all options.
Your code has both syntax errors and is worth cleaning up in general:
The if [[ -z ... statement was missing a then.
The // after usage would have caused a syntax error - POSIX-like shells use # as the comment char.
Since this is bash script, stick with using [[ ... ]] consistently (no need for [ ... ]) and/or use (( ... )) for arithmetic operations.
Specifically, avoid [ ... == ... ], because it mixes POSIX syntax - [ ... ] - with Bash-specific syntax - == ( POSIX only supports =).
If you do use [ ... ], be sure to double-quote variable references, to be safe.
No need for multiple [[ ... ]] expressions to OR them together - do it in a single [[ ... || ... || ... ]].
It's best to avoid all-uppercase shell-variable names so as to avoid conflicts with environment variables and special shell variables.
Output error messages to stderr, using >&2.
Enclose the entire argument to echo -e in double-quotes to protect variable values from possibly unwanted expansions.
Mere syntax errors can usually be caught using shellcheck.net.
Putting it all together, we get:
#!/usr/bin/env bash
# ... code that sets $found
# If you've already processed args. with getopts above,
# you must reset OPTIND to process them again.
OPTIND=1
if (( found == 1 )) # found is numeric, use arithmetic expression to compare
then
echo "You have chosen to insert things."
first= last= email= number= # don't use all-uppercase var. names
while getopts "if:l:e:n:" option
do
case $option in
f)
first=$OPTARG
;;
l)
last=$OPTARG
;;
e)
email=$OPTARG
;;
n)
number=$OPTARG
;;
esac
done
if [[ -z $first || -z $last || -z $email || -z $number ]]; then
echo "Error!!! Some input is missing!!!" >&2
usage # display usage
exit 1
fi
echo -e "$first\t$last\t$email\t$number" >> contacts
fi

if else statement is failing in shell script

I have a very simple shell script which is failing at if-else. And I am not able to understand what is wrong. I checked my syntax and indentation.
echo_usr()
{
read -p "Do you want to preserve $EIP_HOME/conf and $EIP_HOME/installer/.datastore? [Y/N] " usr_in
kill_java()
if [ "$usr_in" == "y*" ] || [ "$usr_in" == "Y*" ]; then
preserve_conf()
else
if [ "$usr_in" == "n*" ] || [ "$usr_in" == "N*" ]; then
reset_home_eip()
else
echo "Invalid input"
fi
fi
reset_db()
}
It is giving me the following error:
syntax error near unexpected token `else'
To call a function with no arguments, just use the name of the function. Empty parentheses are not necessary, and in fact are a syntax error. (Or, more precisely, it's part of the syntax for a function definition, and if you try to use it as a function call you'll get a syntax error message later -- if you're lucky.)
The idea is that a call to a shell function looks and acts like an invocation of an external program.
For example, change:
preserve_conf()
to
preserve_conf
(Using elif rather than else followed by if is a good idea, but it's not the cause of the problem you're seeing.)
There's another problem that you won't see until you get past the syntax error (thanks to Sam for pointing it out in a comment). This:
if [ "$usr_in" == "y*" ] || [ "$usr_in" == "Y*" ]; then
isn't going to work correctly; the == operator used by the [ / test built-in doesn't do wildcard matching. If you're using bash, you can use [[ rather than [, and you can also combine the y and Y cases into a single pattern:
if [[ "$usr_in" == [yY]* ]] ; then
...
elif [[ "$usr_in" == [nN]* ]] ; then
...
fi
If you don't have a shell that supports that syntax, a case statement might be your next best bet -- in fact that's probably even cleaner than the if version:
case "$usr_in" in
[yY]*)
...
;;
[nN]*)
...
;;
*)
...
;;
esac

Meaning of "[: too many arguments" error from if [] (square brackets)

I couldn't find any one simple straightforward resource spelling out the meaning of and fix for the following BASH shell error, so I'm posting what I found after researching it.
The error:
-bash: [: too many arguments
Google-friendly version: bash open square bracket colon too many arguments.
Context: an if condition in single square brackets with a simple comparison operator like equals, greater than etc, for example:
VARIABLE=$(/some/command);
if [ $VARIABLE == 0 ]; then
# some action
fi
If your $VARIABLE is a string containing spaces or other special characters, and single square brackets are used (which is a shortcut for the test command), then the string may be split out into multiple words. Each of these is treated as a separate argument.
So that one variable is split out into many arguments:
VARIABLE=$(/some/command);
# returns "hello world"
if [ $VARIABLE == 0 ]; then
# fails as if you wrote:
# if [ hello world == 0 ]
fi
The same will be true for any function call that puts down a string containing spaces or other special characters.
Easy fix
Wrap the variable output in double quotes, forcing it to stay as one string (therefore one argument). For example,
VARIABLE=$(/some/command);
if [ "$VARIABLE" == 0 ]; then
# some action
fi
Simple as that. But skip to "Also beware..." below if you also can't guarantee your variable won't be an empty string, or a string that contains nothing but whitespace.
Or, an alternate fix is to use double square brackets (which is a shortcut for the new test command).
This exists only in bash (and apparently korn and zsh) however, and so may not be compatible with default shells called by /bin/sh etc.
This means on some systems, it might work from the console but not when called elsewhere, like from cron, depending on how everything is configured.
It would look like this:
VARIABLE=$(/some/command);
if [[ $VARIABLE == 0 ]]; then
# some action
fi
If your command contains double square brackets like this and you get errors in logs but it works from the console, try swapping out the [[ for an alternative suggested here, or, ensure that whatever runs your script uses a shell that supports [[ aka new test.
Also beware of the [: unary operator expected error
If you're seeing the "too many arguments" error, chances are you're getting a string from a function with unpredictable output. If it's also possible to get an empty string (or all whitespace string), this would be treated as zero arguments even with the above "quick fix", and would fail with [: unary operator expected
It's the same 'gotcha' if you're used to other languages - you don't expect the contents of a variable to be effectively printed into the code like this before it is evaluated.
Here's an example that prevents both the [: too many arguments and the [: unary operator expected errors: replacing the output with a default value if it is empty (in this example, 0), with double quotes wrapped around the whole thing:
VARIABLE=$(/some/command);
if [ "${VARIABLE:-0}" == 0 ]; then
# some action
fi
(here, the action will happen if $VARIABLE is 0, or empty. Naturally, you should change the 0 (the default value) to a different default value if different behaviour is wanted)
Final note: Since [ is a shortcut for test, all the above is also true for the error test: too many arguments (and also test: unary operator expected)
Just bumped into this post, by getting the same error, trying to test if two variables are both empty (or non-empty). That turns out to be a compound comparison - 7.3. Other Comparison Operators - Advanced Bash-Scripting Guide; and I thought I should note the following:
I used -e thinking it means "empty" at first; but that means "file exists" - use -z for testing empty variable (string)
String variables need to be quoted
For compound logical AND comparison, either:
use two tests and && them: [ ... ] && [ ... ]
or use the -a operator in a single test: [ ... -a ... ]
Here is a working command (searching through all txt files in a directory, and dumping those that grep finds contain both of two words):
find /usr/share/doc -name '*.txt' | while read file; do \
a1=$(grep -H "description" $file); \
a2=$(grep -H "changes" $file); \
[ ! -z "$a1" -a ! -z "$a2" ] && echo -e "$a1 \n $a2" ; \
done
Edit 12 Aug 2013: related problem note:
Note that when checking string equality with classic test (single square bracket [), you MUST have a space between the "is equal" operator, which in this case is a single "equals" = sign (although two equals' signs == seem to be accepted as equality operator too). Thus, this fails (silently):
$ if [ "1"=="" ] ; then echo A; else echo B; fi
A
$ if [ "1"="" ] ; then echo A; else echo B; fi
A
$ if [ "1"="" ] && [ "1"="1" ] ; then echo A; else echo B; fi
A
$ if [ "1"=="" ] && [ "1"=="1" ] ; then echo A; else echo B; fi
A
... but add the space - and all looks good:
$ if [ "1" = "" ] ; then echo A; else echo B; fi
B
$ if [ "1" == "" ] ; then echo A; else echo B; fi
B
$ if [ "1" = "" -a "1" = "1" ] ; then echo A; else echo B; fi
B
$ if [ "1" == "" -a "1" == "1" ] ; then echo A; else echo B; fi
B
Another scenario that you can get the [: too many arguments or [: a: binary operator expected errors is if you try to test for all arguments "$#"
if [ -z "$#" ]
then
echo "Argument required."
fi
It works correctly if you call foo.sh or foo.sh arg1. But if you pass multiple args like foo.sh arg1 arg2, you will get errors. This is because it's being expanded to [ -z arg1 arg2 ], which is not a valid syntax.
The correct way to check for existence of arguments is [ "$#" -eq 0 ]. ($# is the number of arguments).
I also faced same problem. #sdaau answer helped me in logical way. Here what I was doing which seems syntactically correct to me but getting too many arguments error.
Wrong Syntax:
if [ $Name != '' ] && [ $age != '' ] && [ $sex != '' ] && [ $birthyear != '' ] && [ $gender != '' ]
then
echo "$Name"
echo "$age"
echo "$sex"
echo "$birthyear"
echo "$gender"
else
echo "Enter all the values"
fi
in above if statement, if I pass the values of variable as mentioned below then also I was getting syntax error
export "Name"="John"
export "age"="31"
export "birthyear"="1990"
export "gender"="M"
With below syntax I am getting expected output.
Correct syntax:
if [ "$Name" != "" -a "$age" != "" -a "$sex" != "" -a "$birthyear" != "" -a "$gender" != "" ]
then
echo "$Name"
echo "$age"
echo "$sex"
echo "$birthyear"
echo "$gender"
else
echo "it failed"
fi
There are few points which we need to keep in mind
use "" instead of ''
use -a instead of &&
put space before and after operator sign like [ a = b], don't use as [ a=b ] in if condition
Hence above solution worked for me !!!
Some times If you touch the keyboard accidentally and removed a space.
if [ "$myvar" = "something"]; then
do something
fi
Will trigger this error message. Note the space before ']' is required.
I have had same problem with my scripts. But when I did some modifications it worked for me. I did like this :-
export k=$(date "+%k");
if [ $k -ge 16 ]
then exit 0;
else
echo "good job for nothing";
fi;
that way I resolved my problem. Hope that will help for you too.

Resources