Script Works But Need It to Exit Upon Closing Program

Running Xubuntu 16.04 with shell version "GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)," I have a working script that consistently renames a Chrome window:

#!/bin/sh
while sleep 1; do
	xdotool search --name chrome 2>/dev/null | while read id; do 
		xdotool set_window --name "Chrome" $id
	done
done

I can run this script in the background upon boot, but I would prefer to start it upon opening Chrome, then kill it upon closing Chrome.

I've tried a lot of if statements, etc and failed miserably--setting the variables is well above my skills.

May I ask veteran script-ers for assistance in setting up a script that starts the commands listed above when Chrome is opened, then terminates everything when Chrome is closed?

I very much appreciate your patience for my first post, and please let me know if I haven't made things clear enough.

Many thanks.

Edit: listed OS and shell version

Welcome to the forum.

One option would be to create a wrapper script that, when launched / clicked,

  • puts above loop into background, keeping its PID
  • runs chrome in foreground until closed
  • (softly) kills the PID.
1 Like

In addition to what my colleague RudiC already said: please always state your OS, its version, your shell, its version and perhaps other relevant properties of your environment depending on your question. Otherwise you will always be asked for that first without getting any (specific) answer and everybody - including you - loses time.

Every UNIX and Linux is - mostly - the same but "mostly" doesn't mean "completely". Since in our work we often deal with the exceptions rather than the defaults we need to know what exactly you are on if we want to give you more then a generalised and generic answer like RudiC did.

I hope this helps.

bakunin

1 Like

Thank you for the quick replies and the suggestions for proper posting.

I'm unfamiliar with wrapper scripts, but in theory, is this what I'm looking for:

#!/bin/bash
/home/jake/Scripts_Icons/chrome.sh
/home/jake/Scripts_Icons/close_window.sh

chrome.sh:

#!/bin/sh
/home/jake/Scripts_Icons/chrome_retitled &    ##chrome_retitled is the window renaming script (from op)
google-chrome-stable

close_window.sh:

#!/bin/sh
killall chrome_retitled

This appears to work, in the sense that exiting chrome kills closes chrome completely and terminates chrome_retitled, but I appears to be calling a great many scripts. Since they're simple scripts and all ended upon chrome's closing, is this good enough?

Thanks again for your help.

Not sure the killall will work satisfyingly ... kill $PID will.

Just append the kill command - whichever you use - to the chrome.sh script so it will execute when chrome terminates.

1 Like

Hey, that did it. Thank you, thank you.

Putting the kill command in the first script alleviates the need for separate kill script. With your help, I'm down to two scripts that successfully close out when Chrome exits.

When I was investigating wrapper scripts, a script-er said bash was needed, but I'm able to run it on the lighter sh .

I went down a rabbit hole with if statements and didn't realize the answer was much simpler.

Appreciate your speedy, helpful replies.

Why two scripts? Unless you have additional things going on that you didn't show above, ALL can be done in one single script.

Hmm, then I'm still a little lost.

First script:

#!/bin/sh
while sleep 1; do
	xdotool search --name Chrome 2>/dev/null | while read id; do 
		xdotool set_window --name "Chrome" $id
	done
done

Second command:

google-chrome-stable

Third command:

killall chrome_retitled

Right now, I'm running:

#!/bin/sh
/home/jake/Scripts_Icons/chrome_retitled ##chrome_retitled = first script## &
google-chrome-stable
killall chrome_retitled

Could you show me how to put all three things into a single script?

Thanks once more.

Replace the script call with the actual while loops?

I posted here, thinking that would probably be the way. But my scripting skills are not up to inventing such variables.

But I'm relieved that there isn't an obvious way to simply string those three things together--I spent much time moving them around, but I always ended up having to call the one while script, running that in the background (as you suggested), then executing the two commands, one after the other, in a separate script.

What RudiC is trying to show you is the following: you have one script:

And in another script you call it and put it in the background:

In fact you need to take out your comments or at least put the "&" before the comment signs, otherwise it is just part of the comment, but that only as an aside. So, in fact, you have:

#!/bin/sh
/home/jake/Scripts_Icons/chrome_retitled  &
google-chrome-stable
killall chrome_retitled

The line i marked bold is where you call the first script, right?

Now, a script is basically a list of commands, bput together into a file. You can do that inside a shell too, using what is called "process substitution". You write ( .... ) and what is inside the braces is considered one command to the outside. You can redirect its input or output and everything else you do with a separate script - and that includes putting it in the background:

#!/bin/sh
( while sleep 1; do
	xdotool search --name Chrome 2>/dev/null | while read id; do 
		xdotool set_window --name "Chrome" $id
	done
done) &
google-chrome-stable
kill -15 %1

Notice that "%" is using the job control of the shell: if you have background jobs they are assigned consecutive small numbers which you can list with the jobs command. You can address the processes using these small numbers instead of their PIDs.

Furthermore notice that there is a difference between "process substitution" ( ( ... ) ) and "command substitution" ( $( ... ) ) as they are easily confused. They (can) serve similar purposes but are different things.

You should NOT use killall anyway, because this sends signal 9 to the process(es). You should always try signal 15 first and only use signal 9 if that doesn't work.

I hope this helps.

bakunin

2 Likes
#!/bin/sh
( while sleep 1; do
	xdotool search --name Chrome 2>/dev/null | while read id; do 
		xdotool set_window --name "Chrome" $id
	done
done) &
google-chrome-stable
kill -15 %1

Your instructions are extraordinarily clear and helpful. First off, you combined things into a single script that almost terminates itself appropriately, which is close to what I was hoping for. Secondly, your explanations for why/how parentheses are used make sense and I see how I can apply the concept to other scripts I'm working on.

While I understand perfectly why users might choose

kill -15

rather than the more aggressive

killall

it turns out that

kill -15 %1

doesn't work in the script. This script, called "chrome," appears to terminate itself successfully:

#!/bin/sh
( while sleep 1; do
	xdotool search --name Chrome 2>/dev/null | while read id; do 
		xdotool set_window --name "Chrome" $id
	done
done) &
google-chrome-stable
killall chrome

Because of your clear explanation, I was able go into xubuntu's task manager and see that the script called "chrome" generates itself several times during actual browsing. Before exiting the chrome browser, you can watch "chrome" kill itself until there're only two "chrome"'s running, but it never gets down to one "chrome."

Is that why

killall 

is necessary here? Because there are multiple "chrome"'s? I hope I'm right, because that would mean you've taught me something that I used to troubleshoot on my own.

Granted, the pedagogy here is small, but folks like me have to start somewhere.

1 Like

Yes, exactly. The command kill sends a signal to a (exactly one) process, identified by the given process ID. Issue a

kill -l

to see a list of signals your system is able to send. killall , on the other hand, works on groups of processes: it will send the same signal (if i remember correctly signal 9) to all processes identified by a common command. You can try the difference with a little experiment: Issue the command

sleep 1000 &

three times. In the ps -er | grep sleep output you will see three times the same process "name" (in fact the command, as processes have no names, just IDs) but with three different PIDs, like this:

$ ps -fe | grep leep
bakunin      5897  5893  0 21:12 pts/0    00:00:00 sleep 1000
bakunin      5898  5893  0 21:12 pts/0    00:00:00 sleep 1000
bakunin      5899  5893  0 21:12 pts/0    00:00:00 sleep 1000

You can terminate each one of them by kill -15 <PID> , but killall sleep will first search for all processes with a command "sleep", find these three and then send one after the other a signal.

Web browsers traditionally wreak havoc in the process model for reasons i have not fully figured out. I.e. it is not possible to start a Firefox (or Chrome, for that matter) two times, like any other program. And each window (i hate tabs, so my browser instances are all windows) is not a separate process but - honestly, i have given up on trying to understand. I all programs would behave that way i'd already asked for a permanent place in the asylum. Why a process started once has two processes instead of one i don't understand. If you are interested - and i hope you are, because this is what makes you learn more and more - i suggest you investigate and tell us if you find out. I don't have this xdotool you are using.

I hope this helps.

bakunin

1 Like

I guess that all the chrome s you see "several times during actual browsing" are child processes created / spawned by the parent chrome . When kill ing that, all child processes will disappear as well.
Why use kill at all? Check the existence of the parent process in the while loop ( pgrep available?), and quit it when that process exits. (Untested!) example:

( sleep 1
  while pgrep google-chrome-stable
    do  xdotool search --name Chrome 2>/dev/null | 
        while read id
           do xdotool set_window --name "Chrome" $id
           done
        sleep 1
    done
 ) &
google-chrome-stable

Adapt the first sleep 's interval to allow for the chrome process creation.

1 Like
#!/bin/sh
( sleep 1
  while pgrep chrome
    do  xdotool search --name Chrome 2>/dev/null | 
        while read id
           do xdotool set_window --name "Chrome" $id
           done
        sleep 1
    done
 ) &
google-chrome-stable

This indeed terminates the script upon exiting google-chrome-stable!

pgrep google-chrome-stable

did not find the chrome browser, but

pgrep chrome

did.

Initially, exiting google-chrome-stable left the one instance of the script still running until--because you folks had taught me a certain awareness--I realized that because the script was named "chrome," matters were confused. Renaming the script to something other than "chrome" shut it down entirely after exiting google-chrome-stable.

I should've never used the word "chrome" in the script's name to begin with; I wonder how many other issues I created because of the misleading name.

In fact, because of the poor title, I wonder if bakunin's original suggestion:

kill -15 %1

would've worked from the onset. I'll have to see.

ps -fe | grep leep

I was not familiar with that particular use of grep. That would've headed off some earlier problems, I imagine.

Since you all have been so patient/helpful with such a seemingly arcane script, this is the overarching issue:

xubuntu_panel_embedded_in_mate_18.3_window_title_bar - Album on Imgur

That's the screen of a 7" GPD Pocket running Mate 18.3 but with a Xubuntu panel, since it was the only panel I found that can be both transparent and run directly on the window title bar. Joining the panel with the window title bar saves screen real estate, but a text-filled title covers the print-outs of the panel's genmons. So there was a big need to shorten the title, and I've modified the retitling script for the windows of Thunderbird and Links2 as well.

I have a beloved umpc MBook M1, with a 4.8" screen and without these scripts, it's just gibberish across the window title.

bakunin, I don't know why Chrome/Firefox are finicky about multiple instances. Links2 is mingy too--and it doesn't even have tabs, but it is screamingly fast, completely junk-free, and is lightweight enough to run on a abacus. I use it whenever I can for peaceful, unobtrusive browsing, especially news sites.

Once again, thank you both for your help.

It is time to correct a few of my not-so-precise statements above. Suffice it to say i learned something new myself yesterday. Many thanks to RudiC who pointed out my misconception to me.

A small explanation first of what is only a little trick: ps -fe shows all processes and if i would filter that through grep sleep it would end up finding itself (most times - it is a race condition). To avoid that (and a second grep like the often seen:

ps -fe | grep something | grep -v grep

at the same time) i use leep as a regular expression here. This will also search for "sleep" but since the regexp looks different to the string searched for it will not find itself, thus saving the second grep .

Now to something else, since i explained something not quite adequate and as a beginner you should learn it the right way, not the wrong way. When i talked about "process substitution" and "command substitution" above my wording was not quite accurate. Here is the truth:

The construct ( ... ) basically opens up a "subshell": it starts a shell and runs the commands inside the braces like a separate (unnamed) script in that shell. Afterwards this shell is closed and normal operation resumes. Note that this has the side effect of removing changes in variables you did inside the subshell:

var="abc"
( var="XYZ"
  command1
  command2
)
echo $var

will result in "abc", not "XYZ". Many of the following concepts will be based on such a subshell.

Let us get to "process substitution". A process substitution happens if you use the subshell mechanism to redirect input to or output from such a subshell. In case of redirecting the output it this will offer the ability to "string together" output of differing commands.

Example: you have two files and you want to search in one for one thing and for something else in the other but you want to process the results the same way. Like this:

( grep "something" file1 ; grep "otherthing" file2 )> ....

Here is a more complex example from the ksh manual:

paste <(cut -f1 file1) <(cut -f3 file2) | tee >(process1) >(process2)

This cuts field 1 from file1 and field 3 from file2 , sends these to paste so that a "table" with these two fields is created. This is in turn sent to process1 as well as process2 and also displayed on screen (all by the tee command).

Again, always keep in mind thaat all this is done in subshells, so your variable scope (and other things too) might not be what you think it is.

Finally there is "command substitution". This is done by $( ... ) (sometimes ${ ... ; } ) and works similar to process substitution but you can treat the output of the whole block as a string/number:

var=$(tail -n 1 /some/file)

tail -n 1 will extract the last line of a file. The whole construct will assign this last line as a string to the variable. Again, this is done in a subshell so you will have to watch variable scope, etc., because the subshell is left at the end.

Here is another example: sometimes you may want to preserve a return code in a more complex way. Say you have 3 commands and if any of them fails you want to consider all of them failed:

MyRetVal=$( RC=0
                         if ! command1 ; then RC=1 ;  fi
                         if ! command2 ; then RC=1 ;  fi
                         if ! command3 ; then RC=1 ;  fi
                         echo $RC
                       )

If all commands succeed RC will be "0", otherwise "1". This is printed by the echo command to stdout but assigned to MyRetVal by the command substitution.

I hope this helps.

bakunin

1 Like
tail

In my searches about this original script, I had read about tail, but had not seen as clear an example as yours. That is, unfortunately, an issue with general googling. Before coming here, I did due diligence troubleshooting this script, and never did find the kind of explanations that you're providing. Either I'm googling the wrong words or their algorithm isn't sophisticated enough to generate a response beyond "Here's a script."

paste <(cut -f1 file1) <(cut -f3 file2) | tee >(process1) >(process2)

I've obviously bookmarked this thread, with the hopes of returning to it to fix future problems. Since I'm no mathematician or a user of spreadsheets beyond subject-note, my scripting issues mostly lie in the realm of how things are displayed on the screen, window title bars, genmon outputs, etc.

In light of that, I was wondering if you had the energy to explain the use of an ampersand.

( sleep 1
  while pgrep google-chrome-stable
    do  xdotool search --name Chrome 2>/dev/null | 
        while read id
           do xdotool set_window --name "Chrome" $id
           done
        sleep 1
    done
 ) &
google-chrome-stable

This script worked and because of this helpful thread, I understand why it did. I understand that the amperstand used in the line ") &" essentially says the "while" stuff will run in the background as long as "google-chrome-stable" runs in the foreground.

Okay. But what happens if a user wants to add something in between:

#!/bin/sh
( sleep 1
  while pgrep links
    do  xdotool search --name links 2>/dev/null | 
        while read id
           do xdotool set_window --name "Links2" $id
           done
        sleep 1
    done
 ) &
xlinks2 &
sleep .2
wmctrl -a "Links2"
sleep .2
wmctrl -r :ACTIVE: -b toggle,maximized_vert,maximized_horz
sleep .2
xdotool key s &

[the issue at hand: Links2 doesn't start maximized and clicking "s" brings up its bookmarks]

I could not get the maximization/bookmarks commands to run until I added strategic single ampersands. Without the ampersands, the wmctrl/xdotool commands don't run until after xlinks2 is closed, defeating their purpose.

If the "while" script runs in the background, xlinks2 runs in the foreground, but I don't yet understand how subsequent ampersands *also* seem to drop xlinks2 into the background; nor do I understand why the wmctrl/xdotool commands execute themselves directly on xlinks2 rather than on the "while" script or on some other window [which I have seen happen in previous failed scripts].

Feel free to beg off this thread at any time. The final script of this thread works perfectly; I'm posting for the sake of my continued erudition, or perhaps someone else's, should s/he be lucky enough to stumble here.

Have you considered multiple instances of this script running at once? pgrep allows matching only to processes from the current session with --session 0 this may stop you subshell hanging around until all instances of links have ended:

#!/bin/sh
( sleep 1
  while pgrep --session 0 links
    do  xdotool search --name links 2>/dev/null | 
        while read id
...
1 Like

man bash :

Every command list followed by an & is put into background for execution and the next command is addressed, while commands without it are waited for to terminate. So, in order to get a bunch of programs in parallel, you need to put them all into background.

Rg. your other question "why the wmctrl/xdotool commands execute themselves directly on xlinks2": it seems that unless told otherwise these commands will work on the last (highest) window on "stack" which in this case is xlinks2. You may try to verify / falsify this assumption and report back.

@Chubler_XL--thank you for the idea

while pgrep --session 0 links

I ran that in both the Chrome and Links2 scripts, but the script does continue to spawn itself, with a maximum of three instances, usually running two instances.

But the modifier was new to me and I was glad to learn about it.

As for my ampersand query, thank you, RudiC:

>> it seems that unless told otherwise these commands will work on the last (highest) window on "stack" <<

Makes sense to me. It's intriguing that

xlinks2 &

can send xlinks2 to the background, but subsequent xlinks2 commands (window maximization, key strokes, etc) act as if xlinks2 is in the foreground, or at least physically visible. It would appear that background vs foreground features of a script are not literal, meaning that a given background process can actually be the one thing you're seeing.