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
Related
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.
I found this article, that explains how to redirect output of a bash script to syslog. This is exactly what I needed, but there is a problem.
#!/bin/bash
# Don't ignore any error and return when first error occurs.
exec 1> >(logger -s -t $(basename $0)) 2>&1
set -e
# a list of command(s) that can fail:
chown -R user1:user1 /tmp/myappData/*
chown -R user1:user1 /tmp/myappTmp/*
chown -R user1:user1 /tmp/myappLog/*
#...
exit 0
When I execute above script, and an error occurs, I see that sometimes, the prompt doesn't return after the script is executed. I can't figure out why this is happening. The prompt doesn't return unless I hit enter.
I am concerned that if an app uses this script, it may not get proper exit code back.
If I comment out "set -e", then the prompt always returns properly after the script has executed.
So my question is, what is the proper way to setup a script so that it exits on error, and logs the corresponding message to syslog?
Thank you for your help and suggestions!
The problem here is that the logger pipeline is still running after your script exits, so some of the last content to be logged print after the parent shell has emitted its prompt. If you scroll up, you'll find the prompt hidden somewhere in that prior output.
If you have a very, very new bash, you can collect the PID of the process substitution, and wait for it later.
exec {orig_out}>&1 {orig_err}>&2 1> >(logger -s -t "${0##*/}") 2>&1; logger_pid=$!
[[ $logger_pid ]] || { echo "ERROR: Needs a newer bash" >&2; exit 1; }
cleanup() {
exec >&$orig_out 2>&$orig_err
wait "$logger_pid"
}
trap cleanup EXIT
With an older bash, you can consider other tricks. For example, on Linux, you can use the flock command to try to grab exclusive access to a lockfile before exiting, after ensuring that that lock is held for as long as the logger is running:
log_lock=$(mktemp "${TMPDIR:-/tmp}/logger.XXXXXX")
exec >(flock -x "$log_lock" logger -s -t "${0##*/}") 2>&1
cleanup() {
exec >/dev/tty 2>&1 || exec >/dev/null 2>&1
flock -x "$log_lock" true
}
trap cleanup EXIT
Consider:
gndlp#ubuntu:~$ test -x examples.desktop && echo $?
gndlp#ubuntu:~$ test -x examples.desktop & echo $?
[1] 2992
0
Why is Bash acting the way it is in this situation?
Is the test command simply not finishing and thus the echo command isn't processed?
The meaning of && and & are intrinsically different.
What is && in Bash? In Bash—and many other programming languages—&& means “AND”. And in command execution context like this, it means items to the left as well as right of && should be run in sequence in this case.
What is & in Bash? And a single & means that the preceding commands—to the immediate left of the &—should simply be run in the background.
So looking at your example:
gndlp#ubuntu:~$ test -x examples.desktop && echo $?
gndlp#ubuntu:~$ test -x examples.desktop & echo $?
[1] 2992
0
The first command—as it is structured—actually does not return anything. But second command returns a [1] 2992 in which the 2992 refers to the process ID (PID) that is running in the background and the 0 is the output of the first command.
Since the second command is just running test -x examples.desktop in the background it happens quite quickly, so the process ID is spawned and gone pretty immediately.
& executes a command in the background, and will return 0 regardless of its status.
From the man page:
If a command is terminated by the control operator &, the shell executes the command in the background in a subshell. The shell does not wait for the command to finish, and the return status is 0. Commands separated by a ; are executed sequentially; the shell waits for each command to terminate in turn. The return status is the exit status of the last command executed.
Look at what your commands are:
test -x examples.desktop && echo $?
This means check to see if examples.desktop is executable and if it is then execute echo $?.
test -x examples.desktop & echo $?
means check to see if examples.desktop is executable in the "background". Then execute echo $?.
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