Scripting Screen

Posted by Doug Sat, 13 Sep 2008 14:11:00 GMT

I do pretty much all my work using GNU Screen inside a terminal. I use a different screen session for each project and have pretty much the same window configuration for each session. After finally getting tired of manually setting up my screen sessions, here’s how I managed to script new session setup.

Like many I have a development directory where all my projects live. Inside this devel dir I have the trunk of each project checked out from version control. Most of my projects haven’t been converted to git yet, so I’m not sure how different it’d be compared to svn. My screen session is made up of several windows. Inside the first, I run emacs. The second is autotest. The third I run ./script/server. I also have a window for ./script/console and a database prompt; but the order isn’t that important. After those I’ll have a window or two for misc. bash prompts.

One solution to automatically setting all this up is to put screen commands inside your .screenrc to create and name the windows. You can even specify what command to run in the windows. The problem is that when you exit that command for whatever reason, the screen window is killed with it. That’s not so handy when you need to restart autotest or your server. Also, the .screenrc file doesn’t allow for any type of scripting for dynamically generating any of your startup information.

After digging in the man page a little bit I found a few screen commands that allow it to be scripted fairly well from outside scripts. I’ll show the important commands here.

screen -d -m -S <screen session name>

This will create a new screen session detached but with an input/output stream. The -S names the screen session to make it easier to refer to it later.

screen -X -S <session name> screen -t test 1

This sends a screen command to the screen session with the given name. In this case, the screen command is to create a new screen window named “test” as window 1.


screen -X -S <session name> -p 1 stuff "cd $DEVEL/$PROJECT; autotest
" 

This again sends a screen command to the named session. In this case we’re sending the stuff command directly to window 1. The stuff command sends the string into stdin of the running application in that window. In this case the running app is a bash prompt. Note the trailing newline inside the string will cause the command “typed” at the bash prompt to be run.

The only other problem I ran into is that when creating screen windows like this, they didn’t show up on my hard status line automatically. My solution was to setup a little for loop and select each window like this:


for i in 0 1 2 3 4
do
   screen -X -S <session name> select $i
done

Finally, when all is setup the way you want the last thing to do is join the resulting screen session like this:

screen -x <session name> -p 0

The trailing -p 0 says to start in the first window.

You can download my resulting script here. One thing to note is that I have an entry in /etc/services for each of my projects. I use this so that when I run ./script/server I always start on the same port for each project. Also, I’m making use of bash’s select command for prompting with project (or which already started screen session) to join/start. It’s a little clumsy in that select wants to keep prompting you for input after you’re done with the screen session; but it’s a lot better than what I had.

Posted in  | Tags , ,  | 3 comments

My First Day of Freedom

Posted by Doug Mon, 21 Jan 2008 13:49:00 GMT

Friday was my last day at the company that won’t let me blog about them. I am very excited to be 100% independent freelancing! This has been a goal of mine for many years and it’s finally come true.

This morning Mark Windholtz is coming over to pair with me on a contract we share. It’s his good for not making me commute on my first day of independence. I’m looking forward to working with him and several other independent developers.

Hopefully now that I’m not under corporate thumbs I’ll be able to blog more about the types of problems I’m seeing and methods for overcoming. I’ve specifically been asked to write an article on my experiences working remotely on a Scrum project.

In addition to writing more, I’m also hoping to be able to contribute back to the open source community. It’s tricky when your family depends on your billable hours to also give your work away. However, I think it’s good balance to work on things my clients require as well as things I’m interested in. At first, this is likely to be helping Phil Hagelberg shape up Emacs for ruby development.

So, there you go. I’m going to be working with some good guys; doing more writing; and working on more open source. Wish me luck!

Tags , , , ,  | 4 comments

Jumping to Specific Windows in Emacs

Posted by Doug Thu, 08 Nov 2007 02:58:00 GMT

OK, last emacs tip for a while. It’s ironic, but the last two years I’ve written some cool elisp while at RubyConf. I typically run emacs full screen and split buffers both vertically and horizontally to arrange bits of code. It’s not uncommon for me to end up with three, four, five or even six visible windows in emacs. The problem though is navigating between them.

The out-of-the-box solution is to use other-window (bound to C-x o) and just cycle through them. If you’re fancy you can give a prefix argument to other-window and go backwards. This window cycling is tedious to me though and I’d like something faster. Particularly when I’m mostly bouncing back and forth between two windows.

To solve this problem I wrote jump-to-buffer. Of course, right now I’m really enamored with the fuzzy matching in ido-completing-read. That’s what allows you to type non-sequential characters to match the buffer name. It’s very similar to TextMate’s pattern matching. So, jump-to-buffer uses ido-completing-read. The buffers it gives you as options for completion are only the buffers in visible windows. It also sorts those buffers in the order of the buffer-list; which is in the order of most recently accessed.

To make this work, I’ve written two helper functions: rotate-list, and sort-by-other-list. Without further ado:

(defun dka-sort-by-other-list (to-sort-list other-list)
  (let* ((index 0)
         (other-alist (mapcar (lambda (buffer) 
                                (setq index (+ index 1))
                                (cons buffer index))
                              other-list))
         (swartz (mapcar (lambda (item) 
                           (cons (cdr (assoc item other-alist)) item))
                         to-sort-list))
         (sorted-list (sort swartz (lambda (a b) (< (car a) (car b))))))
    (mapcar 'cdr sorted-list)))

(defun rotate-list (list count)
  "Rotate the LIST by COUNT elements"
  (cond
   ((= count 0) list)
   ((not list) list)
   (t
    (rotate-list (nconc  (cdr list) (list (car list)) '()) (1- count)))))

(defun dka-jump-to-window ()
  "Interactively jump to another visible window based on it's `buffer-name' using `ido-completing-read'"
  (interactive)
  (let* ((visible-buffers (mapcar '(lambda (window) (window-buffer window)) (window-list)))
         (sorted-visible-buffers (dka-sort-by-other-list visible-buffers (buffer-list)))
         (rotated-buffer-list (rotate-list sorted-visible-buffers 1))
         (visible-buffer-names (mapcar (lambda (buffer) (buffer-name buffer)) rotated-buffer-list))
         (buffer-name (ido-completing-read "Enter buffer to jump to: " 
                                           visible-buffer-names
                                           nil t))
         (window-of-buffer
          (delq nil 
                (mapcar '(lambda (window) 
                           (if (equal buffer-name (buffer-name (window-buffer window)))
        window nil)) (window-list)))))
    (select-window (car window-of-buffer)))
)

I’m definitely interested in feedback on this code. The dka-sort-list-by-other-list method was particularly tricky for me to write. I think it’s both right and fast. I haven’t dove into elunit yet to know for sure if it’s right.

Posted in ,  | Tags , , , , ,  | no comments

Older posts: 1 2

Copyright 2001 - 2005 by Lathi.net and Doug Alcorn

Creative Commons, Some Rights Reserved Ruby on Rails Developer Powered by Debian GNU/Linux Powered by Typo