BASH: special parameter for the last command, not parameter, executed - bash

I am looking for a workaround for processes with a long duration.
There is the special parameter $_ containing the last parameter of the last command.
Well I am asking you for something vice versa.
For example:
/etc/init.d/service stop; /etc/init.d/service start
.. could be easier if there is a parameter/variable containing the last binary/script called. Let's define it as $. and we get this:
/etc/init.d/service stop; $. start
Do you have any Idea how to get this?
I found this Thread on SO
But I only get output like this:
printf "\033]0;%s#%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
But the var $BASH_COMMAND is working well:
# echo $BASH_COMMAND
echo $BASH_COMMAND
# echo $BASH_VERSION
4.1.2(1)-release
Any help is very appreciated!
Thank you,
Florian

You can re-execute the last command by using:
!!
however, this won't help with what you want to do, so you could try using the "search and replace on last command" shortcut:
^<text to search for>^<text to replace with>^
so your problem could be solved using:
/etc/init.d/service stop; ^stop^start^
NOTE: This will only replace the first instance of the search text.
Also, see the comments below by more experienced peeps, for other examples and useful sources.

If the primary problem is the duration of the first process, and you know what the next process will be, you can simply issue a wait command against the first process and follow it with the second.
Example with backgrounded process:
./longprocess &
wait ${!}; ./nextprocess # ${!} simply pulls the PID of the last bg process
Example with manual PID entry:
./longprocess
# determine PID of longprocess
wait [PID]; ./nextprocess
Or, if it is always start|stop of init scripts, could make a custom script like below.
#/bin/bash
#wrapperscript.sh
BASESCRIPT=${1}
./$BASESCRIPT stop
./$BASESCRIPT start
Since the commands are wrapped in a shellscript, the default behavior will be for the shell to wait for each command to complete before moving on to the next. So, execution would look like:
./wrapperscript.sh /etc/init.d/service

Related

Disown, nohup or & on Mac OS zsh… not working as hoped

Hi. I'm new to the shell and am working on my first kludged together script. I've read all over the intertube and SO and there are many, MANY places where disown, nohup, & and return are explained but something isn't working for me.
I want a simpler timer. The script asks for user input for the hours, mins., etc., then:
echo "No problem, see you then…"
sleep $[a*3600+b*60+c]
At this point (either on the first or second lines, not sure) I want the script OR the specific command in the script to become a background process. Maybe a daemon? So that the timer will still go off on schedule even if
that terminal window is shut
the terminal app is quit completely
the computer is put to sleep (I realize I probably need some different code still to wake the mac itself)
Also after the "No problem" line I want a return command so that the existing shell window is still useful in the meantime.
The terminal-notifier command (the timer wakeup) is getting called immediately under certain usage of the above (I can't remember which right now), then a second notification at the right time. Using the return command anywhere basically seems to quit the script.
One thing I'm not clear on is whether/how disown, nohup, etc. are applicable to a command process vs. a script process, i.e., will any of them work properly on only a command inside a script (and if not, how to initialize a script as a background process that still asks for input).
Maybe I should use some alternative to sleep?
It isn't necessary to use a separate script or have the script run itself in order to get part of it to run in the background.
A much simpler way is to place the portions that you want to be backgrounded (the sleep and following command) inside of parentheses, and put an ampersand after them.
So the end of the script would look like:
(
sleep $time
# Do whatever
)&
This will cause that portion of the code to be run inside a subshell which is placed into the background, since there's no code after that the first shell will immediately exit returning control to your interactive shell.
When your script is run, it is actually run by starting a new shell to execute it. In order for you to get your script into the background, you would need to send that shell into the background, which you can't do because you would need to communicate with its parent shell.
What you can do is have your script call itself with a special argument to indicate that it should do the work:
#! /bin/zsh
if [ "$1" != '--run' ] ; then
echo sending to background
$0 --run $# &
exit
fi
sleep 1
echo backgrounded $#
This script first checks to see if its first argument is --run. If it is not, then it calls itself ($0) with that argument and all other arguments it received ($#) in the background, and exits. You can use a similar method, performing the test when you want to enter the background, and possibly sending the data you will need instead of every argument. For example, to send just the number of seconds:
$0 --run $[a*3600+b*60+c] &

how to implement event handling in shell script?

In my shell script, im deleting a file at the end of script. And i need it to be deleted even if the script was stopped by (ctrl c or ctrl z)..Is there any way to read that and delete the file?
Thanks in advance
Like #pgl said, trap is what you want. The syntax is:
trap <actionhere> <event> [event...]
The action is one and only one argument, but it can run several commands. The event is either exit (when you call exit manually) or a signal by its "short" name, ie without the SIG prefix (for instance, INT for SIGINT.
Example:
trap "rm -f myfile" INT exit
You can change the trap all along the script. And of course, you can use variable interpolation in your action.
You can catch ctrl+c's with the trap builtin. Try this to get started:
help trap

Getting last process' PID in Makefile

My Makefile look something like this:
setsid ./CppServer>daemon.log 2>&1 &
echo $!>daemon.pid
What I expect it to do is to store the PID of my_awesome_script in corresponding file. However there's nothing there. So where's the problem?
If your makefile really looks like this you will get an error, because I don't see any actual make syntax, just shell syntax. However, my crystal ball tells me that these two lines might be part of the recipe for a rule. If they are, you should realise how make executes recipes; for each line a separate subshell is created, in which that line's command is executed independently: your two commands don't know anything about each other's environment. If you want two commands to be executed in the same subshell, you should issue them as one line (using line continuation characters if necessary), or use make's ONESHELL directive.
The variable you're trying to use prints the pid of the last program run in the background. It is correctly written as echo $! > filename.extension. But since you are running it in the foregorund you have two choices. Run it in the background by appending an & to the end of the line ./script_to_run &, or you can have the script itself print to file the pid of the currently running process by using echo $$ > filename.extension (inside the script). Here is a link that might help you http://tldp.org/LDP/abs/html/internalvariables.html

Shell script that can check if it was backgrounded at invocation

I have written a script that relies on other server responses (uses wget to pull data), and I want it to always be run in the background unquestionably. I know one solution is to just write a wrapper script that will call my script with an & appended, but I want to avoid that clutter.
Is there a way for a bash (or zsh) script to determine if it was called with say ./foo.sh &, and if not, exit and re-launch itself as such?
The definition of a background process (I think) is that it has a controlling terminal but it is not part of that terminal's foreground process group. I don't think any shell, even zsh, gives you any access to that information through a builtin.
On Linux (and perhaps other unices), the STAT column of ps includes a + when the process is part of its terminal's foreground process group. So a literal answer to your question is that you could put your script's content in a main function and invoke it with:
case $(ps -o stat= -p $$) in
*+*) main "$#" &;;
*) main "$#";;
esac
But you might as well run main "$#" & anyway. On Unix, fork is cheap.
However, I strongly advise against doing what you propose. This makes it impossible for someone to run your script and do something else afterwards — one would expect to be able to write your_script; my_postprocessing or your_script && my_postprocessing, but forking the script's main task makes this impossible. Considering that the gain is occasionally saving one character when the script is invoked, it's not worth making your script markedly less useful in this way.
If you really mean for the script to run in the background so that the user can close his terminal, you'll need to do more work — you'll need to daemonize the script, which includes not just backgrounding but also closing all file descriptors that have the terminal open, making the process a session leader and more. I think that will require splitting your script into a daemonizing wrapper script and a main script. But daemonizing is normally done for programs that never terminate unless explicitly stopped, which is not the behavior you describe.
I do not know, how to do this, but you may set variable in parent script and check for it in child:
if [[ -z "$_BACKGROUNDED" ]] ; then
_BACKGROUNDED=1 exec "$0" "$#" & exit
fi
# Put code here
Works both in bash and zsh.
the "tty" command says "not a tty" if you're in the background, or gives the controlling terminal name (/dev/pts/1 for example) if you're in the foreground. A simple way to tell.
Remember that you can't (or, not recommended to) edit the running script. This question and the answers give workarounds.
I don't write shell scripts a long time ago, but I can give you a very good idea (I hope). You can check the value of $$ (this is the PID of the process) and compare with the output of the command "jobs -l". This last command will return the PID of all the backgrounded processes (jobs) and if the value of $$ is contained in the result of the "jobs -l", this means that the current script is running on background.

Making a command loop in shell with a script

How can one loop a command/program in a Unix shell without writing the loop into a script or other application.
For example, I wrote a script that outputs a light sensor value but I'm still testing it right now so I want it run it in a loop by running the executable repeatedly.
Maybe I'd also like to just run "ls" or "df" in a loop. I know I can do this easily in a few lines of bash code, but being able to type a command in the terminal for any given set of command would be just as useful to me.
You can write the exact same loop you would write in a shell script by writing it in one line putting semicolons instead of returns, like in
for NAME [in LIST ]; do COMMANDS; done
At that point you could write a shell script called, for example, repeat that, given a command, runs it N times, by simpling changing COMMANDS with $1 .
I recommend the use of "watch", it just do exactly what you want, and it cleans the terminal before each execution of the commands, so it's easy to monitor changes.
You probably have it already, just try watch ls or watch ./my_script.sh. You can even control how much time to wait between each execution, in seconds, with the -n option, and you can use -d to highlight the difference in the output of consecutive runs.
Try:
Run ls each second:
watch -n 1 ls
Run my_script.sh each 3 seconds, and highlight differences:
watch -n 3 -d ./my_script.sh
watch program man page:
http://linux.die.net/man/1/watch
This doesn't exactly answer your question, but I felt it was relavent. One of the great things with shell looping is that some commands return lists of items. Of course that is obvious, but a something you can do using the for loop is execute a command on that list of items.
for $file in `find . -name *.wma`; do cp $file ./new/location/ done;
You can get creative and do some very powerful stuff.
Aside from accepting arguments, anything you can do in a script can be done on the command line. Earlier I typed this directly in to bash to watch a directory fill up as I transferred files:
while sleep 5s
do
ls photos
end

Resources