binary operator expected in win-bash - windows

I'm trying to write a bash script that will run on both Mac and Windows by using win-bash for the windows side, since it doesn't have to be installed.
However, Mac and Windows seem to have different opinions of syntax.
For example, I have the following script thus far:
echo "Setup..."
shopt -s expand_aliases #make sure aliases work
if [ "$(uname)" == "Darwin" ]; then
alias p4cli=./bin/p4
else
alias p4cli=p4
fi
echo "Checking login status..."
p4cli groups > .trash
if [ $? -ne 0 ]; then
p4cli login
fi
echo "Done!"
This fails with:
[: ==: binary operator expected
On windows (win-bash) but works just fine on Mac (which I'm going to assume is the more correct one... since it's actually Unix).
Any idea of what I'm doing wrong here?

Try single = instead of double ==:
if [ "$(uname)" = "Darwin" ]; then
They both mean string equality check inside a test, but the single equal sign for comparison is POSIX-compliant, whereas the double equal sign isn't - it just works for the current bash / ksh as a syntactic sugar for readability (will fail for older shells as well).

Related

Bash to zsh actual syntax differences, such as brackets in 'if' [duplicate]

The zsh_test.sh is simple, as following:
#!/usr/bin/env zsh
if [ $USER == 'root' ]; then
echo "root"
else
echo "not root"
fi
Copy and paste the above codes into a zsh shell, it executed well.
$ #!/usr/bin/env zsh
$ if [ $USER == 'root' ]; then
then> echo "root"
then> else
else> echo "not root"
else> fi
not root
But directly execute script file zsh_test.sh, got an error.
$ ./zsh_test.sh
./zsh_test.sh:3: = not found
I now see what's wrong: You are the victim of a fairly obscure zsh mechanism, which is described in the zshexpn man page and is called '=' expansion. From the man-page:
If a word begins with an unquoted `=' and the EQUALS option is set, the remainder of the word is taken as the name of a command. If a command exists by that name, the word is replaced by the full pathname of the command.
You can try it with the command
echo ==
which also outputs this error message. For instance, on my platofm
echo =ruby
outputs /usr/bin/ruby, because this is where I have ruby installed. If you would have in your PATH a program named =, the == would resolve to this path.
While it is unusual to use a double == sign inside [ ... ], the zsh implementation of this command allows it, but you would have to quote the operator, to avoid =-expansion:
if [ $USER '==' root ]; then
An alternative would be to use [[ ... ]] instead. This is not a command, but a syntactic construct, and expansion rules are different inside it. Therefore
if [[ $USER == root ]]; then
would work as well.
I'm afraid that you are using test command wrong. Let's see why.
test command is defined since Unix version III. You can often find this command also as [ binary in your PATH. In most modern shells (let's pretend that bash is modern shell as well), there is also implementation of test or [ as builtin command. From the specification, the only valid way to compare two strings is this:
STRING1 = STRING2
the strings are equal
STRING1 != STRING2
the strings are not equal
Original strict POSIX implementation of test command is somehow limited and can be difficult to use. But it is portable, and that it's main strength. But what if you don't care about portability at all? Then there are Conditional Expressions.
Conditional Expressions, available as [[ builtin command, are improved, not POSIX compatible replacement for original test command. Look in the manual for the things you can compare with them to get the idea. Double equality sign (==) is also supported (and the documentation explicitly says it's for compatibility with other sorts of computer language.)
Conclusion?
When you are writing scripts for particular shell, like zsh, and you are absolutely sure that portability is not important for you, always use [[ instead of [. Your life will be easier and change to your script is minimal:
#!/usr/bin/env zsh
if [[ $USER == 'root' ]]; then
echo "root"
else
echo "not root"
fi
If portability between different shells and environments is necessary, you will have to use original test or [ command, and forget about zsh, == and many other things at all.
#!/bin/sh
if [ "$USER" = 'root' ]; then
printf '%s\n' "root"
else
printf '%s\n' "not root"
fi

Why the zsh codes like [ $var == 'str' ] runs well as a command but error as a script file?

The zsh_test.sh is simple, as following:
#!/usr/bin/env zsh
if [ $USER == 'root' ]; then
echo "root"
else
echo "not root"
fi
Copy and paste the above codes into a zsh shell, it executed well.
$ #!/usr/bin/env zsh
$ if [ $USER == 'root' ]; then
then> echo "root"
then> else
else> echo "not root"
else> fi
not root
But directly execute script file zsh_test.sh, got an error.
$ ./zsh_test.sh
./zsh_test.sh:3: = not found
I now see what's wrong: You are the victim of a fairly obscure zsh mechanism, which is described in the zshexpn man page and is called '=' expansion. From the man-page:
If a word begins with an unquoted `=' and the EQUALS option is set, the remainder of the word is taken as the name of a command. If a command exists by that name, the word is replaced by the full pathname of the command.
You can try it with the command
echo ==
which also outputs this error message. For instance, on my platofm
echo =ruby
outputs /usr/bin/ruby, because this is where I have ruby installed. If you would have in your PATH a program named =, the == would resolve to this path.
While it is unusual to use a double == sign inside [ ... ], the zsh implementation of this command allows it, but you would have to quote the operator, to avoid =-expansion:
if [ $USER '==' root ]; then
An alternative would be to use [[ ... ]] instead. This is not a command, but a syntactic construct, and expansion rules are different inside it. Therefore
if [[ $USER == root ]]; then
would work as well.
I'm afraid that you are using test command wrong. Let's see why.
test command is defined since Unix version III. You can often find this command also as [ binary in your PATH. In most modern shells (let's pretend that bash is modern shell as well), there is also implementation of test or [ as builtin command. From the specification, the only valid way to compare two strings is this:
STRING1 = STRING2
the strings are equal
STRING1 != STRING2
the strings are not equal
Original strict POSIX implementation of test command is somehow limited and can be difficult to use. But it is portable, and that it's main strength. But what if you don't care about portability at all? Then there are Conditional Expressions.
Conditional Expressions, available as [[ builtin command, are improved, not POSIX compatible replacement for original test command. Look in the manual for the things you can compare with them to get the idea. Double equality sign (==) is also supported (and the documentation explicitly says it's for compatibility with other sorts of computer language.)
Conclusion?
When you are writing scripts for particular shell, like zsh, and you are absolutely sure that portability is not important for you, always use [[ instead of [. Your life will be easier and change to your script is minimal:
#!/usr/bin/env zsh
if [[ $USER == 'root' ]]; then
echo "root"
else
echo "not root"
fi
If portability between different shells and environments is necessary, you will have to use original test or [ command, and forget about zsh, == and many other things at all.
#!/bin/sh
if [ "$USER" = 'root' ]; then
printf '%s\n' "root"
else
printf '%s\n' "not root"
fi

If then else in Bash

I am trying to write a script that checks whether unzip is installed on a RHEL server. If it is installed, the script should proceed, but if not, the following message should be displayed:
Unzip package is not installed.
However, I am trying to be careful and consider that the versions may differ from server to server and I am not sure if I can use a wild card. Below is an example of what I am looking for.
UNZIP='rpm -qq unzip'
If [$UNZIP = unzip*] then 'RUN COMMAND' else echo "Unzip is not installed."
You could try something like:
unzip_package="$(rpm -qq unzip)"
if [[ $unzip_package =~ ^unzip.*$ ]]; then
# ... run command
else
echo "Unzip is not installed."
fi
Here's a few comments on your code:
1) To run a command in subshell and catch the output in a variable, there's two syntax :
var="$(command)"
var=`command`
But not:
var='command'
2) You need space inside brackets in your test:
[ -z "$unzip" ]: [ is an alias of the test command (or a builtin in bash with same behavior). You should also protect your operands with douple quotes when you use this syntax.
[[ -z $unzip ]]: available since KSH88 (in my memory) and allow more powerfull stuffs like testing regexp since bash 4+ with the =~ operator.
You should see this reminder to get more details.
3) You miss a fi to close the if statement
4) Be careful with the case of statements: if and not If
5) You also miss multiples semicolons:
if <cmd1>; then <cmd2>; else <cmd3>; fi
Or:
if ...; then
# do some stufs
else
# ...
fi
Or:
if ....
then
# do some stufs
else
# ...
fi
To conclude you really should learn a little bit more the basics of shell syntax and statements. Here's a really good guide for Bash.

What are these bash_profile functions doing?

I'm confused about this conditional:
if [[ ! -z "$1" ]]
What is this language?
Here is what I'm familiar with for my terminal and bash_profile:
Bash is the shell, or command language interpreter, for the GNU
operating system.
and
Simply put, the shell is a program that takes your commands from the
keyboard and gives them to the operating system to perform. In the old
days, it was the only user interface available on a Unix computer.
Nowadays, we have graphical user interfaces (GUIs) in addition to
command line interfaces (CLIs) such as the shell.
On most Linux systems a program called bash (which stands for Bourne
Again SHell, an enhanced version of the original Bourne shell program,
sh, written by Steve Bourne) acts as the shell program.
function parse_git_branch {
branch=`git rev-parse --abbrev-ref HEAD 2>/dev/null`
if [ "HEAD" = "$branch" ]; then
echo "(no branch)"
else
echo "$branch"
fi
}
function prompt_segment {
# for colours: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
# change the 37 to change the foreground
# change the 45 to change the background
if [[ ! -z "$1" ]]; then
echo "\[\033[${2:-37};45m\]${1}\[\033[0m\]"
fi
}
function build_mah_prompt {
# time
ps1="$(prompt_segment " \# ")"
# cwd
ps1="${ps1} $(prompt_segment " \w ")"
# git branch
git_branch=`parse_git_branch`
if [[ ! -z "$git_branch" ]]
then
ps1="${ps1} $(prompt_segment " $git_branch " 32)"
fi
# next line
ps1="${ps1}\n\$ "
# set prompt output
PS1="$ps1"
}
PROMPT_COMMAND='build_mah_prompt'
The language is Bash, a modern shell based on the old Bourne shell and
(mostly) compatible with POSIX standards.
test aka [
[[ is a Bash extension of the test command also known as [. The test command is a separate executable but since it’s so useful for shell programming, most (if not all) modern shells implement it as a shell builtin. The following commands show that both versions are available on many systems:
$ type -a test
test is a shell builtin
test is /usr/bin/test
$ type -a [
[ is a shell builtin
[ is /usr/bin/[
For more info, see man test or help test (with Bash).
[[
[[ is implemented as a Bash keyword (not an external command). It originally came from the Korn shell and works similarly but has many improvements over the original [ command. See the following for more info:
What is the difference between test, [ and [[ ?
What's the difference between [ and [[ in bash?
Is [[ ]] preferable over [ ] in bash scripts?
Specific example
According to man test (POSIX specification)
−z string True if the length of string string is zero; otherwise, false.
Thus, the [[ -z "$1" ]] construct returns 0 (value for True in Unix shells) if $1, the first positional parameter to a script or function is an empty string. Introducing the negation ! operator converts the expression to its Boolean opposite, i.e, False if the following expression evaluates to True and vice versa.
To sum up, the whole expression evaluates to True if the first argument to the function is a non-empty string and False if it’s empty (or possibly not set at all).
If you read the above links, you’ll notice that [[ ! -z "$1" ]] is actually equivalent to [[ -n "$1" ]] which return True if $1contains anything, i.e., is not empty. This can be further shortened to [[ $1 ]] as quotes aren’t required for variables within [[.
Note: the portable version (for POSIX shells) is [ -n "$1" ] or [ "$1" ] (where the variables have to be quoted to protect from pathname expansion, word splitting and other potential side effects). See http://mywiki.wooledge.org/Quotes for more info.
Functions
The remaining code are shell functions which look like they’re used to build up a colourful prompt which provides details of the status of a git repository if the current working directory is under version control.

Test bash command line arguments

I don't know bash well but this seems pretty basic, yet I'm stuck on it. I'm using the bash installed on Mac OS X. I'm simply trying to test 1 command line argument and this is what I have and it doesn't work.
if [$1 -eq 'clean']
then
echo "Your argument is 'clean'!"
fi
Every time I've tried it, bash gives me a command not found error.
I'm obviously doing something wrong, what is it?
Couple of issues here:
Spaces around [ and ] are required in shell
-eq is used for comparing integers not for strings
Try this instead:
if [[ "$1" == "clean" ]]; then
echo "Your argument is 'clean'!"
fi
If you are using bash then [[ and ]] are more efficient than [ and ]

Resources