Making Terminal slow down and wait for next scripted command

I have a little app that has a “batch mode” to run a number of commands in sequence. Basically it generates a list of stuff like this:

./script --run-option-1 --run-option-2
./script --run-option-5 --run-option-6

Say, a dozen to a few dozen lines like that.

Then my Applescript in my app says:

	do shell script ("open -a Terminal.app")
	tell application "Terminal"
		do script cd_path in window 1
		repeat with theItem in (items of command_list)
			do script theItem in window 1
		end repeat
	end tell

It feeds the entire list directly into Terminal in one go. Terminal usually handles this okay, and holds off accepting the input for the next command until the one before it is done processing. But sometimes it gets a bit scrambled and trips itself up. Sometime it ends up skipping a few commands, then getting back on track and correctly running later ones.

I would ideally like to run one script, wait until that Terminal process is complete, then run the next one, wait until it is complete, etc. I don’t really care if this goes a bit slower, speed is not a concern here. Automation of the processes with good accuracy is much more important.

Any ideas for how to slow down feeding the commands to Terminal one at a time? Thx in advance!

I apologize for asking a question and then answering it, but I did some experimenting in the meantime and I have hit upon a decent solution: putting " && " between each script will make them wait until the process before it has finished. In my testing the last few hours this is working reliably. This means I have to smush all of the script calls together into one long string, with the " && " in between each script, rather than do “repeat with theItem” in Applescript. Then I pass the whole long string to Terminal in one go. Also, for some reason it does not run immediately, but leaves me at a weird "> " text prompt. When I type “exit” the scripts begin to run. So the new code looks like this:

	set final_log to {}
	repeat with xItem in (items of this_log)
		try
			set AppleScript's text item delimiters to {" #"}
			set tItem to text item 1 of xItem
			set uItem to text item 2 of xItem
			set vItem to text item 3 of xItem
			set wItem to "./script " & tItem & " --run-option " & vItem & " --run-option " & uItem & " && " as string
			set end of final_log to wItem
			set AppleScript's text item delimiters to oldDelims
		on error
			set AppleScript's text item delimiters to oldDelims
		end try
	end repeat
	
	set final_text to final_log as string
	
	set cd_path to ("cd " & quoted form of this_path) as text
		
	do shell script ("open -a Terminal.app")
		
	tell application "Terminal"
		do script cd_path in window 1
		do script final_text in window 1
		do script "exit" in window 1
	end tell

This is admittedly a niche application, but if anyone needs to run sequential shell scripts that must not interfere with each other, maybe this will be helpful. Cheers!

3 Likes

Hello @subtledoctor :wave:

First of all - a nifty and cool way …

But I think you forgot about something relevant in working with TID‘s in general…

You want to use two flavors of them - the default and the custom ones but if you want to reset TID‘s to default after your custom or in case of an error you need to define them as default before defining them as custom.

Your Script asks for oldTID‘s but it won’t get them in case of a failure since you didn’t define what’s oldTID‘s - and to mention - it doesn’t even reset them during the operation when it runs successfully as well.

In general TID‘s are reset after a Script was run - successfully or not. But if an operation that’s working actively with TID‘s you always need to define them properly - especially in cases like the one your Script is sketching.

A single line based on this scheme should solve this:
set {<var=oldTIDs>,<AS TID‘s>} to {<AS TID‘s>,<customTID>}

Greetings from Germany :de:

Tobias

1 Like

subtledoctor. Thanks for your post. I’ve been working to reacquaint myself with Bash, and I had forgotten about the && operator.

As regards the above issue, I wasn’t able to replicate the circumstance you describe, but I have to wonder if a few short delays might solve the issue. This working example did not need the delay on my M2 Mac mini, though.

do shell script ("open -a Terminal.app")

delay 0.5

tell application "Terminal"
	do script "mkdir ~/Test && cd ~/Test && touch 'Test File.txt'" in window 1
end tell
1 Like

I often run into unexpected behavior — especially with the Terminal app —
so, to keep things simple, I’ve made it a habit to always wait one second between operations.

Sometimes when launching a new Terminal session or creating a new window,
the zsh path_helper seems to lag a bit.
If a command runs before environment variables are fully loaded,
it might fail or behave oddly — unless you wait at least 0.2 seconds.

So, when executing commands in a freshly opened Terminal or a new window,
adding a short delay might help.
Just a thought — hope it’s useful to someone.

SCRIPT

tell application "Terminal" to launch

tell application "Terminal"
	set refWindowID to (do script "\n\n")
end tell

	
	repeat
		tell application "Terminal"
			tell refWindowID
				log processes as list
				set numCntProcesses to (count of processes) as integer
			end tell
			if numCntProcesses > 2 then
				delay 0.1
			else
				exit repeat
			end if
		end tell
	end repeat

LOG

tell application "Terminal"
	do script "\n\n"
		--> tab 1 of window id 4588
	get processes of tab 1 of window id 4588
		--> {"login", "-zsh", "path_helper"}
-->runing path_helper
(*login, -zsh, path_helper*)
-->some time (*login, -zsh, locale*) (*login, -zsh,-zsh*)
-->I believe all of these are called during the zsh startup process.
	count every processes of tab 1 of window id 4588
		--> 3
	get processes of tab 1 of window id 4588
		--> {"login", "-zsh"}
	(*login, -zsh*)
	count every processes of tab 1 of window id 4588
		--> 2
end tell

I’m not very knowledgeable about the shell but is the ‘wait’ command not relevant to this problem?

wait [ job … ]

Wait for the specified jobs or processes. If job is not given then all currently active child processes are waited for. Each job can be either a job specification or the process ID of a job in the job table. The exit status from this command is that of the job waited for.

Thanks for the reply! The OldDelims variable is actually set earlier in the script and made global, so every subroutine can use it to reset the TIDs. (What I posted is just a small tidbit, the actual script is ~400 lines long.) It’s a good catch though!

As for using a timed delay in the Applescript itself and feed each “do script” to Terminal with delays: I am avoiding that because I don’t know how long each Terminal process will run. Some finish in less than one second; others take 10 minutes or more. With a 2-second delay i could easily still be passing a command to Terminal while it is working. That usually still works… but a delay at the Terminal level makes it more reliable.

“Wait” is a good idea - I didn’t know about that. But it sounds like it serves the same purpose as " && " and the latter seems to be working reliably.

This is all good feedback though! I hope if someone has a similar problem in the future they find guidance here.

As I mentioned, I’m not too familiar with the use of some shell features.

I would simply note that ‘&&’ can present a problem when you require something downstream to complete even if the script stops prematurely, eg ‘exit’ command.