Refer to this unanswered question before continuing: Tmux: check exit code of command that was sent by send-keys

tmux send-keys just sends the command and exit successfully but repeat 5; do print This is a test; sleep 1; done still continue running as a subprocess - not in a subshell.

In case I need to make sure repeat 5; do print This is a test; sleep 1; done completely done before doing another tmux send-keys.

How could I achieve that with zsh?

EDIT: @Stéphane Chazelas: I'd like to extend or make the question more clear:

In case of a zsh script that contain multiple send-keys commands as below:

send-keys 'command1_run_as_subprocess_with_unpredictable_time_to_complete' Enter
send-keys 'command2_run_as_subprocess_with_unpredictable_time_to_complete' Enter
send-keys 'command3_run_as_subprocess_with_unpredictable_time_to_complete' Enter

Each send-keys has to wait for the previous one totally completed before running.

How your solution would apply to above case?:

typeset -A pane_pid=($(for t (id index) tmux lsp -F "#{pane_$t} #{pane_pid}"))

pid=$pane_pid[%0] # pid of process started in first pane

1 Answer 1


I suppose you could wait until the shell running in that tmux pane reads from the terminal device again:

If on Linux,

until grep -qw tty_read "/proc/$pid/stack"; do sleep 1; done

where $pid is the process id of that shell.

Or more efficiently and with a smaller delay with zsh:

zmodload zsh/zselect
until [[ $(</proc/$pid/stack) = *tty_read* ]] zselect -t 10

Note that you need /proc/sys/kernel/yama/ptrace_scope to contain 0 for that to work (for processes to be able to look into what other processes are doing like that). Beware that changing that value to 0 (with sudo sysctl -w kernel.yama.ptrace_scope=0) slightly reduces the security of the system.

To get a mapping between tmux pane id or index and pid of the process it ran in there, you can do:

typeset -A pane_pid=($(for t (id index) tmux lsp -F "#{pane_$t} #{pane_pid}"))

pid=$pane_pid[%0] # pid of process started in first pane

So combining both:

zmodload zsh/zselect
wait_for_shell_on_pane_to_read_tty() {
  local pane=$1
  local -A pane_pid=($(for t (id index) tmux lsp -F "#{pane_$t} #{pane_pid}"))
  until [[ $(</proc/$pane_pid[$pane]/stack) = *tty_read* ]] zselect -t 10

pane=%0 # replace with the pane you want to send those commands to
        # see tmux lsp for the list of panes

for cmd (
  'repeat 5; do print This is a test; sleep 1; done'
) {
  wait_for_shell_on_pane_to_read_tty $pane
  tmux send-keys -t $pane $cmd Enter

Note that since we're sending those command lines in sequence without delay between them, the shell in the pane may still be reading from the tty after we've sent the first command (and hasn't executed it yet) which could cause several command lines to be sent at once.

  • Thanks, I've just edit the question for more details, can you take a look? Commented Sep 19, 2018 at 13:35
  • @TuyenPham see edit Commented Sep 19, 2018 at 15:37
  • I've got this error when trying to execute the snippet: error when reading /proc/28850/stack: operation not permitted Commented Sep 20, 2018 at 2:58
  • Thanks, I've just revised this issue, is there anyway that we can avoid /proc/<..>/stack, it sticks to linux distributions and is that something for cmd ( <..> ) {...} work with bash? Commented Jan 5, 2019 at 1:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .