Check if sourced (.bash_profile) command exists - bash

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.

Related

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

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

-n redundant in command -v tests?

When testing for the existence of a command,
[ -n "$(command -v foo)" ]
and
[ "$(command -v foo)" ]
seem functionally equivalent, yet most examples I've seen include the -n test explicitly. I assume that if a command does not exist, that's equivalent to:
[ "" ]
Is it bad practice to omit -n?
Regardless of using -n or not, you are testing the output of the command builtin. I would test it's return value:
if command -v "$cmd" >/dev/null 2>&1 ; then
echo "command $cmd exists"
fi

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

Simple bash script for starting application silently

Here I am again. Today I wrote a little script that is supposed to start an application silently in my debian env.
Easy as
silent "npm search 1234556"
This works but not at all.
As you can see, I commented the section where I have some troubles.
This line:
$($cmdLine) &
doesn't hide application output but this one
$($1 >/dev/null 2>/dev/null) &
works perfectly. What am I missing? Many thanks.
#!/bin/sh
# Daniele Brugnara
# October, 2013
# Silently exec a command line passed as argument
errorsRedirect=""
if [ -z "$1" ]; then
echo "Please, don't joke me..."
exit 1
fi
cmdLine="$1 >/dev/null"
# if passed a second parameter, errors will be hidden
if [ -n "$2" ]; then
cmdLine="$cmdLine 2>/dev/null"
fi
# not working
$($cmdLine) &
# works perfectly
#$($1 >/dev/null 2>/dev/null) &
With the use of evil eval following script will work:
#!/bin/sh
# Silently exec a command line passed as argument
errorsRedirect=""
if [ -z "$1" ]; then
echo "Please, don't joke me..."
exit 1
fi
cmdLine="$1 >/dev/null"
# if passed a second parameter, errors will be hidden
if [ -n "$2" ]; then
cmdLine="$cmdLine 2>&1"
fi
eval "$cmdLine &"
Rather than building up a command with redirection tacked on the end, you can incrementally apply it:
#!/bin/sh
if [ -z "$1" ]; then
exit
fi
exec >/dev/null
if [ -n "$2" ]; then
exec 2>&1
fi
exec $1
This first redirects stdout of the shell script to /dev/null. If the second argument is given, it redirects stderr of the shell script too. Then it runs the command which will inherit stdout and stderr from the script.
I removed the ampersand (&) since being silent has nothing to do with running in the background. You can add it back (and remove the exec on the last line) if it is what you want.
I added exec at the end as it is slightly more efficient. Since it is the end of the shell script, there is nothing left to do, so you may as well be done with it, hence exec.
& means that you're doing sort of multitask whereas
1 >/dev/null 2>/dev/null
means that you redirect the output to a sort of garbage and that's why you don't see anything.
Furthermore cmdLine="$1 >/dev/null" is incorrect, you should use ' instead of " :
cmdLine='$1 >/dev/null'
you can build your command line in a var and run a bash with it in background:
bash -c "$cmdLine"&
Note that it might be useful to store the output (out/err) of the program, instead of trow them in null.
In addition, why do you need errorsRedirect??
You can even add a wait at the end, just to be safe...if you want...
#!/bin/sh
# Daniele Brugnara
# October, 2013
# Silently exec a command line passed as argument
[ ! $1 ] && echo "Please, don't joke me..." && exit 1
cmdLine="$1>/dev/null"
# if passed a second parameter, errors will be hidden
[ $2 ] && cmdLine+=" 2>/dev/null"
# not working
echo "Running \"$cmdLine\""
bash -c "$cmdLine" &
wait

Catch failure in shell script

Pretty new to shell scripting. I am trying to do the following:
#!/bin/bash
unzip myfile.zip
#do stuff if unzip successful
I know that I can just chain the commands together in with && but there is quite a chunk, it would not be terribly maintainable.
You can use the exit status of the command explicitly in the test:
if ! unzip myfile.zip &> /dev/null; then
# handle error
fi
You can use $?. It returns:
- 0 if the command was successfully executed.
- !0 if the command was unsuccessful.
So you can do
#!/bin/bash
unzip myfile.zip
if [ "$?" -eq 0 ]; then
#do stuff on unzip successful
fi
Test
$ cat a
hello
$ echo $?
0
$ cat b
cat: b: No such file or directory
$ echo $?
1
The variable $? contains the exit status of the previous command. A successful exit status for (most) commands is (usually) 0, so just check for that...
#!/bin/bash
unzip myfile.zip
if [ $? == 0 ]
then
# Do something
fi
If you want the shell to check the result of the executed commands and stop interpretation when something returns non-zero value you can add set -e which means Exit immediately if a command exits with a non-zero status. I'm using this often in scripts.
#!/bin/sh
set -e
# here goes the rest

Resources