shell to find a file , execute it - exit if 'error' and continue if ' no error' - linux

I have to write a shell script and i don't know how to go about it.
Basically i have to write a script where i'd find a file ( it could be possibly named differently). If either file exists then it must be executed, if it returns a 0 ( no error), it should continue the build, if it's not equal to 0 ( returns with error), it should exit. If either file is not found it should continue the build.
the file i have to find could be either file.1 or file.2 so it could be either named (file.1), or (file.2).
some of the conditions to make it more clear.
1) if either file exists , it should execute - if it has any errors it should exit, if no errors it should continue.
2) none could exist, if that's the case then it should continue the build.
3) both files will not be present at the same time ( additional info)
I have tried to write a script but i doubt it's even closer to what i am looking for.
if [-f /home/(file.1) or (file.2)]
then
-exec /home/(file.1) or (file.2)
if [ $! -eq 0]; then
echo "no errors continuing build"
fi
else
if [ $! -ne 0] ; then
exit ;
fi
else
echo "/home/(file.1) or (file.2) not found, continuing build"
fi
any help is much appreciated.
Thanks in advance

DOIT=""
for f in file1.sh file2.sh; do
if [ -x /home/$f ]; then DOIT="/home/$f"; break; fi
done
if [ -z "$DOIT" ]; then echo "Files not found, continuing build"; fi
if [ -n "$DOIT" ]; then $DOIT && echo "No Errors" || exit 1; fi
For those confused about my syntax, try running this:
true && echo "is true" || echo "is false"
false && echo "is true" || echo "is false"

Just putting the line
file.sh
in your script should work, if you set up your script to exit on errors.
For example, if your script was
#!/bin/bash -e
echo one
./file.sh
echo two
Then if file.sh exists and is executable it would run and your whole script would run. If not, the script would fail when it tried to execute the non-existing file.
If you want to execute one file or the other, extend the idea to the following:
#!/bin/bash -e
echo one
./file1.sh || ./file2.sh
echo two
This means if file1.sh does not exist, it will try file2.sh and if that is there it will run and your whole script will run.
This give preference to file1 of course, meaning if they both exist, then only file1 will run.

Related

How to run bash script while it returns code 0?

I have bash script with many lines of code and I need run it while it returns $? == 0, but in case if it has error I need stop it and exit with code 1?
The question is how to do it?
I tried to use set -e command, but Jenkins does not marks build as failed, for him it looks like Success
I also need to get the Error message to show it in my Jenkins log
I managed to get error code(in my case it will be 126), but how to get error message?
main file
fileWithError.sh
rc=$?; if [[ $rc != 0 ]]; then
echo "exit {$rc} ";
fi
fileWithError.sh
#!/bin/sh
set -e
echo "Test"
agjfsjgfshgd
echo "Test2"
echo "Test3"
Just add the command set -e to the beginning of the file
This should look something similar to this
#!/bin/sh
set -e
#...Your code...
I think you just want:
#!/bin/sh
while fileWithError.sh; do
sleep 1;
done
echo fileWithError.sh failed!! >&2
Note that if the script is written well, then the echo is
redundant as fileWithError.sh should have written a decent
error message already. Also, the sleep may not be needed, but is useful to prevent a fast loop if the script succeeds quickly.
You can get the explicit return value, but it requires a bit of refactoring.
#!/bin/sh
true
while test $? = 0; do fileWithError.sh; done
echo fileWithError.sh failed with status $?!! >&2
since the return value of the while script will be the
return value of sleep in the first construction.
Its not quite easy to get an error code only.
How about this ...
#!/bin/bash
Msg=$(fileWithError.sh 2>&1) # redirect all error messages to stdout
if [ "$?" -ne 0 ] # Not Equal
then
echo "$Msg"
exit 1
fi
exit 0
You catch all messages created by fileWithError.sh and if the programm returned an error code then you have the error message already saved in a variable.
But this will make a disadvantage, because you will temporary store all messages created by fileWithError.sh till the error appears.
You can filter the error message with echo "$Msg" |tail -n 1, but its not 100% save.
You should also do some changes in fileWithError.sh...
Switch set -e with trap "exit 1" ERR. this will close the script on errors.
Hope this will help.

Problems of set -e with grep command [duplicate]

I am using following options
set -o pipefail
set -e
In bash script to stop execution on error. I have ~100 lines of script executing and I don't want to check return code of every line in the script.
But for one particular command, I want to ignore the error. How can I do that?
The solution:
particular_script || true
Example:
$ cat /tmp/1.sh
particular_script()
{
false
}
set -e
echo one
particular_script || true
echo two
particular_script
echo three
$ bash /tmp/1.sh
one
two
three will be never printed.
Also, I want to add that when pipefail is on,
it is enough for shell to think that the entire pipe has non-zero exit code
when one of commands in the pipe has non-zero exit code (with pipefail off it must the last one).
$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0
Just add || true after the command where you want to ignore the error.
Don't stop and also save exit status
Just in case if you want your script not to stop if a particular command fails and you also want to save error code of failed command:
set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE
More concisely:
! particular_script
From the POSIX specification regarding set -e (emphasis mine):
When this option is on, if a simple command fails for any of the reasons listed in Consequences of Shell Errors or returns an exit status value >0, and is not part of the compound list following a while, until, or if keyword, and is not a part of an AND or OR list, and is not a pipeline preceded by the ! reserved word, then the shell shall immediately exit.
Instead of "returning true", you can also use the "noop" or null utility (as referred in the POSIX specs) : and just "do nothing". You'll save a few letters. :)
#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."
Thanks for the simple solution here from above:
<particular_script/command> || true
The following construction could be used for additional actions/troubleshooting of script steps and additional flow control options:
if <particular_script/command>
then
echo "<particular_script/command> is fine!"
else
echo "<particular_script/command> failed!"
#exit 1
fi
We can brake the further actions and exit 1 if required.
I found another way to solve this:
set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e
You can turn off failing on errors by set +e this will now ignore all errors after that line. Once you are done, and you want the script to fail again on any error, you can use set -e.
After applying set +e the find does not fail the whole script anymore, when files are not found. At the same time, error messages
from find are still printed, but the whole script continues to execute. So it is easy to debug if that causes the problem.
This is useful for CI & CD (for example in GitHub Actions).
If you want to prevent your script failing and collect the return code:
command () {
return 1 # or 0 for success
}
set -e
command && returncode=$? || returncode=$?
echo $returncode
returncode is collected no matter whether command succeeds or fails.
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status
Example of using this to create a log file
log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
if [ "$exit_status" = 0 ]
then
event="$output"
log_event
else
event="ERROR $output"
log_event
fi
I have been using the snippet below when working with CLI tools and I want to know if some resource exist or not, but I don't care about the output.
if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
echo "none exist actually exist!"
fi
while || true is preferred one, but you can also do
var=$(echo $(exit 1)) # it shouldn't fail
I kind of like this solution :
: `particular_script`
The command/script between the back ticks is executed and its output is fed to the command ":" (which is the equivalent of "true")
$ false
$ echo $?
1
$ : `false`
$ echo $?
0
edit: Fixed ugly typo

How to check dependency in bash script

I want to check whether nodejs is installed on the system or not. I am getting this error:
Error : command not found.
How can i fix it?
#!/bin/bash
if [ nodejs -v ]; then
echo "nodejs found"
else
echo "nodejs not found"
fi
You can use the command bash builtin:
if command -v nodejs >/dev/null 2>&1 ; then
echo "nodejs found"
echo "version: $(nodejs -v)"
else
echo "nodejs not found"
fi
The name of the command is node, not nodejs
which returns the path to the command to stdout, if it exists
if [ $(which node 2>/dev/null) ]; then
echo "nodejs found"
else
echo "nodejs not found"
fi
This is not what the OP asked for (nearly 3 years ago!), but for anyone who wants to check multiple dependencies:
#!/bin/bash
echo -n "Checking dependencies... "
for name in youtube-dl yad ffmpeg
do
[[ $(which $name 2>/dev/null) ]] || { echo -en "\n$name needs to be installed. Use 'sudo apt-get install $name'";deps=1; }
done
[[ $deps -ne 1 ]] && echo "OK" || { echo -en "\nInstall the above and rerun this script\n";exit 1; }
Here's how it works. First, we print a line saying that we are checking dependencies. The second line starts a "for name in..." loop, in which we put the dependencies we want to check, in this example we will check for youtube-dl, yad and ffmpeg. The loop commences (with "do") and the next line checks for the existence of each command using the bash command "which." If the dependency is already installed, no action is taken and we skip to the next command in the loop. If it does need to be installed, a message is printed and a variable "deps" is set to 1 (deps = dependencies) and then we continue to the next command to check. After all the commands are checked, the final line checks to see if any dependencies are required by checking the deps variable. If it is not set, it appends "OK" to the line where it originally said "Checking dependencies.... " and continues (assuming this is the first part of a script). If it is set, it prints a message asking to install the dependencies and to rerun the script. It then exits the script.
The echo commands look complicated but they are necessary to give a clean output on the terminal. Here is a screenshot showing that the dependencies are not met on the first run, but they are on the second.
PS If you save this as an script, you will need to be in the same directory as the script and type ./{name_of_your_script} and it will need to be executable.
You may check the existence of a program or function by
type nodejs &>/dev/null || echo "node js not installed"
However, there is a more sophisticated explanation available here.
I was thinking about this and came up with a few versions, then went on the internet to see what others have to say and ended up here. Albeit an old thread, I'll reply with my thoughts.
First to answer the OP's original question: How can i fix it?
if node -v &>/dev/null; then
echo "nodejs found"
else
echo "nodejs not found"
fi
If you are simply checking if node works, this would do it. But it isn't a very generic way to do it.
Another way is to use command in a loop and collect the missing dependencies (in this example looking for the commands kind and kubectl).
for app in kind kubectl; do command -v "${app}" &>/dev/null || not_available+=("${app}"); done
(( ${#not_available[#]} > 0 )) && echo "Please install missing dependencies: ${not_available[*]}" 1>&2 && exit 1
Or less concisely expressed:
unset not_available # script safety, however not necessary.
for app in kind kubectl; do
if ! command -v "${app}" &>/dev/null; then
not_available+=("${app}")
fi
done
if (( ${#not_available[#]} > 0 )); then
echo "Please install missing dependencies: ${not_available[#]}" 1>&2
exit 1
fi
Then I figured I'd want a way to do the same without a loop, so came up with this:
not_installed=$(command -V kind kubectl 2>&1 | awk -F': +' '$NF == "not found" {printf "%s ", $(NF-1)}')
[[ -n ${not_installed} ]] && echo "Please install missing dependencies: ${not_installed}" 1>&2 && exit 1
The command -V can take any number of entries and posts the result back to stdout and stderr (though I redirect both to stdout for the next command to parse).
awk sets the field separator to <colon><one or more space>, expressed as : +. If the last field contains, "not found", print the second to last field, being the name of the command which is not installed.
Lastly, if the variable contains any data, then report back which dependencies that are missing to stderr and exit the script!
You can do dependency checks in a million ways, but here are a few alternatives which are more generally applicable and not too lengthy while still being easy to follow. :]
If all you want is to check to see if a command exists, use which command. It returns the patch if the command is called, and nothing if it is not found
if [ "$(which openssl)" = "" ] ;then
echo "This script requires openssl, please resolve and try again."
exit 1
fi

Bash ignoring error for a particular command

I am using following options
set -o pipefail
set -e
In bash script to stop execution on error. I have ~100 lines of script executing and I don't want to check return code of every line in the script.
But for one particular command, I want to ignore the error. How can I do that?
The solution:
particular_script || true
Example:
$ cat /tmp/1.sh
particular_script()
{
false
}
set -e
echo one
particular_script || true
echo two
particular_script
echo three
$ bash /tmp/1.sh
one
two
three will be never printed.
Also, I want to add that when pipefail is on,
it is enough for shell to think that the entire pipe has non-zero exit code
when one of commands in the pipe has non-zero exit code (with pipefail off it must the last one).
$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0
Just add || true after the command where you want to ignore the error.
Don't stop and also save exit status
Just in case if you want your script not to stop if a particular command fails and you also want to save error code of failed command:
set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE
More concisely:
! particular_script
From the POSIX specification regarding set -e (emphasis mine):
When this option is on, if a simple command fails for any of the reasons listed in Consequences of Shell Errors or returns an exit status value >0, and is not part of the compound list following a while, until, or if keyword, and is not a part of an AND or OR list, and is not a pipeline preceded by the ! reserved word, then the shell shall immediately exit.
Instead of "returning true", you can also use the "noop" or null utility (as referred in the POSIX specs) : and just "do nothing". You'll save a few letters. :)
#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."
Thanks for the simple solution here from above:
<particular_script/command> || true
The following construction could be used for additional actions/troubleshooting of script steps and additional flow control options:
if <particular_script/command>
then
echo "<particular_script/command> is fine!"
else
echo "<particular_script/command> failed!"
#exit 1
fi
We can brake the further actions and exit 1 if required.
I found another way to solve this:
set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e
You can turn off failing on errors by set +e this will now ignore all errors after that line. Once you are done, and you want the script to fail again on any error, you can use set -e.
After applying set +e the find does not fail the whole script anymore, when files are not found. At the same time, error messages
from find are still printed, but the whole script continues to execute. So it is easy to debug if that causes the problem.
This is useful for CI & CD (for example in GitHub Actions).
If you want to prevent your script failing and collect the return code:
command () {
return 1 # or 0 for success
}
set -e
command && returncode=$? || returncode=$?
echo $returncode
returncode is collected no matter whether command succeeds or fails.
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status
Example of using this to create a log file
log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
if [ "$exit_status" = 0 ]
then
event="$output"
log_event
else
event="ERROR $output"
log_event
fi
I have been using the snippet below when working with CLI tools and I want to know if some resource exist or not, but I don't care about the output.
if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
echo "none exist actually exist!"
fi
while || true is preferred one, but you can also do
var=$(echo $(exit 1)) # it shouldn't fail
I kind of like this solution :
: `particular_script`
The command/script between the back ticks is executed and its output is fed to the command ":" (which is the equivalent of "true")
$ false
$ echo $?
1
$ : `false`
$ echo $?
0
edit: Fixed ugly typo

Shell script continues to run even after exit command

My shell script is as shown below:
#!/bin/bash
# Make sure only root can run our script
[ $EUID -ne 0 ] && (echo "This script must be run as root" 1>&2) || (exit 1)
# other script continues here...
When I run above script with non-root user, it prints message "This script..." but it doe not exit there, it continues with the remaining script. What am I doing wrong?
Note: I don't want to use if condition.
You're running echo and exit in subshells. The exit call will only leave that subshell, which is a bit pointless.
Try with:
#! /bin/sh
if [ $EUID -ne 0 ] ; then
echo "This script must be run as root" 1>&2
exit 1
fi
echo hello
If for some reason you don't want an if condition, just use:
#! /bin/sh
[ $EUID -ne 0 ] && echo "This script must be run as root" 1>&2 && exit 1
echo hello
Note: no () and fixed boolean condition. Warning: if echo fails, that test will also fail to exit. The if version is safer (and more readable, easier to maintain IMO).
I think you need && rather than||, since you want to echo and exit (not echo or exit).
In addition (exit 1) will run a sub-shell that exits rather than exiting your current shell.
The following script shows what you need:
#!/bin/bash
[ $1 -ne 0 ] && (echo "This script must be run as root." 1>&2) && exit 1
echo Continuing...
Running this with ./myscript 0 gives you:
Continuing...
while ./myscript 1 gives you:
This script must be run as root.
I believe that's what you were looking for.
I would write that as:
(( $EUID != 0 )) && { echo "This script must be run as root" 1>&2; exit 1; }
Using { } for grouping, which executes in the current shell. Note that the spaces around the braces and the ending semi-colon are required.

Resources