30

I use Ubuntu 16.04 and I need the following tmux solution because I want to run a timeout process with sleep as in my particular case I wasn't satisfied from at and encountered a bug with nohup (when combining nohup-sleep). Now, tmux seems as best alternative as it has its own no-hangup mechanism and is actually working fine in manual usage (I ask the question only in regards to automizing the process I can already do manually with it).

What I need:

I need a way to do the following 3 actions, all in one operation:

  1. Attaching a new tmux session.
  2. Injecting a ready set of commands to that session, like (sleep 30m ; rm -rf dir_name ; exit). I would especially prefer a multi-line set, and not one long row.
  3. Executing the above command set the moment it was finished to be written as stdin in new tmux session.

In other words, I want to execute a code set in another tmux session that was specially created for that cause, but to do all in one operation.


Notes:

  • I aim to do all from my original working session (the one I work from most of the time). Generally, I have no intention to visit the newly created session, I just want to create it with its automatically executed code and that's it.

  • If possible, I would prefer an heredoc solution. I think it's most efficient.

3 Answers3

47

If you put the code you want to execute in e.g. /opt/my_script.sh, it's very easy to do what you want:

tmux new-session -d -s "myTempSession" /opt/my_script.sh

This starts a new detached session, named "myTempSession", executing your script. You can later attach to it to check out what it's doing, by executing tmux attach-session -t myTempSession.

That is in my opinion the most straightforward and elegant solution. I'm not aware of any easy way of execute commands from stdin (read "from heredocs") with tmux. By hacking around you might even be able to do it, but it would still be (and look like) a hack.

For example, here's a hack that uses the command i suggested above to simulate the behaviour you want (= execute code in a new tmux session from a heredoc. No write occurs on the server's hard drive, as the temporary file is created /dev/shm, which is a tmpfs):

(
  cat >/dev/shm/my_script.sh &&
  chmod +x /dev/shm/my_script.sh &&
  tmux new-session -d '/dev/shm/my_script.sh; rm /dev/shm/my_script.sh'
) <<'EOF'
    echo "hacky, but works"
EOF
Mario Vitale
  • 988
  • 9
  • 12
  • 1
    Dear Mario. Always a pleasure to read your answers. I am not sure if I understand correct, please tell me if I'm wrong: `1.` There are two ways to do so, one involves first creating a temporary script (that contains the code) and execute it with creation and the second is an heredoc way. `2.` If the second way works (when each echo row contains each code row of the heredoc) why should we see this as a hack if it's a Bash valid code? Maybe because rows of the heredoc are echoed and not executed regularly in heredocs? –  Jan 06 '17 at 05:15
  • 3
    @Benia It's a hack since it only works on some Unix systems that have `/dev/shm`. It's also a hack since it's more complicated, difficult to read, to understand and to maintain than simply writing the script to an ordinary file and starting that with `tmux`. – Kusalananda Jan 06 '17 at 08:30
  • I think I miss or confused about something - "Starting that with `tmux`"; Can you elaborate this saying and explain what you were doing? –  Jan 06 '17 at 08:52
  • 1
    As @Kusalananda explained, heredocs for code execution are bad practice in general. Altough ugly and unmantainable, it works, but it forces you to find roundabout way of doing otherwise *very* simple stuff...just compare the first and second code blocks in the answer: can you tell how it works? How long does it take you to do that? It's _even more_ of a hack as tmux has no way of executing commands from stdin (save for even-more-hacky ways so ugly i won't even consider), so external scripts are the *only* (answer to the `1.` of your question) way. – Mario Vitale Jan 06 '17 at 11:51
  • I just ran `tmux -d -s "myTempSession" /opt/my_script.sh` but I then got this output: `usage: tmux [-2CluvV] [-c shell-command] [-f file] [-L socket-name] [-S socket-path] [command [flags]]`. –  Jan 06 '17 at 15:20
  • It happens wether I'm in the native shell session or in a tmux session. –  Jan 06 '17 at 17:52
  • 1
    Sorry, there was a mistake in the first sample (the "new-session" command is not implied if you use arguments). Now it's fixed. – Mario Vitale Jan 06 '17 at 18:27
  • 1
    Now when I execute `tmux new-session -d -s "myTempSession" /opt/my_script.sh` it does not open a temporary file for me to paste the code... What I need is to open the script directly from current session, or at least something to move to the new session and then directly go back to the current session. BTW I plan to give bounty for all of your help as it is the least I can do to thank you and I could give bounty in 11 hours. –  Jan 07 '17 at 03:37
  • 1
    As the first line of my answer says, my suggestion is to put the code you want to execute in `/opt/my_script.sh`...**instead** of copy-pasting it every time. If however you want to keep copy-pasting it, the hack i posted will work fine: you can paste your code in the heredoc space between `'EOF'` and `EOF` (NB: some kernels will not have `/dev/shm`, in which case you can just replace every instance of `/dev/shm/my_script.sh` with `/tmp/my_script.sh`). I'm flattered about the bounty but it won't be necessary, as with each question i keep learning too. – Mario Vitale Jan 07 '17 at 10:26
  • Oh, I confused to think that the second code block actually starts a temporary script I could paste code into (instead of direct pasting as with heredoc). I now see that it just executes `/opt/my_script.sh` itself, from current session ---> In another `tmux` session. –  Jan 07 '17 at 11:41
  • The second script _does execute the temporary code you paste in the heredoc_; try putting any code you want instead of the `echo`. The fact that you find it confusing proves the point me and Kusalananda made. I strongly, *strongly* suggest that you use the first solution, especially if you find it difficult to understand the (unnecessarily complex) second code block. – Mario Vitale Jan 07 '17 at 23:33
  • 1
    I no longer find it confused... I studied it and wrote a summary about it in a file. I prefer using the second way because by principle I don't want to store any scripts on the server and I do want to know heredocs better. –  Jan 08 '17 at 06:37
  • Mario, there shouldn't be a major problem changing all /dev/shm's in /tmp/ or /root/ right? Because I do have such a problem: https://unix.stackexchange.com/questions/362276/tmux-scripts-wont-be-executed-bash-path-script-sh-no-such-file-or-directory –  Apr 30 '17 at 16:40
  • If permissions are correctly configured, then there's no problem at all. Seeing you removed the question, i guess you found this out yourself – Mario Vitale Apr 30 '17 at 22:52
  • Hi, when I run `tmux new-session -d -s "myTempSession" /opt/my_script.sh`, tmux executes with no output. But, when I run `tmux list-sessions`, I get back `no server running on /private/tmp/tmux-501/default`. When I run `tmux attach -t myTempSession`, I get `no sessions`. I am running on a Mac. Any idea why this isn't working? I am trying the same on raspberry pi with the same result, running raspbian. For further clarification, I am trying to run a python script, so replaced `/opt/my_script.sh` with `python myscript.py`. – jaredad7 Jul 13 '18 at 12:50
  • It's possible that your script runs and exits quickly, before you try executing `tmux list-sessions`; you can verify this by introducing a sleep at the end of your script, and then checking if you're able to attach to the session. The above solution does not let you store the output of a program after it has exited, as that was not the asker's use case. – Mario Vitale Jul 13 '18 at 22:12
  • `tmux new-session irssi` works for me – daparic Feb 07 '21 at 03:11
3

Also see https://serverfault.com/questions/339390/run-command-in-detached-tmux-session for examples on using the send-keys command on a detached pane.

user3751385
  • 161
  • 5
1

Mixing the above solution I came up with another way to achieve the same (assuming bash):

#!/bin/bash

wantTmuxExecute() {
   sleep 30m ;
   rm -rf dir_name ; 
   ...
}
# send to tmux
type wantTmuxExecute | tail -n +2 > /dev/shm/wantInTmux.sh

# execute tmux
tmux new-session -d 'source /dev/shm/wantInTmux.sh ; rm /dev/shm/wantInTmux.sh'
tmux send-keys 'wantTmuxExecute'
JrBenito
  • 111
  • 5
  • Why do you need the `type wantTmuxExecute | tail -n +2 > /dev/shm/wantInTmux.sh` line here? – a06e Apr 27 '22 at 08:27
  • In bash, a `type someFunction` will print the code of `someFunction`, pipe this output to tail to remove lines and direct the output to a shared memory area `/dev/shm` to a file `wantInTmux.sh`. This shared memory will be available inside the tmux session. If you have that something already in another file and executable, do not need it. – JrBenito May 19 '22 at 22:46