Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting TERM variable for Emacs shell

Is it possible to set the TERM environment variable for Emacs shell to some other value than "dumb"? In order to make TRAMP work I'm adjusting some parts of .bashrc on remote machines according to $TERM == "dumb" condition but for the shell I'd like these ignored (opposite approach - setting the TERM for TRUMP - would also be applicable).

like image 464
Tomas Varga Avatar asked Feb 02 '18 01:02

Tomas Varga


People also ask

What is term xterm?

In computing, xterm is the standard terminal emulator for the X Window System. It allows users to run programs which require a command-line interface. xterm.

How do I use Emacs shells?

7.7 How do I use a shell in Emacs? You can start an interactive shell in Emacs by typing M-x shell . By default, this will start the standard Windows shell cmd.exe . Emacs uses the SHELL environment variable to determine which program to use as the shell.

Is Emacs its own shell?

Emacs comes with its own shell (as in, like bash or zsh ) written in entirely in Emacs-Lisp. The shell, named Eshell, is a feature-rich replacement for your standard-fare shells like bash with the added bonus of working on any platform Emacs runs on.


1 Answers

Let me give your answer in two parts: first, one that technically answers your question but isn't very useful, and second, one that probably answers your needs even though it takes a different tack to get there.

Setting TERM in the shell

When starting shell-mode with M-x shell, Emacs starts the shell you want (usually the same as your login shell, but this can be changed if you really want to) and then sources a file, if it exists, based on the shell's name. The places it looks are

  1. ~/.emacs_$SHELLNAME
  2. ~/.emacs.d/init_${SHELLNAME}.sh

In this file you can set TERM. For instance, if you use zsh, and create the file

# ~/.emacs_zsh or ~/.emacs.d/init_zsh.sh
export TERM=emacs

then you'll get what you asked for: shells started with M-x shell will have the TERM set to emacs instead of dumb.

While this technically answers your question, though, it's not particularly useful—if you try it, here's what you'll get when you start a shell:

zsh: can't find terminal definition for emacs
$ echo $TERM
emacs
$

The issue here is that shell-mode doesn't implement any terminal emulation. In other words, dumb is exactly the right TERM value to use.

(Note there are modes that emulate terminals—see, for instance, M-x ansi-term—and they set TERM=eterm-color or similar, but they're designed to let you use Emacs as an xterm replacement for visual-mode shell commands, whereas M-x shell is designed to let you run shell commands while still interacting with the input and output in an Emacs-y way.)

When you choose a TERM value that isn't supported by termcap, you'll get the above error and some programs may get confused about what's happening (and some will refuse to run at all). If you select a full-featured TERM value like xterm instead, you'll get "line-noise" characters as the programs attempt to send formatting codes to a terminal emulator that isn't there.

You could probably get away with finding some termcap that was limited enough in capabilities that it won't bother you with noise too much, but if this is just to let you differentiate Emacs interactive shells from either Emacs non-interactive shells or non-Emacs interactive shells, there's a better choice.

This choice, in fact, isn't even sufficient. That's because this special shell script is loaded by both shell-mode and by TRAMP, so the above still wouldn't let you differentiate the two—you'd just get emacs in both cases instead of dumb!

So this is where the better choice comes in:

Using the INSIDE_EMACS environment variable

While, as you noted, the interactive shell and TRAMP both set TERM to dumb by default, they also set an environment variable INSIDE_EMACS.

Its existence (or not) alone is useful for your shell startup scripts, but its power for your use case lies in its value—which, for interactive (M-x shell) use, is something like 25.2.2,comint, but for TRAMP is 25.2.2,tramp.

So, to check for the three cases, here's what you can do (and what I personally have done myself in my ~/.zshrc for many years):

# Setup for all shells--Emacs or not, interactive or not, goes
# here
PATH=...
source $my_functions_file

# Now dumb terminals
if [[ "${TERM}" == "dumb" ]]; then
  # Here put anything you want to run in any dumb terminal,
  # even outside Emacs.
  PATH=...
  alias lsF='ls -F'
  etc

  # Now, just configs for shells inside Emacs
  case ${INSIDE_EMACS/*,/} in
    (comint)
      do_comint_stuff
      ;;
    (tramp)
      do_tramp_stuff
      ;;
    (term*)
      # For M-x ansi-term, etc., you get a value like
      #   25.2.2,term:0.96, but those shouldn't coincide with
      #   TERM being `dumb`, so warn....
      echo "We somehow have a dumb Emacs terminal ${INSIDE_EMACS/*,/}" >&2
      ;;
    ("")
      # Empty means we're $TERM==dumb but not in Emacs, do nothing
      ;;
    (*)
      # We shouldn't get here, so write a warning so we can
      # figure out how else Emacs might be running a shell,
      # but send it to stderr so that it won't break anything
      echo "Something is wrong: INSIDE_EMACS is ${INSIDE_EMACS}" >&2
      ;;
  esac

  # finish shell setup for dumb now--the rest of the file will
  # be skipped
  return
fi

# Stuff for non-dumb, interactive visual, shells goes here
setup_prompt
setup_keybindings
etc

We don't reset TERM to a different value inside the case statement because dumb is exactly what it should be.

Note that above, in the (tramp) section of the case statement, you could do what you mentioned in your question—set TERM to something else just for TRAMP—but that would be a bad idea, since Emacs actually reads and acts on responses it gets from TRAMP shells, and the line noise would be an even bigger problem. TRAMP can do some really amazing things, but only when the shell output it's reading is in the format TRAMP expects.

(One final thing: using the code as above, with the INSIDE_EMACS checks nested instead the dumb terminal check, we don't have a single place to put code to be run in all Emacs-spawned shells regardless of type, including M-x ansi-term and its ilk. You could write a separate statement for that in your shell config... but that's exactly what ~/.emacs.d/init_${SHELLNAME}.sh is for, so probably a better choice if you need this for some reason.)

like image 133
Trey Avatar answered Oct 06 '22 21:10

Trey