How to ignore failure of command called through command builtin? - bash

I have a shell script (running on macOS with GNU bash, version 3.2.57(1)-release) where I set -e at the beginning, but I also want to ignore some of the potential failures, so that they don't end the execution of the script. I'm doing that by appending || ... to the relevant commands:
#!/bin/sh
set -e
false || echo ignore failure
The above works and outputs ignore failure, as expected.
However, if I call the false command through the command builtin, this strategy doesn't work -- the following version of the script exits as soon as false fails, without printing anything:
#!/bin/sh
set -e
command false || echo ignore failure
Why is that? How can I get the desired behavior of ignoring the failure even in the second case?
(In this simplified example, I could of course just delete the command builtin, but in my actual use case, it's part of a function that I don't control.)

Why does command false || echo fail?
Seems like this is a bug in bash versions below 4.0.
I downloaded the old versions 3.2.57 and 4.0, compiled them on Linux, and ran your script. I could reproduce your problem in 3.2.57. In 4.0 everything worked as expected.
Strangely, I couldn't find an according note in bash's lists list of changes, but if you search for set -e you find multiple other bugfixes regarding the behavior of set -ein other versions, for instance:
This document details the changes between this version, bash-4.4-rc1, and
the previous version, bash-4.4-beta.
[...]
o. Fixed a bug that caused set -e to be honored in cases of builtins invoking other builtins when it should be ignored.
How to fix the problem?
The best way would be to use more recent version of bash. Even on macOS this shouldn't be a problem. You can compile it yourself or install it from something like brew.
Other than that, you can use workarounds like leaving out command or adding a subshell ( command false; ) || echo ignore failure (courtesy of Nate Eldredge). In either case, things get quite cumbersome. As you don't know when exactly the bug happens you cannot be sure that you correctly worked around it every time.

Related

Running a sh file in git bash even results in variable assignment statements being shown in terminal

I am trying to run a sh file on git bash on Windows 10. I am even seeing the statements which I don't want to be shown like variable assignment in the console. But the same stuff I can't see on either Windows Subsystem for Linux(WSL) or other linux distributions. It becomes a bit annoying to see all the code lines in the sh file when trying to run that, when I am only interested in seeing the statements which I want to echo. Can someone please help in this ?
Sample sh file :
#!/usr/bin/env sh
VARIABLE="OK"
echo $VARIABLE
Output on git bash:
Output on WSL(or any other Linux Systems):
That only happens when both of these occur:
set -x is used
PS4 is set
If you fix either one, the messages will go away.
If you want to fix number one, you can do set +x, or you can find the file that
is calling set -x and remove that line. Probably ~/.bash_profile or
~/.bashrc.
If you want to fix number two, you can do PS4=, and you can add it to one of
those files above to persist if you want.

Conceal a segmentation fault while keeping following commands aware of this incident

This question asked about how to conceal a segmentation fault in a bash script and #yellowantphil provided a solution: pipe the output anywhere
Now I am looking through plenty of repositories handed in from my students. I need to check whether source codes in each repository could be compiled, and if so, whether the executable could work properly.
And I've observed that some of their executables end in failure with output 'segmentation fault'. Since I want to hide most details in my script, I prefer not showing any of this annoying output (and thus I found the question mentioned above). However, I still need to be aware that happens (to skip a loop). What should I do now?
A minimum reproduction of this problem:
Create any executable that causes 'segmentation fault'
Place it in a Bash script:
#!/bin/bash
./segfaultgen >/dev/null 2>&1 | :
echo $?
With that | : (mentioned in #yellowantphil's answer), the following sentence shows the output 0, which does not tell the truth. However error messages appear if | : is commented out. I've also tried appending || echo 1 before | :. It doesn't work as well :(
By default a pipeline only fails if the right-side fails. Enable pipefail so the pipeline will fail if either command fails.
(It's a good option in general. I enable by default it in all of my scripts.)
#!/bin/bash
set -o pipefail
./segfaultgen &>/dev/null | :
echo $?
Also, since you're using bash, &>/dev/null is shorter.

bash script overrides hard coded variables in executed second script

I'm calling Uncle. I'm attempting to manipulate variables that have hard coded values in a second bash script I am calling. I have no control over the script and am building a wrapper around it to adjust some build behavior before it finally kicks off a yocto build. I'm not sure what else to try after reading and trying numerous examples.
Examples of the situation:
build.sh calls build2.sh
IS_DEV=1 ./build2.sh #trying to override value
build2.sh
IS_DEV=0 # hardcoded value
echo $IS_DEV
# always results in 0.
I have also tried export IS_DEV=1 before calling build2.sh.
I'm sure this is pretty simple, but I cannot seem to get this to work. I appreciate any assistance. Is this possible? I'm using GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu) on Ubuntu 16.04.4 LTS.
Oh, I have also tried the sourcing technique with no luck.
IS_DEV=1 . ./build2.sh
IS_DEV=1 source ./build2.sh
Where am I getting this wrong?
Much appreciated.
If you can't modify the script, execute a modified version of it.
sed 's/^IS_DEV=0 /IS_DEV=1 /' build2.sh | sh
Obviously, pipe to bash if you need Bash semantics instead of POSIX sh semantics.
If the script really hard-codes a value with no means to override it from the command line, modifying that script is the only possible workaround. But the modification can be ephemeral; the above performs a simple substitution on the script, then passes the modified temporary copy through a pipe to a new shell instance for execution. The modification only exists in the pipeline, and doesn't affect the on-disk version of build2.sh.

How to have clean messages from shell script even when set -x is in effect

This question is motivated by Jenkins jobs, and their Execute shell build step. Jenkins by default calls sh with -x switch, which results in echoing the commands it executes. This is definitely good and desired behaviour. However, it would be very nice to be able to just print messages nicely, in addition to having the set -x in effect. An example follows:
If there is echo Next we fix a SNAFU in the build step script, console output of the build will have
+ echo Next we fix a SNAFU
Next we fix a SNAFU
It would be much nicer to have just single line,
Next we fix a SNAFU
How to achieve this? Solution is ideally general sh solution, but Jenkins-specific solution is fine too. And solution should be quite nice looking in the shell script source too, as the dual purpose of the echoes is to both document the script, and make the output more clear.
So just surrounding every echo like that with
set +x
echo Message
set -x
is not very good, because it will print + set +x before every message, and it also takes up 3 lines in the script.
set +x
<command>
set -x
This will disable the printing of the <command>
I found two solutions, neither is ideal but both are kind of ok.
I like better this first one. Instead of having echo Message, have
true Message
It will display
+ true Message
This works, because true ignores its command line arguments (mostly). Downside is the + true clutter before message, and possibly confusing use of true command for others who read the script later. Also, if echo is turned off, then this will not display the message.
Another way is to do echo like this:
( set +x;echo Message )
which will print
+ set +x
Message
This works, because commands in () are executed in a subshell, so changes like set don't affect the parent shell. Downside of this is, it makes the script a bit ugly and more redious to write, and there's an extra line of output. Also, this spawns an extra subshell, which might affect build performance slightly (especially if building under Cygwin on Windows). Positive is, it will also print message if echo is off, and perhaps intention is immediately clear to those who know shell scripting.

Bash completion for Maven escapes colon

I added bash completion for Maven following the docs:
http://maven.apache.org/guides/mini/guide-bash-m2-completion.html
Everything works well except for goals that use a colon. For instance, instead of
mvn eclipse:eclipse
completion escapes the colon
mvn eclipse\:eclipse
Any suggestions how this can be fixed? I'm using Ubuntu 8.10 (2.6.27-17-generic) and
$ bash -version
GNU bash, version 3.2.39(1)-release (i486-pc-linux-gnu)
From Bash FAQ E13.
Just after the complete command in the script you linked to, issue this command to remove the colon from the list of completion word break characters:
COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
Here is a related question and another suggested solution:
How to reset COMP_WORDBREAKS without effecting other completion script?
As stated before, the simplest solution is to alter COMP_WORDBREAKS. However, modifying COMP_WORDBREAKS in your completion script is not safe (as it is a global variable and it has the side effect of affecting the behavior of other completion scripts - for example scp).
Therefore, bash completion offers some helper methods which you can use to achieve your goal in a better and more safer way.
Two helper methods were added in Bash completion 1.2 for this:
_get_comp_words_by_ref with the -n EXCLUDE option
gets the word-to-complete without considering the characters in EXCLUDE as word breaks
__ltrim_colon_completions
removes colon containing prefix from COMPREPLY items
(a workaround for http://tiswww.case.edu/php/chet/bash/FAQ - E13)
So, here is a basic example of how to a handle a colon (:) in completion words:
_mytool()
{
local cur
_get_comp_words_by_ref -n : cur
# my implementation here
__ltrim_colon_completions "$cur"
}
complete -F _mytool mytool
Using the helper methods also simplifies the completion script and ensures that you get the same behavior on any environment (bash-3 or bash-4).
You can also take a look at man or perl completion scripts in /etc/bash_completion.d to see how they use the above helper methods to solve this problem.
Any suggestions how this can be fixed? I'm using Ubuntu 8.10 (2.6.27-17-generic) and
Dennis answer is definitely correct.
But for the record, there is a logged issue (MNG-3928) to improve the documentation about the Maven integration with bash. The issue has a script attached which is an improved version of the one currently online and just works. You might want to give it a try.
Personally, I use the Bash Completion script from Ludovic Claude's PPA (the one that is bundled into the maven package from Ubuntu) that I download directly from bazaar (her e is a direct download link to the HEAD revision). It is just awesome.
I'd go with the Maven2 Bash Completion File at willcodeforbeer.com.
works great
handles colons
doesn't muck up global variable COMP_WORDBREAKS (thereby breaking scp, etc)
easy to edit and understand
Hope that helps!

Resources