How to run tmux with shell command - bash

I want to run tmux with shell command in it
I try:
tmux new-session -d -s foo 'echo intmux'
( I want to use this later in bash script)
But it doesn't works. I expect that:
1) Tmux open new window ( like tmux new )
2) command echo intmux will be called

1) Tmux open new window ( like tmux new )
You will not see a new session, because you create it using -d (detached) flag.
What you want (execute a command and leave a shell open) can be easily achieved with:
tmux new -s foo 'echo intmux; $SHELL'
Another tricky option bound to specific (bash) shell:
tmux new-session -s foo 'bash --rcfile <(echo ". ~/.bashrc; echo intmux")'
This allows to preserve default behavior (reading . ~/.bashrc) and pass your arbitrary command.

You can run
tmux new-session '-s foo' \; neww '-t 2 -n echo' mc
But that spawns another window in the new session running your command so possibly you want to close the first window
tmux new-session '-s foo' \; neww '-n echo' mc \; kill-window '-t 1'

Related

How to completely close terminal (Ubuntu bash) from TMUX session?

Can someone please help as how can I close the bash terminal completely from inside of TMUX session (closing TMUX session as well)?
Below code is from my .bashrc where TMUX session starts as soon as terminal launches
if command -v tmux &> /dev/null && [ -z "$TMUX" ]; then tmux attach -t default -c "$HOME" || tmux new -s default -c "$HOME" fi
Thanks!
You can use tmux detach -P to both detach and send HUP to the parent process, which should make it exit as well.

Run command in new background tmux window and wait for process to finish

I'm trying to use tmux in a script, so that it runs a command that takes some time (let's say 'ping -c 5 8.8.8.8', for example) in a new hidden pane, while blocking the current script itself until the ping ends.
By "hidden pane", I mean running the command in a new pane that would be sent in background, and is still accessible by switching panes in order to monitor and/or interact with it (not necessarily ping).
(cf. EDIT)
Here is some pseudo bash code to show more clearly what I'm trying to do:
echo "Waiting for ping to finish..."
echo "Ctrl-b + p to switch pane and see running process"
tmux new-window -d 'ping -c 5 8.8.8.8' # run command in new "background" window
tmux wait-for # display "Done!" only when ping command has finished
echo "Done!"
I know the tmux commands here don't really have any sense like this, but this is just to illustrate.
I've looked at different solutions in order to either send a command in background, or wait until a process has finished in an other pane, but I still haven't found a way to do both correctly.
EDIT
Thanks to Nicholas Marriott for pointing out the -d option exists when creating a new window to avoid switching to it automatically. Now the only issue is to block the main script until the command ends.
I tried the following, hoping it would work, but it doesn't either (the script doesn't resume).
tmux new-window -d 'ping -c 5 8.8.8.8; tmux wait -S ping' &
tmux wait $!
Maybe there is a way by playing with processes (using fg,bg...), but I still haven't figured it out.
Similar questions:
[1] Make tmux block until programs complete
[2] Bash - executing blocking scripts in tmux
[3] How do you hide a tmux pane
[4] how to wait for first command to finish?
You can use wait-for but you need to give it a channel and signal that channel when your process is done, something like:
tmux neww -d 'ping blah; tmux wait -S ping'
tmux wait ping
echo done
If you think you might run the script several times in parallel, I suggest making a channel name using mktemp or similar (and removing the file when wait-for returns).
wait-for can't automatically wait for stuff like pane or windows exiting, silence in a pane, and so on, but I would like to see that implemented at some point.
The other answers are only working if you're already within a tmux session.
But if you are outside of it you've to use something like this:
tmux new-session -d 'vi /etc/passwd' \; split-window -d 'vi /etc/group' \; attach
If you want to call this within a script you should check whether or not "$TMUX" is set. (Or just unset to force a nested tmux window).
#!/bin/sh
export com1="vi /etc/passwd"
export com2="vi /etc/group"
if [ -z $TMUX ]
then
export doNewSession="new-session -d 'exit 0'"
else
export doNewSession=""
fi
tmux $doNewSession \; split-window -d $com1 \; split-window -d $com2 \; attach;
[ -z $TMUX ] && exit 0
My solution was to make a named pipe and then wait for input using read:
#!/bin/sh
rm -f /wait
mkfifo /wait
tmux new-window -d '/bin/sh -c "ping -c 5 8.8.8.8; echo . > /wait"'
read -t 10 WAIT <>/wait
[ -z "$WAIT" ] &&
echo 'The operation failed to complete within 10 seconds.' ||
echo 'Operation completed successfully.'
I like this approach because you can set a timeout and, if you wanted, you could extend this further with other tmux controls to kill the ongoing process if it doesn't end the way you want.

compound command with bash -c in tmux vs screen

I need to run a command with bash -c within a tmux session, from within a shell script. In contrast to screen, tmux seems to require to quote the entire command, which leads to problems as bash -c also requires quoting for correct functioning with more complex command strings.
In the following I'm trying to demonstrate the behavior with a minimal example. What I'm trying to achieve involves more complex commands than ls of course. Also for my purpose it is necessary to expand the CMD variable, as it is built in the script before.
A minimal script for screen:
#!/bin/bash
set -x
CMD="ls -l; sleep 5"
screen -d -m bash -c "$CMD"
When executing this script you get (stdout due to -x)
+ CMD='ls -l; sleep 5'
+ screen -d -m bash -c 'ls -l; sleep 5'
The sleep command is for having time to attach to the screen session and see what happens. When attaching to the screen session after executing the above script one sees that the output of the ls command is in long list format, i.e. the command is executed properly.
In tmux it seems one has to quote the command to get it executed in the new session. I use the following script:
#!/bin/bash
set -x
CMD="ls -l; sleep 5"
tmux new -d "bash -c $CMD"
The stdout is
+ CMD='ls -l; sleep 5'
+ tmux new -d 'bash -c ls -l; sleep 5'
As one can see, the cmd sequence for bash -c is not quoted properly anymore. When attaching to the created tmux session one can see, that this results in ls being executed without the long list option recognized.
What can I do to get the proper quoting (i.e. single quotes around the expanded string) for the $CMD string passed to bash -c?
Update
Escaping, as Eric Renouf suggested, with \"$CMD\" produces
tmux new -d 'bash -c "ls -l; sleep 5"'
and escaping with '$CMD' produces
tmux new -d 'bash -c '\''ls -l; sleep 5'\'''
Both works for the provided minimal example, but is still not exactly what screen produces and does not work in my case.
Here are the exact call I'm making (see here for all the gory details):
$SCREEN -S "scalaris_$NODE_NAME" -d -m bash -x -f +B -c "$START_CMD; sleep 365d"
which produces (output of -x)
/usr/bin/screen -S scalaris_first#pvs-pc07.zib.de -d -m bash -x -f +B -c '"/usr/bin/erl" -setcookie "chocolate chip cookie" -pa /home/jvf/code/scalaris/contrib/yaws/ebin -pa /home/jvf/code/scalaris/contrib/log4erl/ebin -pa /home/jvf/code/scalaris/ebin -sasl sasl_error_logger false -yaws embedded true -scalaris log_path "\"/home/jvf/code/scalaris/log/first#pvs-pc07.zib.de\"" -scalaris docroot "\"/home/jvf/code/scalaris/docroot\"" -scalaris config "\"/home/jvf/code/scalaris/bin/scalaris.cfg\"" -scalaris local_config "\"/home/jvf/code/scalaris/bin/scalaris.local.cfg\"" -connect_all false -hidden -name first#pvs-pc07.zib.de -scalaris start_type first -scalaris port 14195 -scalaris yaws_port 8000 -scalaris join_at_list '\''[0]'\'' -scalaris start_mgmt_server true -scalaris nodes_per_vm "1" -s scalaris +sbt db +swt low +sbwt short'
I think the solutions suggested so far do not work because of the use of double quotes within the command, but I'm not 100% positive about that. How can I reproduce the exact quoting screen produces (single quotes around the complete command passed to bash -c) with tmux?
I'm trusting that you need to do this for tmux, but to get the extra quotes you want, you could just escape them in the outer quote like:
tmux new -d "bash -c \"$CMD\""
or you could put those in single quotes in most cases like
tmux new -d "bash -c '$CMD'"
since the $CMD will be expanded by the outer quotes

How to write a shell script that starts tmux session, and then runs a ruby script

I want to write a shell script that does this:
First, create a tmux session
Second, run a ruby script called "run.rb" INSIDE the tmux session
In pseudo-code, what I want to do:
tmux new -s my_session
ruby run.rb # NOTE: I want this to run inside the my_session tmux session.
tmux detach
How do I do this? (More posts I read, more confusing it gets.)
#!/bin/bash
tmux new-session -d -s my_session 'ruby run.rb'
Create a file named my_script.sh and give it the above contents.
Make the file executable by running:
chmod 755 my_script.sh
or
chmod +x my_script.sh
Then run the shell script:
./my_script.sh
Making the shell script executable
When you perform the chmod 755 filename command you allow everyone to read and execute the file, and the file owner is allowed to write to the file as well. You may need this for Perl and other scripts that should be run via a webserver. If you apply 755 to a directory, it means that everyone can go to it and get its file listing.
These permissions are usually translated into textual representation of rwxr-xr-x.
You can alternatively use chmod +x file_name on a file to make it executable.
K M Rakibul Islam's updated code contains an unnecessary detach command at the end which causes an error message "no client found" (my_session has already been detached and thus is not in scope so tmux cannot understand which session you want to detach). The correct code should be:
#!/bin/bash
tmux new-session -d -s my_session 'ruby run.rb'
With some experimenting, I figured out how to control tmux via shell script.
tmux new-session -d -s htop-session 'htop'; # start new detached tmux session, run htop
tmux split-window; # split the detached tmux session
tmux send 'htop -t' ENTER; # send 2nd command 'htop -t' to 2nd pane. I believe there's a `--target` option to target specific pane.
tmux a; # open (attach) tmux session.
The above splits the tmux session into two window, and runs htop in both.
To answer original question, you can run a ruby script and not detached the tmux session with command below:
tmux new-session -s ruby_session 'ruby run.rb'; # open tmux session and run ruby script.
You could use teamocil to do this easily. You could just create a YAML file:
windows:
- name: rubysession
root: ~
layout: tiled
panes:
- ruby run.rb; tmux detach
If you named it 'rubysession.yml' then run:
teamocil rubysession
And that would work perfectly for your purpose and require no hacks. Also teamocil is awesome for loads of other uses!
If you want to keep your tmux session alive after starting some commands, a possible solution is to start a bash with an init file:
tmux new -d -s mysession "bash --init-file foo.script"
where foo.script would contain your commands. Alternatively, you can feed the command to the shell directly from the command line:
tmux new -d -s mysession2 "bash --init-file <(echo ruby run.rb)"
Note that --init-file was meant for reading system wide initialization files like /etc/bash.bashrc so you might want to 'source' these in your script.
I am not sure if this is still interesting for you, but I like to give you an answer / hint: in case you want, for example, start multiple tmux sessions by shell script and execute some command, you can do it like follow:
# just for test and show case
mkdir test_1 test_2
echo "current tmux sessions"
tmux ls
echo "kill all tmux sessions"
tmux kill-server
declare -a directories=("test_1" "test_2")
for i in "${directories[#]}"
do
cd ${i}
pwd
tmux new -d -s ${i} "ls -la"
cd ..
done
For the demonstration, the script will create a folder test_1 and test_2. After that I have defined an array with the two folders and run through the two folders and start a tmux session with the current folder name and execute the command "ls -la".
If you like to run through all sub directories in your current directory, please replace "for i in "${directories[#]}" with "for f in *; do". Here is an example that also exclude symbolic folders:
echo "current tmux sessions"
tmux ls
echo "kill all tmux sessions"
tmux kill-server dependencies
for f in *; do
if [[ -d "$f" && ! -L "$f" ]]; then
cd ${f}
pwd
tmux new -d -s ${i} "ls -la"
cd ..
fi
done
Here is a link to the gist file: https://gist.github.com/AICDEV/cf1497793bb1c27cb9b94d56c209ad6f

How can I start the gnome-terminal with multiple tabs with always the same title and history?

I want to start the bash with 4 tabs, having different titles. In all of them I'm working in different directories so it would be useful if I can cd to different paths.
Now it would be also great to save the history separately for every tab. So that every tab only remembers the commands I ran on it, even after a reboot.
Currently I have a script which starts gnome-terminal with 4 tabs.
gnome-terminal --geometry=150x50 --tab --title="src" -e "bash -c \"cd "~/path/to/src";exec bash\"" --tab --title="first test" -e "bash -c \"cd "~/path/to/single-test-dir";exec bash\"" --tab --title="test3" -e "bash -c \"cd "~/path/to/testdir";exec bash\"" --tab --title="test4" -e "bash -c \"cd "~/path/to/somewhere";exec bash\""
I suppose you can use a gnome-terminal custom command for each profile, for example
bash -c 'PROFILE=default_profile exec bash'
or
bash -c 'PROFILE=screen_profile exec screen -U'
or similar.
Then in ~/.bashrc
if [[ -n $PROFILE ]]; then
HISTFILE=~/.bash_history."$PROFILE"
fi
source of the answer.

Resources