Why doesn't testing exit status work when output is redirected? - bash

I have been testing bash and found the following code:
#!/bin/bash
[[ `which pacman` ]] && echo pacman
[[ `which apt-get` ]] && echo apt-get
It will test what package manager is installed and echo it.
On some systems, a failed which command prints the error to stderr. I want to suppress all output from the which command.
So I came up with the following code:
#!/bin/bash
[[ `which pacman >/dev/null 2>&1` ]] && echo pacman
[[ `which apt-get >/dev/null 2>&1` ]] && echo apt-get
But this doesn't output anything. When I run each command on the command line like this:
which pacman >/dev/null 2>&1 && echo $?
On a system with pacman, it prints 0 which it should. The && also proves that the previous command succeeded.
So why doesn't the redirection code work like it does on the command line? What can I add to the script to make it work?
This is really confusing to me, as I have never had this type of problem before. Usually, any command that works on the command line should also work in a bash script, shouldn't it?

[[ ... ]] without a specified test runs [[ -n ... ]]. In this case, it tests whether the captured output is non-empty. But if you redirect everything to /dev/null, the output is indeed empty!
You don't need to capture the output. which should already return a non-zero status when it cannot find the file to execute.
which pacman &> /dev/null && echo pacman

Related

nesting if in a for loop for aws cli commands [duplicate]

I am trying to compare strings in bash. I already found an answer on how to do it on stackoverflow. In script I am trying, I am using the code submitted by Adam in the mentioned question:
#!/bin/bash
string='My string';
if [[ "$string" == *My* ]]
then
echo "It's there!";
fi
needle='y s'
if [[ "$string" == *"$needle"* ]]; then
echo "haystack '$string' contains needle '$needle'"
fi
I also tried approach from ubuntuforums that you can find in 2nd post
if [[ $var =~ regexp ]]; then
#do something
fi
In both cases I receive error:
[[: not found
What am I doing wrong?
[[ is a bash-builtin. Your /bin/bash doesn't seem to be an actual bash.
From a comment:
Add #!/bin/bash at the top of file
How you are running your script?
If you did with
$ sh myscript
you should try:
$ bash myscript
or, if the script is executable:
$ ./myscript
sh and bash are two different shells. While in the first case you are passing your script as an argument to the sh interpreter, in the second case you decide on the very first line which interpreter will be used.
Is the first line in your script:
#!/bin/bash
or
#!/bin/sh
the sh shell produces this error messages, not bash
As #Ansgar mentioned, [[ is a bashism, ie built into Bash and not available for other shells. If you want your script to be portable, use [. Comparisons will also need a different syntax: change == to =.
if [ $MYVAR = "myvalue" ]; then
echo "true"
else
echo "false"
fi
I had this problem when installing Heroku Toolbelt
This is how I solved the problem
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 ago 15 2012 /bin/sh -> dash
As you can see, /bin/sh is a link to "dash" (not bash), and [[ is bash syntactic sugarness. So I just replaced the link to /bin/bash. Careful using rm like this in your system!
$ sudo rm /bin/sh
$ sudo ln -s /bin/bash /bin/sh
If you know you're on bash, and still get this error, make sure you write the if with spaces.
[[1==1]] # This outputs error
[[ 1==1 ]] # OK
Specify bash instead of sh when running the script. I personally noticed they are different under ubuntu 12.10:
bash script.sh arg0 ... argn
Execute in your terminal:
sudo update-alternatives --install /bin/sh sh /bin/bash 100
Make the file executable and then execute without sh.
make it executable by $ chmod +x filename
then instead of sh filename use ./filename

Bash script command and getting rid of shellcheck error SC2181

I have the following bash script:
dpkg-query --show --showformat='${Status}\n' "$i" 2> \
/dev/null | grep "install ok installed" &> /dev/null
if [[ $? -eq 0 ]]; then
l_var_is_desktop="true"
fi
and the ShellCheck utility (https://www.shellcheck.net/) is giving me the following output:
$ shellcheck myscript
Line 17:
if [[ $? -eq 0 ]]; then
^-- SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.
$
The link to this warning is the following: https://github.com/koalaman/shellcheck/wiki/SC2181
What is the best way for modifying this. The command is really too long to put into one line. I would like to avoid using ShellCheck ignore rules.
I've tried creating a local variable and storing the output of the command, but this breaks other rules.
The command doesn't really get much longer by putting it directly in if, you're just adding 3 characters.
if dpkg-query --show --showformat='${Status}\n' "$i" 2> \
/dev/null | grep "install ok installed" &> /dev/null
then
l_var_is_desktop="true"
fi

Check if sourced (.bash_profile) command exists

k.sh:
if ! [ -x "$(command -v nvm)" ]; then
echo 'Error: nvm is not installed' >&2
else
echo 'nvm installed'
fi
Terminal:
km#Karl ~/dev/cac (master) $ nvm --version
0.33.2
km#Karl ~/dev/cac (master) $ . k.sh
Error: nvm is not installed
I'd like to have bash check if the command exists! NVM is sourced in .bash_profile and .bashrc.
[ -x ] tests if a file exists and is executable. It will fail if you pass the name of a shell function or alias. There's no real need for the extra check. Simply checking if command succeeded is enough.
if ! command -v nvm &> /dev/null
hash is for me the most portable:
if hash nvm 2>/dev/null; then echo exists; else echo does not exist; fi
Why it does not work? Let's see what you do:
command -v nvm # prints nothing and exits with 1
$(command -v nvm) # prints nothing and exits with 1
[ -x "$(command -v nvm)" ] exit status of command is ignored. Only the string returned by the command matters. So it is executed like this:
[ -x "" ] test command exits with status 1, as the file "" is not an executable (such file does not exists).
! [ -x "" ] then you negate the return status, so it returns 0, i.e. true
If you which to use command -v to check if a file exists, check for it's return status, not the string:
if command -v nvm 2>/dev/null; then echo exists; else echo does not exist; fi
But hash is a bit more portable, better stick to hash.

SoF2 shell script not running

I've got the following code in my shell script:
SERVER=`ps -ef | grep -v grep | grep -c sof2ded`
if ["$SERVER" != "0"]; then
echo "Already Running, exiting"
exit
else
echo "Starting up the server..."
cd /home/sof2/
/home/sof2/crons/start.sh > /dev/null 2>&1
fi
I did chmod a+x status.sh
Now I try to run the script but it's returning this error:
./status.sh: line 5: [1: command not found
Starting up the server...
Any help would be greatly appreciated.
Could you please try changing a few things in your script as follows and let me know if that helps you?(changed back-tick to $ and changed [ to [[ in code)
SERVER=$(ps -ef | grep -v grep | grep -c sof2ded)
if [[ "$SERVER" -eq 0 ]]; then
echo "Already Running, exiting"
exit
else
echo "Starting up the server..."
cd /home/sof2/
/home/sof2/crons/start.sh > /dev/null 2>&1
fi
The problem is with the test command. "But", I hear you say, "I am not using the test command". Yes you are, it is also known as [.
if statement syntax is if command. The brackets are not part of if syntax.
Commands have arguments separated (tokenized) by whitespace, so:
[ "$SERVER" != "0" ]
The whitespace is needed because the command is [ and then there are 4 arguments passed to it (the last one must be ]).
A more robust way of comparing numerics is to use double parentheses,
(( SERVER == 0 ))
Notice that you don't need the $ or the quotes around SERVER. Also the spacing is less important, but useful for readability.
[[ is used for comparing text patterns.
As a comment, backticks ` ` are considered deprecated because they are difficult to read, they are replaced with $( ... ).

how to check existence of a command in bash?

I am trying to write a configuration script which needs to install few packages only if they are not already present so I tried the following ways which worked but I get the error on my screen that the 'command not found'
I tried :
if ! type "$foobar_command_name" > /dev/null; then
# install foobar here
fi
and
function test {
"$#"
local status=$?
if [ $status -ne 0 ]; then
#install this package
fi
return $status
}
test command1
test command2
You would want to redirect stderr to /dev/null as well (not just stdout).
For this, you can use the 2>&1 redirect which joins stderr into stdout, which you then redirect to /dev/null.
The following should work:
if ! type "$foobar_command_name" >/dev/null 2>&1; then
# install foobar here
fi
Note that instead of doing it this way, you could also do it as:
if ! type "$foobar_command_name" >/dev/null 2>/dev/null; then
# install foobar here
fi

Resources