I have a Postgres console command createdb appname_production_master, which return error exit code if the database with this name already exists.
Is it possible to make this command do not return any exit code?
Just ignore the exit code, for example like this.
createdb appname_production_master || true
Unix commands always return exit codes, but you need not respond to the exit code.
When you run a command $? is set to the exit code of the process. As this happens for every command, simply running a different command after the first will change $?.
For example:
createdb appname_production_master # returns 1, a failure code
# $? is 1
/bin/true # always returns 0, success
# $? is 0
Here's another example:
/bin/false # returns false, I assume usually 1
echo $? # outputs 1
echo $? # outputs 0, last echo command succeeded
Related
I would like to conduct analysis using program Arlsumstat_64bit with thousand of input files.
Arlsumstat_64bit reads input files (.arp) and write result file (sumstat.out).
Each input will append new line on the result file (sumstat.out) based on the argument "0 1"
Therefore, I wrote a shell script to execute all the input (*.arp) in the same folder.
However, if the input files contain error, the shell script will be stuck without any subsequently process. Therefore, I found a command with "timeout" to deal my issue.
I made a shell script as following
#!/bin/bash
for sp in $(ls *.arp) ;
do
echo "process start: $sp"
timeout 10 arlsumstat_64bit ${sp}.arp sumstat.out 1 0
rm -r ${sp}.res
echo "process done: $sp"
done
However, I still need to know which input files failed.
How could make a list telling me which input files are "timeout"?
See the man page for the timeout command http://man7.org/linux/man-pages/man1/timeout.1.html
If the command times out, and --preserve-status is not set, then exit
with status 124. Otherwise, exit with the status of COMMAND. If no
signal is specified, send the TERM signal upon timeout. The TERM
signal kills any process that does not block or catch that signal.
It may be necessary to use the KILL (9) signal, since this signal
cannot be caught, in which case the exit status is 128+9 rather than
124.
You should find out which exit codes are possible for the program arlsumstat_64bit. I assume it should exit with status 0 on success. Otherwise the script below will not work. If you need to distinguish between timeout and other errors it should not use exit status 124 or which is used by timeout to indicate a timeout. So you can check the exit status of your command to distinguish between success, error or timeout as necessary.
To keep the script simple I assume you don't need to distingish between timeout and other errors.
I added some comments where I modified your script to improve it or to show alternatives.
#!/bin/bash
# don't parse the output of ls
for sp in *.arp
do
echo "process start: $sp"
# instead of using "if timeout 10 arlsumstat_64bit ..." you could also run
# timeout 10 arlsumstat_64bit... and check the value of `$?` afterwards,
# e.g. if you want to distinguish between error and timeout.
# $sp will already contain .arp so ${sp}.arp is wrong
# use quotes in case a file name contains spaces
if timeout 10 arlsumstat_64bit "${sp}" sumstat.out 1 0
then
echo "process done: $sp"
else
echo "processing failed or timeout: $sp"
fi
# If the result for foo.arp is foo.res, the .arp must be removed
# If it is foo.arp.res, rm -r "${sp}.res" would be correct
# use quotes
rm -r "${sp%.arp}.res"
done
Below code should work for you:
#!/bin/bash
for sp in $(ls *.arp) ;
do
echo "process start: $sp"
timeout 10 arlsumstat_64bit ${sp}.arp sumstat.out 1 0
if [ $? -eq 0 ]
then
echo "process done sucessfully: $sp"
else
echo "process failed: $sp"
fi
echo "Deleting ${sp}.res"
rm -r ${sp}.res
done
In the shell script, I want to do that
if the shell script failed ( exited with non zero value), then before exiting the process, do something.
How could I insert such a if statement block in my shell script.
Is that feasible?
For example,
set -e
echo $password > confidential.txt
rm <file-that-does-not-exist>
rm confidential.txt
I want to make sure that the confidential.txt is made sure to be removed anyways
Use the trap command:
trap 'if [ $? -ne 0 ]; then echo failed; fi' EXIT
The EXIT trap is run when the script exits, and $? contains the status of the last command before it exited.
Note that a shell script's exit status is the status of the last command that it executed. So in your script, it will be the status of
rm confidential.txt
not the error from
rm filethatdoesnotexist
Unless you use set -e in the script, which makes it exit as soon as any command gets an error.
Use trap with the EXIT pseudo signal:
remove_secret () {
rm -f /path/to/confidential.txt
}
trap remove_secret EXIT
You probably don't want the file to remain if the script exits with 0, so EXIT happens regardless of the exit code.
Note that without set -e, rm on a non-existent file doesn't stop the script.
Assuming you're on Linux (or another operating system with /proc/*/fd), you have an even better option: Delete confidential.txt before putting the password into it at all.
That can look something like the following:
exec 3<>confidential.txt
rm -f -- confidential.txt
printf '%s\n' "$password" >&3
...and then, to read from that deleted file:
cat "/proc/$$/fd/3" ## where $$ is the PID of the shell that ran the exec command above
Because the file is already deleted, it's guaranteed to be eligible for garbage collection by your filesystem the moment your script (or the last program it started inheriting its file descriptors) exits or is killed, even if it's killed in a way that doesn't permit traps or signal processing to take place.
I'm new to Linux and am trying to create a simple program that checks if a user exists, if exists - exits the terminal, if not - creates it. I think I've done everything except exiting the terminal.
This is my code so far:
#!/bin/bash
user_name=newUser
if [ $(getent passwd $user_name) ]
then
echo "User $user_name already exists!"
exit
else
echo "The user $user_name doesn't exist and will be added"
sudo useradd -d /home/mint/newUser $user_name
fi
edit: As I said i'm new to Linux. Can someone edit my code and post it, I need to add it to a script, maybe i can close it with while function?
The exit command simply exits the current script. If you want to exit the terminal, you need to exit (or otherwise terminate) the program which is running in that terminal.
A common way to accomplish this is to make the process which wants to exit run as a function in your interactive shell.
add_user () { /path/to/your/script "$#" || exit; }
In this case, though, I would simply advise you to keep your script as is, and leave it to the user to decide whether or not they want to close their terminal.
By the way, the construct
if [ $(command) ]
will be true if the output from command is a non-empty string. The correct way to check the exit code from command is simply
if command
possibly with output redirection if you don't want the invoking user to see any output from command.
The function above also requires your scripit to exit with an explicit error; probably change it to exit 1 to explicitly communicate an error condition back to the caller.
#!/bin/bash
# First parameter is name of user to add
user_name=$1
# Quote variable; examine exit code
if getent passwd "$user_name" >/dev/null 2>&1
then
# Notice addition of script name and redirect to stderr
echo "$0: User $user_name already exists!" >&2
# Explicitly exit with nonzero exit code
exit 1
else
# script name & redirect as above
echo "$0: The user $user_name doesn't exist and will be added" >&2
# Quote argument
sudo useradd -d /home/mint/newUser "$user_name"
fi
This has been answered on askubuntu.com. I will summarise here:
In your script, as you may have noticed, exit will only exit the current script.
A user-friendly way to achieve what you're after is to use the exit command to set a suitable return code and call your script like ./<script> && exit.
If you really want the hard way, use
kill -9 $PPID
which will attempt to kill the enclosing process.
This question already has answers here:
Exit when one process in pipe fails
(2 answers)
Closed 4 years ago.
Even if the mycode.sh has non-0 exit code this command returns 0 as ssh connection was successful. How to get the actual return code of the .sh on remote server?
/home/mycode.sh '20'${ODATE} 1 | ssh -L 5432:localhost:5432 myuser#myremotehost cat
This is not related to SSH, but to how bash handles the exit status in pipelines. From the bash manual page:
The return status of a pipeline is the exit status of the last command, unless the pipefail option is enabled. If pipefail is enabled, the pipeline's return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully. If the reserved word ! precedes a pipeline, the exit status of that pipeline is the logical negation of the exit status as described above. The shell waits for all commands in the pipeline to terminate before returning a value.
If you want to check that there was an error in the pipeline due to any of the commands involved, just set the pipefail option:
set -o pipefail
your_pipeline_here
echo $? # Prints non-zero if something went wrong
It is not possible to actually send the exit status to the next command in the pipeline (in your case, ssh) without additional steps. If you really want to do that, the command will have to be split like this:
res="$(/home/mycode.sh '20'${ODATE} 1)"
if (( $? == 0 )); then
echo -n "$res" | ssh -L 5432:localhost:5432 myuser#myremotehost cat
else
# You can do anything with the exit status here - even pass it on as an argument to the remote command
echo "mycode.sh failed" >&2
fi
You may want to save the output of mycode.sh to a temporary file instead of the $res variable if it's too large.
/home/mycode.sh is located onto the local host.
the ssh command is running cat on the remote server.
All text printed to the standard output of the /home/mycode.sh is redirected to the cat standard input.
The man ssh reads:
EXIT STATUS
ssh exits with the exit status of the remote command or with 255 if an error occurred.
Conclusion: the ssh exists with the EXIT STATUS of the cat or 255 if an error occurred.
if /home/mycode.sh script prints commands to the standard input, they can be run on the remote server when the cat is not present:
/home/mycode.sh '20'${ODATE} 1 | ssh -L 5432:localhost:5432 myuser#myremotehost
In my test, the EXIT STATUS of the last command executed on the remote server is returned by ssh:
printf "%s\n" "uname -r" date "ls this_file_does_not_exist" |\
ssh -L 5432:localhost:5432 myuser#myremotehost ;\
printf "EXIT STATUS of the last command, executed remotely with ssh is %d\n" $?
4.4.0-119-generic
Wed Aug 29 02:55:04 EDT 2018
ls: cannot access 'this_file_does_not_exist': No such file or directory
EXIT STATUS of the last command, executed remotely with ssh is 2
inside of my script I need to run two scripts as another user, so I used the following line:
su otherUser -c "firstScript;secondScript;status=$?"
echo "returning $status"
return $status
The problem is that $status will always return 0.I did test with secondScript failing (wrong argument). Not sure if it's because I exited otherUser or if the $status is actually the result of the su command. Any suggestions?!
You need to capture status inside your outer shell, not in the inner shell invoked by su; otherwise, the captured value is thrown away as soon as that inner shell exits.
This is made much easier because su passes through the exit status of the command it runs -- if that command exits with a nonzero status, so will su.
su otherUser -c 'firstScript; secondScript'; status=$?
echo "returning $status"
return $status
Note that this only returns the exit status of secondScript (as would your original code have done, were it working correctly). You might think about what you want to do if firstScript fails.
Now, it's a little more interesting if you only want to return the exit code of firstScript; in that case, you need to capture the exit status in both shells:
su otherUser -c 'firstScript; status=$?; secondScript; exit $status'); status=$?
echo "returning $status"
return $status
If you want to run secondScript only if firstScript succeeds, and return a nonzero value if either of them fails, it becomes easy again:
su otherUser -c 'firstScript && secondScript'); status=$?
echo "returning $status"
return $status