shell script error status and syntax checking - shell

I have written a shell script and I am using few commands like rm, ls, etc. In case where those commands fails , I am checking the return status '$?' . But If the script has some syntax error, how can I get the error status of it ? Basically I am going to source this script from another script using the 'source' command. So if the script which is sourced has any syntax error I want to display that in console. Is there any way to get that status ? In shell I executed the script with syntax error and I got the error like 'missing [' , but when I executed echo $? its returning 0 as the status, is this the behavior ? How can I get the status if the script has some syntax error or not ?

You can check the syntax of a shell script using the -n option prior to sourcing:
bash -n somescript # Works also for sh, ksh, zsh et al.
will tell you if somescript is syntactically okay without actually running it. In a program:
if bash -n somescript; then
. somescript
else
printf '%s\n' "Uh-oh, somescript is not syntactically correct." >&2
fi

Related

How to make exception for a bash script with set -ex

I have a bash script that has set -ex, which means the running script will exit once any command in it hits an error.
My use case is that there's a subcommand in this script for which I want to catch its error, instead of making the running script shutdown.
E.g., here's myscript.sh
#!/bin/bash
set -ex
# sudo code here
error = $( some command )
if [[ -n $error ]] ; then
#do something
fi
Any idea how I can achieve this?
You can override the output of a single command
this_will_fail || true
Or for an entire block of code
set +e
this_will_fail
set -e
Beware, however, that if you decide you don't want to use set -e in the script anymore this won't work.
If you want to handle a particular command's error status yourself, you can use as the condition in an if statement:
if ! some command; then
echo 'An error occurred!' >&2
# handle error here
fi
Since the command is part of a condition, it won't exit on error. Note that other than the ! (which negates it, so the then clause will run if the command fails rather than it succeeds), you just include the command directly in the if statement (no brackets, parentheses, etc).
BTW, in your pseudocode example, you seem to be treating it as an error if the command produces any output; usually that's not what you want, and I'm assuming you actually want to test the exit status to detect errors.

Shell exit codes inconsistants with simple command

I have an issue that should not be too hard to solve, I just can't figure what I'm doing wrong.
I need to test if a command is successful or not, and the command needs to be executed from a script. The command is:
curl 127.0.0.1:5000 &> /dev/null
Right now there is no server running, so it should always fail. And it does fail when I execute it from a command line. However when I run it from inside a shell script, it fails but the exit code is 0. What could the cause of that be?
Here is the script:
if curl 127.0.0.1:5000 &> /dev/null
then
echo "sucess"
exit 0
else
echo "failure"
exit 1
fi
And here is the output:
success
curl: (7) Failed to connect to 127.0.0.1 port 5000: Connection refused
However, it does work as expected if I remove the redirection (I'm quite a beginner in shell code, but the redirection shouldn't also redirect the exit code right? So I really don't know what this means)
here is the code without redirections that works as expected (therefore that indicates a failure and has an exit code of 1):
if curl 127.0.0.1:5000
then
echo "sucess"
exit 0
else
echo "failure"
exit 1
fi
Anyone has an idea?
Edit:
I was launching the script with sh script_name.sh in zsh. When I use zsh script_name.sh it now works normally. I still don't fully understand why but at least it works!
"&> /dev/null" is interpreted differently in Bourne shell (sh), The "&" puts the command in background, you can test it with "sleep 100 &>/dev/null". Since it successfully put the command in background, it is a success, and the exit status of the backgrounded command is disregarded.
If you want it to work in Bourne shell (sh), use the traditional syntax ">/dev/null 2>&1", and it will work in newer shells as well, i.e. it is more compatible.
In a system where sh is linked to bash, it will work as is.

$? from bash script command executed by TCL (open pipe) on windows returns wrong value

I've got tcl script with two ways of execution bash script:
#exec bash ./run.sh
open "|bash ./run.sh r"
The bash script is shown below:
#!/bin/bash
ls
if [ "$?" != "0" ]; then
echo "ERROR: Status failed!" > status
else
echo "Everything is OK!" > status
fi
I'm using tclsh for Windows with bash from git bash. When I use:
exec bash ./run.sh
I've got in status file:
Everything is OK!
otherwise:
open "|bash ./run.sh r"
got:
ERROR: Status failed!
Is there any possibility to correctly detect exit code when opened the tcl pipe?
You don't describe whether you get different results out of the ls part of the script. That matters; the ls command is most certainly capable of changing its behaviour according to the environment in which it is invoked. This matters because Tcl executes subprocesses (on Windows) directly using the CreateProcess() system call, rather than the various wrapped versions that Cygwin and git bash use. Other possibilities are that you're launching the script in a different directory and so on.
However, in general we'd expect a script to behave very similarly when launched via exec or via open |… r as they share a common core of functionality. The only differences are to do with how output and termination are waited for.
If you create a subprocess pipeline, by default you won't get to find out about errors from it until you close the pipeline. exec generates any errors “immediately” because it doesn't return control to you until the subprocess has terminated and all output has been read.

Exiting a shell script with an error

basically I have written a shell script for a homework assignment that works fine however I am having issues with exiting. Essentially the script reads numbers from the user until it reads a negative number and then does some output. I have the script set to exit and output an error code when it receives anything but a number and that's where the issue is.
The code is as follows:
if test $number -eq $number >dev/null 2>&1
then
"do stuff"
else
echo "There was an error"
exit
The problem is that we have to turn in our programs as text files using script and whenever I try to script my program and test the error cases it exits out of script as well. Is there a better way to do this?
The script is being run with the following command in the terminal
script "insert name of program here"
Thanks
If the program you're testing is invoked as a subprocess, then any exit command will only exit the command itself. The fact that you're seeing contrary behavior means you must be invoking it differently.
When invoking your script from the parent testing program, use:
# this runs "yourscript" as its own, external process.
./yourscript
...to invoke it as a subprocess, not
# this is POSIX-compliant syntax to run the commands in "yourscript" in the current shell.
. yourscript
...or...
# this is bash-extended syntax to run the commands in "yourscript" in the current shell.
source yourscript
...as either of the latter will run all the commands -- including exit -- inside your current shell, modifying its state or, in the case of exit, exec or similar, telling it to cease execution.

How to prevent direct bash script execution and allow only usage from other script?

I have one script with common functions that is included in other my scripts with:
. ~/bin/fns
Since my ~/bin path is on the PATH, is there a way to prevent users to execute fns from command line (by returning from the script with a message), but to allow other scripts to include this file?
(Bash >= 4)
Just remove the executable bit with chmod -x . ~/bin/fns. It will still work when sourced, but you can't call it (accidentally) by its name anymore.
Some scripts at my workplace use a special shebang
#!/bin/echo Run:.
which returns
Run:. <pathname>
when you use it as a command.
Add the following at the beginning of the script you want to be only allowed to be sourced:
if [ ${0##*/} == ${BASH_SOURCE[0]##*/} ]; then
echo "WARNING"
echo "This script is not meant to be executed directly!"
echo "Use this script only by sourcing it."
echo
exit 1
fi
This will check if the current script and executed shell script file basenames match. If they match, then obviously you are executing it directly so we print a message and exit with status 1.
if (return 0 2>/dev/null) ; then
:
else
echo "Error: script was executed."
exit 1
fi

Resources