Monday, May 09, 2005

GNU screen hack-fu: running one command in multiple windows

I've long used the wonderful GNU screen text-windowing system, which allows you to do lots of neat things like detach a terminal session and reattach later, and switch between multiple terminal `windows' within a single session.

When you have lots of gnu screen windows open, sometimes it'd be nice to run the same command on several of them -- for example, cd several windows to the same directory, or ssh to the same server. I've always done this by copy-and-pasting, though there are some funky commands in screen for copying and pasting text between windows.

Now, thanks to the screen -X command and a short shell script, I can do this very easily.

I name the script screenex, and use it as follows:

[03:36 PM mithras@powerbook: ~] screenex 1 6 ls In windows 1 - 6, doing: ls

I simply pass the range of window numbers to run the command in, followed by the command itself. I can also omit the second number, to execute the command on just one other window:

[03:39 PM mithras@powerbook: ~] screenex 2 pine In windows 2 - 2, doing: pine

Neat! This becomes much more powerful, though, when you add substitution of the window number. When you include the token --INDEX-- in the command, the window number is substituted for --INDEX--. I use this to launch ssh connections to six machines at once, which is handy when I'm running a bunch of jobs in parallel. At work we have a bunch of servers, named,, etc. So to connect to a different machine in each window, I just run

[03:41 PM mithras@powerbook: ~] screenex 1 6 ssh In windows 1 - 6, doing: ssh

And I'm connected, ready to run my parallel jobs! You can also do cool stuff if you have environment variables that are indexed by number, for example WORKING_DIR_1, WORKING_DIR_2, etc. Anyway, I'll save more for later.

The script:

(Note that the ^M character is a literal newline character, which you can enter in vi by typing ctrl-V, then enter.)

[ Click to show the entire script ]
#!/bin/sh # # command to run the given command on multiple `GNU screen' windows # usage: screenex [begin-window] [end-window] command # # by Mithras The Prophet (mithras.the.prophet, which is a gmail account.) # # # returns 1 if arg is numeric, 0 otherwise # is_numeric() { var="$1" if [ -z "$var" ]; then echo "0" else [ "$var" -eq 0 ] 2> /dev/null if [ $? -eq 0 -o $? -eq 1 ]; then echo "1" else echo "0" fi fi } # # if argument 1, or arguments 1 and 2 are numeric, then # we apply the command to just that range of windows; # otherwise, to all windows # mycommand="" window_begin="1" # beginning of range of windows to send command to window_end="6" # and end arg1="$1" shift; if [ -z "$arg1" ]; then exit; fi if [ $(is_numeric "$arg1") -eq 1 ]; then # this is beginning of range window_begin="$arg1" # try to grab a second numeric arg? arg2="$1" shift; if [ $(is_numeric "$arg2") -eq 1 ]; then # we have a second number! window_end="$arg2" else # ahve second arg, but not a number # so it must be start of command window_end="$window_begin" # just do the one window mycommand="\"$arg2\"" fi else # arg1 -not- numeric, # so it must be start of command mycommand="$arg1" fi while [ ! -z "$1" ] do mycommand="$mycommand \"$1\"" # rest of line shift done echo "In windows $window_begin - $window_end, doing: $mycommand" # # now execute! # let "window_end = $window_end + 1" # so we can do strict -lt test index="$window_begin" while [ $index -lt $window_end ] do thiscommand=${mycommand/--INDEX--/$index} screen -X at ${index}# stuff "${thiscommand}^M" let "index = $index + 1" done


Post a Comment

<< Home