20

I would like my tcsh script to launch an editor (e.g., vi, emacs):

#!/bin/tcsh
vi my_file

This starts up vi with my_file but first displays a warning "Vim: Warning: Output is not to a terminal" and my keystrokes don't appear on the screen. After I kill vi, my terminal window is messed up (no newlines), requiring a "reset". I tried "emacs -nw", "xemacs -nw", and pico with similar results. "xemacs" works but launches a separate window. I want to reuse the same terminal window.

Is there a way to launch an editor from a script so that it reuses the same terminal window?

matpie
  • 17,033
  • 9
  • 61
  • 82
Marc Eaddy
  • 1,762
  • 3
  • 16
  • 23
  • Normally I do exactly what you do and it works fine for me. But my shell is bash. This kind of thing also works fine under sh. Can you do a "which vi" to find out if something other than vi is being called? And... what kind of operating system environment are you running in? – Carl Smotricz Nov 06 '09 at 20:48
  • 1
    With bash and vim you can just do vi my_file from within the script. I am not aware why you would have to do anything special. – Brian Rasmussen Nov 06 '09 at 21:06
  • Are you trying to get vim to read a FILE or the contents of VARIABLE? – matpie Nov 06 '09 at 22:42
  • What OS/terminal application and version of Vim are you using? I had no trouble with your script when I tried it. – Nicholas Riley Nov 07 '09 at 03:30

7 Answers7

21

I answered my own question! You have to redirect terminal input and output:

#!/bin/tcsh
vi my_file < `tty` > `tty`
Marc Eaddy
  • 1,762
  • 3
  • 16
  • 23
11

The reason you're getting the error is that when you start a shell in your environment, it's starting in a subshell that has STDIN and STDOUT not connected to a TTY — probably because this is in something like a pipeline. When you redirect, you're opening a new connection directly to the device. So, for example, your command line turns

$ vi < `tty` > `tty`

into

$ vi < /dev/ttys000 > /dev/ttys000

So you're not really using your old STDIN/STDOUT, you're creating two new files and mapping them to your vi process's STDIN/STDOUT.

Now, tell us what you're doing with this and we'll tell you how to avoid this kludge.

Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
  • I'm not creating new files. I'm redirecting my terminal device file (tty tells you which file it is), which is associated with my terminal window and existed prior to running my script, to the STDIN/STDOUT of vi. I created a script that wraps "cvs commit" and does a lot of extra checks (e.g., runs our test suite) before committing a check-in. This is sometimes called a "gated checkin." For reasons I won't go into, I can't use the standard CVS pre-commit hook. I'm trying to emulate how "cvs commit" launches an editor when you don't specify a commit message on the command line. – Marc Eaddy Nov 06 '09 at 22:38
6

I wanted to do something similar. I wanted an alias that would find the last file I was working on, and open it in vi(1) for editing. Anyway, I couldn't figure out how to do it as a readable alias (in tcsh) so I just created an ugly shell script (csh because I'm old) instead:

#!/bin/csh

set DIR = "~/www/TooMuchRock/shows/"

set file = $DIR`ls -t $DIR | head -1`
set tty = `tty`

vi $file <$tty >$tty

(1) kraftwerk:bin> which vi vi: aliased to /usr/local/bin/vim -u ~/.exrc

Sid Much Rock
  • 61
  • 1
  • 1
3

Had the same trouble with 'pinfo' in a shell script 'while' loop. The line can be used in the script, it uses 'ps' to find the tty of the current process number, "$$", and stores that tty in $KEY_TTY:

  KEY_TTY=/dev/`ps | grep $$ | tr -s '[:blank:]' | cut -d " " -f 3`

Later in the script, just call the tty-only proggie, with $KEY_TTY as input, in my case it was:

  pinfo -m $s $page < $KEY_TTY

For 'vi' it'd be:

  vi $a < $KEY_TTY > $KEY_TTY

The advantage is that the script as a whole can still accept STDIN input, and 'vi' (or whatever) should work fine -- without having to remember to set any environmental variables before running the script.

AGC
  • 31
  • 1
  • Needs `cut -d ' ' -f 2` for me instead on Ubuntu 18.04. Ended up just using `ps otty= $$` instead of any pipeline. – Steven Lu Jan 15 '20 at 18:21
3

Absolutely. :-)

Write your script and have it call the EDITOR environment variable, which you will have set to "emacsclient". Then start up Emacs, execute M-x server-start, switch to a shell buffer (M-x shell) and execute your script. Emacsclient will pop up the thing to be edited and C-x # will act as a "done" command and take you back to your script with edits completed or aborted, as you choose.

Enjoy.

Edit: I meant to add that these days Emacs IS my terminal program. I have dozens of shell buffers and never have to worry about losing output and can use all the power of Emacs to manipulate and analyse the terminal output. And have Emacs scripts generate input to the shells. Awesome actually. For example, watching Tomcat output scroll by in a shell buffer while editing sources or processing mail or doing most any Emacs thing is very convenient. When a Tomcat stack trace appears I can quickly respond to it.

pajato0
  • 3,628
  • 3
  • 31
  • 37
3

Set your terminal tty to a variable, and then redirect the editor i/o through that variable.

In your script:

#!/bin/sh

ls | while read a; do vi $a < $MYTTY >$MYTTY; done

And then execute the script with:

$ MYTTY=`tty` ./myscript >/tmp/log
slm
  • 15,396
  • 12
  • 109
  • 124
Sami
  • 3,263
  • 3
  • 29
  • 37
2

I was able to get the desired behavior under bash+Cygwin+Terminator:

#!/bin/bash
vim foo

Run the script, vim loads, no error messages, behaves as normal. There are undoubtedly dozens of variations between our setups, however, so I can't hazard a guess as to what makes the difference. I'm curious what it is, but you got it working, which is the important part.

qid
  • 1,883
  • 10
  • 15