Bash command substitution freeze - bash

Currently I encountered the following situation when bash script freeze on
PID=`cat test.pid`
After the analysis it was found that even this commands freeze
TEST=$(echo 1)
TEST=`echo 1`
Using set -x in bash script, I can see the following output
+ echo 1
1
++ echo 1
for script
#!/bin/bash
set -x
echo 1
TEST=$(echo 1)
set +x
This script is called from Qt process and everything have worked lately.
When I call this script manually from bash it also works, but when I do it from process it fails.
Currently I'm looking for possible reasons of such freezes, and I've no more ideas.
When I printed environments they matched, but I also can't prinenv inside `` as it freezes also.

Related

how to handle exit code for multiple sequential unix command in a shell script

I am need of shell script which have multiple commands like
Command -1 mv
command 2- cp
command -3 - sed
command -4 echo ,append etc
rc=$?
if =0 success
else
exist30
but even though move command failed script retuning return code as 0 and script showing success message.
Do i need to main RC for all the command or can i better handle return code for each command to make sure every command run successfully
If you have to deal with commands which may fail you can do the following:
if mv ...; then
echo mv success
else
echo mv failure
rc=1
fi
exit 1
For having your script to crash with the first failed command make sure to add the line set -e below your shebang (#!/bin/sh).
Keep in mind that the error handling above is compatible with set -e, the script will not stop.
set -eu is considered best practice for shell scripts, -u will result in a crash of your script if a variable used is unset.

BASH get error code in parent terminal from child terminal?

In a bash script I start a new terminal with a command that gives an error. However I don't seem to be able to grab that error code:
#! /bin/bash
gnome-terminal -x bash -c "cat dksdamfasdlm"
echo $?
Output:
0
So I get the error code of the gnome-terminal command instead of the cat one. One suggestion I got, was to make a file with the code and read that from the parent bash. The problem is that I still seem to not be able and read the error code even that way:
#! /bin/bash
gnome-terminal -x bash -c "cat dksdamfasdlm; echo $?; sleep 2"
Output (on new terminal):
cat: dksdamfasdlm: No such file or directory
0
Why is that? Some suggestion on how to solve this? I just want to somehow grab the error in the new terminal from the parent bash.
It seems GNOME Terminal exits immediately after starting, which is obvious if you run for example gnome-terminal -x sleep 10. Since it doesn't wait for the command to finish, there's no way the return code will be that of the command. I could find no option in gnome-terminal --help-all to keep the process in the foreground.
Regarding your second question, you've double-quoted the command, so $? is expanded before running it. This should work:
gnome-terminal -x bash -c 'cat dksdamfasdlm; echo $?; sleep 2'
PS: The -x option is not documented in GNOME Terminal 3.8.4's gnome-terminal --help-all, various references don't help much, and there's no good explanation for why there's a -e option with identical semantics and different syntax.

How can I prevent bash from reporting an error when attempting to call a non-existing script?

I am writing a simple script in bash to check whether or not a bunch of dependencies are installed on the current system. My script attempts to run a sample script with the -h flag, greps the output for a keyword i would expected to be returned by the sample scripts, and therefore knows whether or not the sample script is installed on the system.
I then pass this through a conditional statement that basically says sample scripts = OK or sample scripts = FAIL. However, in the case in which the sample script isn't installed on the system, bash throws the warning -bash: sample_script: command not found. How can I prevent this from displaying? I tried using the 1>&2 error redirection, but the warning still appears on the screen (I want the OK/FAIL output text to be displayed on the user's screen upon running my script).
Thanks for any suggestions!
If you just want to suppress errors (stderr) and let the "OK" or "FAIL" you are echoing (stdout) pass through, you would do:
./yourscript.sh 2> /dev/null
Although the better approach would be to test whether sample_script is executable before trying to execute it. For instance:
if [ -x "$script" ]; then
*do whatever generates FAIL or OK*
fi
#devnull dixit
command -h 2>/dev/null
I use this function to be independent of which, whence, type -p and whatnot:
pathto () {
DIRLIST=$(echo "$PATH"|tr : ' ')
for e in "$#"; do
for d in $DIRLIST; do
test -f "$d/$e" -a -x "$d/$e" && echo "$d/$e"
done
done
}
pathto script will echo the full path if it can be found (and is executable). Returning 0 or 1 instead left as an exercise :-)
for bash:
if ! type -P sample_script &> /dev/null; then
echo Error: sample_script is not installed. Come back later. >&2
exit 1
fi
sample_script "$#"

grep in bash script + Jenkins

I have a grep command that works in a bash script:
if grep 'stackoverflow' outFile.txt; then
exit 1
fi
This works fine when run on my host. When I call this from a Jenkins build step however, it exits 0 everytime, not seeing 'stackoverflow'. What is going wrong?
Add the following line as the first line in your "Execute Shell" command
#!/bin/sh
grep command exits with a non zero code when it does not find match and that causes jenkins to mark the job as failed. See Below.
In the help section of "Execute Shell"
Runs a shell script (defaults to sh, but this is configurable) for building the project. The script will be run with the workspace as the current directory. Type in the contents of your shell script. If your shell script has no header line like #!/bin/sh —, then the shell configured system-wide will be used, but you can also use the header line to write script in another language (like #!/bin/perl) or control the options that shell uses.
By default, the shell will be invoked with the "-ex" option. So all of the commands are printed before being executed, and the build is considered a failure if any of the commands exits with a non-zero exit code. Again, add the #!/bin/... line to change this behavior.
As a best practice, try not to put a long shell script in here. Instead, consider adding the shell script in SCM and simply call that shell script from Jenkins (via bash -ex myscript.sh or something like that), so that you can track changes in your shell script.
I am a bit confused by the answers on this question! i.e. Sorry, but the answers here are incorrect for this question. The question is good/interesting as plain grep in scripts does cause scripts to exit with failure if the grep is not successful (which can be unexpected), whereas a grep inside an if will not cause exit with failure.
For the example shown in the question exit 1 will be done IF the grep command runs successfully(file exists) AND if the string is found in file. (grep command returns 0 exit code to if).
#Gonen's comment to add 'ls -l outFile.txt' should have been followed up on to see what the real reason for failure was.
TLDR; if catches the exit code of commands inside the if clause:
A grep command that 'fails'(no match or error) inside an if statement in jenkins will not cause jenkins script to stop. Whereas a grep command that fails not inside an if will cause jenkins to stop and exit with fail.
The exit/return code handling is different for commands inside an if statement in shell. if catches the return code and no matter if command was successful or failed the if will return success to $0(after if) (and do actions in if or else).
From man bash:
if list; then list; [ elif list; then list; ] ... [ else list; ] fi
The if list is executed. If its exit status is zero, the then list is executed. Otherwise, each elif list is executed in turn, and
if its exit status is zero, the corresponding then list is executed
and the command completes. Otherwise, the else list is executed, if
present. The exit status is the exit status of the last command
executed, or zero if no condition tested true.
To illustrate, try this (same result in bash or sh):
$ if grep foo bar ; then echo got it; fi; echo $?
grep: bar: No such file or directory
0
$ touch bar
$ if grep foo bar ; then echo got it; fi; echo $?
0
$ echo foo >bar
$ if grep foo bar ; then echo got it; fi; echo $?
foo
got it
0
$ if grep foo bar ; then echo gotit; grep gah mah; fi; echo $?
foo
gotit
grep: mah: No such file or directory
2
I think you have error in your script. You must add 'fi' at the end of 'if' block:
if grep 'stackoverflow' outFile.txt; then
exit 1
fi
If the two were exactly the same it should work. Is your current directory or user different in the two environments? You might not be able to read the file.

Automatic exit from Bash shell script on error [duplicate]

This question already has answers here:
Aborting a shell script if any command returns a non-zero value
(10 answers)
Closed 3 years ago.
I've been writing some shell script and I would find it useful if there was the ability to halt the execution of said shell script if any of the commands failed. See below for an example:
#!/bin/bash
cd some_dir
./configure --some-flags
make
make install
So in this case, if the script can't change to the indicated directory, then it would certainly not want to do a ./configure afterwards if it fails.
Now I'm well aware that I could have an if check for each command (which I think is a hopeless solution), but is there a global setting to make the script exit if one of the commands fails?
Use the set -e builtin:
#!/bin/bash
set -e
# Any subsequent(*) commands which fail will cause the shell script to exit immediately
Alternatively, you can pass -e on the command line:
bash -e my_script.sh
You can also disable this behavior with set +e.
You may also want to employ all or some of the the -e -u -x and -o pipefail options like so:
set -euxo pipefail
-e exits on error, -u errors on undefined variables, -x prints commands before execution, and -o (for option) pipefail exits on command pipe failures. Some gotchas and workarounds are documented well here.
(*) Note:
The shell does not exit if the command that fails is part of the
command list immediately following a while or until keyword,
part of the test following the if or elif reserved words, part
of any command executed in a && or || list except the command
following the final && or ||, any command in a pipeline but
the last, or if the command's return value is being inverted with
!
(from man bash)
To exit the script as soon as one of the commands failed, add this at the beginning:
set -e
This causes the script to exit immediately when some command that is not part of some test (like in a if [ ... ] condition or a && construct) exits with a non-zero exit code.
Use it in conjunction with pipefail.
set -e
set -o pipefail
-e (errexit): Abort the script at the first error, when a command exits with non-zero status (except in until or while loops, if-tests, and list constructs)
-o pipefail: Causes a pipeline to return the exit status of the last command in the pipe that returned a non-zero return value.
Chapter 33. Options
Here is how to do it:
#!/bin/sh
abort()
{
echo >&2 '
***************
*** ABORTED ***
***************
'
echo "An error occurred. Exiting..." >&2
exit 1
}
trap 'abort' 0
set -e
# Add your script below....
# If an error occurs, the abort() function will be called.
#----------------------------------------------------------
# ===> Your script goes here
# Done!
trap : 0
echo >&2 '
************
*** DONE ***
************
'
An alternative to the accepted answer that fits in the first line:
#!/bin/bash -e
cd some_dir
./configure --some-flags
make
make install
One idiom is:
cd some_dir && ./configure --some-flags && make && make install
I realize that can get long, but for larger scripts you could break it into logical functions.
I think that what you are looking for is the trap command:
trap command signal [signal ...]
For more information, see this page.
Another option is to use the set -e command at the top of your script - it will make the script exit if any program / command returns a non true value.
One point missed in the existing answers is show how to inherit the error traps. The bash shell provides one such option for that using set
-E
If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.
Adam Rosenfield's answer recommendation to use set -e is right in certain cases but it has its own potential pitfalls. See GreyCat's BashFAQ - 105 - Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?
According to the manual, set -e exits
if a simple commandexits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in a if statement, part of an && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command's return value is being inverted via !".
which means, set -e does not work under the following simple cases (detailed explanations can be found on the wiki)
Using the arithmetic operator let or $((..)) ( bash 4.1 onwards) to increment a variable value as
#!/usr/bin/env bash
set -e
i=0
let i++ # or ((i++)) on bash 4.1 or later
echo "i is $i"
If the offending command is not part of the last command executed via && or ||. For e.g. the below trap wouldn't fire when its expected to
#!/usr/bin/env bash
set -e
test -d nosuchdir && echo no dir
echo survived
When used incorrectly in an if statement as, the exit code of the if statement is the exit code of the last executed command. In the example below the last executed command was echo which wouldn't fire the trap, even though the test -d failed
#!/usr/bin/env bash
set -e
f() { if test -d nosuchdir; then echo no dir; fi; }
f
echo survived
When used with command-substitution, they are ignored, unless inherit_errexit is set with bash 4.4
#!/usr/bin/env bash
set -e
foo=$(expr 1-1; true)
echo survived
when you use commands that look like assignments but aren't, such as export, declare, typeset or local. Here the function call to f will not exit as local has swept the error code that was set previously.
set -e
f() { local var=$(somecommand that fails); }
g() { local var; var=$(somecommand that fails); }
When used in a pipeline, and the offending command is not part of the last command. For e.g. the below command would still go through. One options is to enable pipefail by returning the exit code of the first failed process:
set -e
somecommand that fails | cat -
echo survived
The ideal recommendation is to not use set -e and implement an own version of error checking instead. More information on implementing custom error handling on one of my answers to Raise error in a Bash script

Resources