bash function run command in background - bash

issue related to this: Cannot make bash script work from cloud-init
I tried all kinds of variants like this:
function ge() {
if [ "$1" == ""]
then
geany &
else
eval "geany $1 &"
#also tried:
geany $1 &
geany "$1" &
etc
fi
}
I tried with or without eval, with $1 quoted or not etc.
In all cases (if it works at all) I get bash: [: some.txt: unary operator expected
What I want is that the editor opens/creates the file in the background, so I can still use the terminal for foreground tasks.
Ultimately, I want a working function that does what I intended above, but with geany replaced by $EDITOR. So on different platforms I can use a different editor.
Why is the syntax in functions different than in scripts?

It's certainly possible to start commands in the background via a script:
#!/bin/bash
cmd=geany
function ge {
if [[ $# -eq 0 ]]
then
${cmd} &
else
${cmd} "$#" &
fi
}
ge "$#"
or simpler:
#!/bin/bash
geany "$#" &
but starting an interactive command in the background and terminating the script is likely to fail since the background command's stdin will be closed as soon as the script dies.
You can wait for the background command to finish before terminating the script to fix that though.

Related

Run bash script in background by default

I know I can run my bash script in the background by using bash script.sh & disown or alternatively, by using nohup. However, I want to run my script in the background by default, so when I run bash script.sh or after making it executable, by running ./script.sh it should run in the background by default. How can I achieve this?
Self-contained solution:
#!/bin/sh
# Re-spawn as a background process, if we haven't already.
if [[ "$1" != "-n" ]]; then
nohup "$0" -n &
exit $?
fi
# Rest of the script follows. This is just an example.
for i in {0..10}; do
sleep 2
echo $i
done
The if statement checks if the -n flag has been passed. If not, it calls itself with nohup (to disassociate the calling terminal so closing it doesn't close the script) and & (to put the process in the background and return to the prompt). The parent then exits to leave the background version to run. The background version is explicitly called with the -n flag, so wont cause an infinite loop (which is hell to debug!).
The for loop is just an example. Use tail -f nohup.out to see the script's progress.
Note that I pieced this answer together with this and this but neither were succinct or complete enough to be a duplicate.
Simply write a wrapper that calls your actual script with nohup actualScript.sh &.
Wrapper script wrapper.sh
#! /bin/bash
nohup ./actualScript.sh &
Actual script in actualScript.sh
#! /bin/bash
for i in {0..10}
do
sleep 10 #script is running, test with ps -eaf|grep actualScript
echo $i
done
tail -f 10 nohup.out
0
1
2
3
4
...
Adding to Heath Raftery's answer, what worked for me is a variation of what he suggested such as this:
if [[ "$1" != "-n" ]]; then
$0 -n & disown
exit $?
fi

URxvt as a shell-to-run-things or how to exit bash by <Enter>, but execute string entered so far?

What am I looking for is a replacement for all those things you’ve probably seen by hitting Mod+R in various tiling WM or Alt+F2 in Gnome DE — a small window to run things from there. I feel very sad without having my bash aliases and other stuff missing because those shells (at least those ones I can use now) are non-interactive and their interactiveness can’t be set as an option at run time.
This is why I decided to use URxvt window as a ‘shell for one command’. In my WM I have Mod+R shortcut bound to execute
/bin/bash -c 'export ONE_COMMAND_SHELL=t && urxvt -name one_command_shell'
And put
[ -v ONE_COMMAND_SHELL ] && bind -x '"\C-m":"exit"'
in my ~/.bashrc
This way I can distinguish an URxvt instance which will become ‘shell for one command’, and set C-m combination (yes, it also works for Enter) to exit the shell, and, therefore, URxvt. The problem is how to execute string entered so far before exit?
I found two potential clues:
a) Making use of BASH_COMMAND
BASH_COMMAND
The command currently being executed or about to be executed, unless the
shell is executing a command as the result of a trap, in which case it is
the command executing at the time of the trap.
But I didn’t get it working because
[ -v ONE_COMMAND_SHELL ] && bind -x '"\C-m":"exec $BASH_COMMAND exit"'
will cause exec exec exec.
b) A trap on SIGCHLD!
It may work for you, if you place
[ -v ONE_COMMAND_SHELL ] && {
PS1=
eaoc() { exit; }
trap eaoc SIGCHLD
}
At the end of your ~/.bashrc. PS1 may be non-empty, but it may not have subshell calls, or the trap will execute and shell will exit before the prompt is to be generated. This holds URxvt open till the forked process exit, so may be considered a half-way to solution.
c) A trap to DEBUG.
DEBUG trap executes before any simple command to be executed and before the first command in each function. If we provide a number via OC_SHELL and will count the number of executed commands, like…
/bin/bash -c 'export OC_SHELL=0 && urxvt -name one_command_shell'
# note a zero ----------------^
And at the end of ~/.bashrc
[ -v OC_SHELL ] && {
export PS1=
eaoc() { echo $OC_SHELL && [ $(( OC_SHELL++ )) -ge 1 ] && wait $! && exit; }
trap eaoc DEBUG
}
We get a fail again. Because our process, forked like
$ gimp &
Dies with its parent. How to deal with it?
Interesting thing is that DEBUG trap executes right after the command was entered, this trap does not wait for a process to return its status code or pid of background process in case of using &.
Ok, that’s how create process independent of bash Need to somehow wrap entered string into (nohup … &)
This works for me:
one_command_execute() {
eval $(echo "$READLINE_LINE") &
exit
}
[ -v ONE_COMMAND_SHELL ] && bind -x '"\C-m":"one_command_execute"'
Another version is without eval:
one_command_checkexit() {
[ -v ONE_COMMAND_DONE ] && exit
ONE_COMMAND_DONE=1
}
[ -v ONE_COMMAND_SHELL ] && PROMPT_COMMAND=one_command_checkexit
This doesn't close the window until the command exits. To execute everything in background automatically, add:
[ -v ONE_COMMAND_SHELL ] && bind '"\C-m":" & \n"'

shell script comparing two variables

I have created a script for updating some ipaddress in iptables. Hereby I'm describing the issues which I'm facing with that.
Issues:
Comparison not happening between two variables within a script
At the end of script i need to execute a command ie; service restart/stop etc and output of the commands should be visible while executing the script.
1) Here am comparing two variable strings:
BASE=172.31.0.0
CMD=172.31.1.0
if [[ "$CMD" == "$BASE" ]]; then
echo "ip are same"
else
echo "not matched"
fi
but there is no response/output while executing the above script. Here its not comparison is not happening..Kindly suggest a best solution to resolve this issue.
2) after executing the script I need to restart the iptables:
BASE=172.31.0.0
CMD=172.31.1.0
if [[ "$CMD" == "$BASE" ]]; then
echo "ip are same"
else
echo "not matched"
fi
service iptables restart
iptables -nvL
A script should display the output of the last two lines (commands). Kindly suggest me the best solution and how to do this in a best way.
That's very odd. This should work, so if it's not working you forgot to mention something important.
How is this script being executed? Do you simply type ./script or is it executed by some service (like cron)?
Here are some of suggestions to debug:
Sanity check: see if bash works (perhaps your login shell isn't bash, so you didn't notice). Run this at the terminal:
/bin/bash -c 'echo hello world'
It prints hello world, right? How about this:
/bin/bash -c 'BASE=172.31.0.0; CMD=172.31.1.0; if [[ "$CMD" == "$BASE" ]]; then echo "ip are same"; else echo "not matched"; fi'
If any of the above doesn't work, you have a problem with your bash installation.
Instead of executing your script with ./script.sh, run it like this:
/bin/bash script.sh
Nothing? Run this:
file script.sh
If it ends with something like "with CRLF line terminators", then cdarke nailed it: the file was created on Windows with an improper tool. Recreate it on Linux or use dos2unix. But anyway, I doubt it because with a CRLF-ending file I get this printed:
bash: ./script.sh: /bin/bash^M: bad interpreter: No such file or directory
Instead of nothing at all.
Put those this line on the beginning of the file:
set -x
(below #!/bin/bash, if you have it). This ensures a debugging trace will be printed, showing each command as it is executed.
If still there is nothing shown.. put this at your script (below set -x if you put it):
touch /tmp/hi-this-is-strange
Then check if there is a /tmp/hi-this-is-strange file after you run the script.

Getting exit code of last shell command in another script

I am trying to beef up my notify script. The way the script works is that I put it behind a long running shell command and then all sorts of notifications get invoked after the long running script finished.
For example:
sleep 100; my_notify
It would be nice to get the exit code of the long running script. The problem is that calling my_notify creates a new process that does not have access to the $? variable.
Compare:
~ $: ls nonexisting_file; echo "exit code: $?"; echo "PPID: $PPID"
ls: nonexisting_file: No such file or directory
exit code: 1
PPID: 6203
vs.
~ $: ls nonexisting_file; my_notify
ls: nonexisting_file: No such file or directory
exit code: 0
PPID: 6205
The my_notify script has the following in it:
#!/bin/sh
echo "exit code: $?"
echo "PPID: $PPID"
I am looking for a way to get the exit code of the previous command without changing the structure of the command too much. I am aware of the fact that if I change it to work more like time, e.g. my_notify longrunning_command... my problem would be solved, but I actually like that I can tack it at the end of a command and I fear complications of this second solution.
Can this be done or is it fundamentally incompatible with the way that shells work?
My shell is Z shell (zsh), but I would like it to work with Bash as well.
You'd really need to use a shell function in order to accomplish that. For a simple script like that it should be pretty easy to have it working in both zsh and bash. Just place the following in a file:
my_notify() {
echo "exit code: $?"
echo "PPID: $PPID"
}
Then source that file from your shell startup files. Although since that would be run from within your interactive shell, you may want to use $$ rather than $PPID.
It is incompatible. $? only exists within the current shell; if you want it available in subprocesses then you must copy it to an environment variable.
The alternative is to write a shell function that uses it in some way instead.
One method to implement this could be to use EOF tag and a master script which will create your my_notify script.
#!/bin/bash
if [ -f my_notify ] ; then
rm -rf my_notify
fi
if [ -f my_temp ] ; then
rm -rf my_temp
fi
retval=`ls non_existent_file &> /dev/null ; echo $?`
ppid=$PPID
echo "retval=$retval"
echo "ppid=$ppid"
cat >> my_notify << 'EOF'
#!/bin/bash
echo "exit code: $retval"
echo " PPID =$ppid"
EOF
sh my_notify
You can refine this script for your purpose.

How to include nohup inside a bash script?

I have a large script called mandacalc which I want to always run with the nohup command. If I call it from the command line as:
nohup mandacalc &
everything runs swiftly. But, if I try to include nohup inside my command, so I don't need to type it everytime I execute it, I get an error message.
So far I tried these options:
nohup (
command1
....
commandn
exit 0
)
and also:
nohup bash -c "
command1
....
commandn
exit 0
" # and also with single quotes.
So far I only get error messages complaining about the implementation of the nohup command, or about other quotes used inside the script.
cheers.
Try putting this at the beginning of your script:
#!/bin/bash
case "$1" in
-d|--daemon)
$0 < /dev/null &> /dev/null & disown
exit 0
;;
*)
;;
esac
# do stuff here
If you now start your script with --daemon as an argument, it will restart itself detached from your current shell.
You can still run your script "in the foreground" by starting it without this option.
Just put trap '' HUP on the beggining of your script.
Also if it creates child process someCommand& you will have to change them to nohup someCommand& to work properly... I have been researching this for a long time and only the combination of these two (the trap and nohup) works on my specific script where xterm closes too fast.
Create an alias of the same name in your bash (or preferred shell) startup file:
alias mandacalc="nohup mandacalc &"
Why don't you just make a script containing nohup ./original_script ?
There is a nice answer here: http://compgroups.net/comp.unix.shell/can-a-script-nohup-itself/498135
#!/bin/bash
### make sure that the script is called with `nohup nice ...`
if [ "$1" != "calling_myself" ]
then
# this script has *not* been called recursively by itself
datestamp=$(date +%F | tr -d -)
nohup_out=nohup-$datestamp.out
nohup nice "$0" "calling_myself" "$#" > $nohup_out &
sleep 1
tail -f $nohup_out
exit
else
# this script has been called recursively by itself
shift # remove the termination condition flag in $1
fi
### the rest of the script goes here
. . . . .
the best way to handle this is to use $()
nohup $( command1, command2 ...) &
nohup is expecting one command and in that way You're able to execute multiple commands with one nohup

Resources