为Emacs shell设置TERM变量

7

Emacs shell的TERM环境变量是否可以设置为除了"dumb"以外的其他值?为了使TRAMP正常工作,我正在根据$TERM == "dumb"条件调整远程机器上.bashrc的某些部分,但是对于shell,我希望忽略这些内容(相反的方法-为TRUMP设置TERM-也适用)。


3
您可以测试INSIDE_EMACS环境变量,而不会干扰TERM(这似乎是一件潜在危险的事情)。 - phils
1
TERM 的作用是告诉程序它们正在运行的终端类型,以便它们可以决定如何控制终端。 TERM=dumb 是正确的,因为 shell 不是一个终端。 如果您将 TERM 设置为其他内容,您将欺骗查看该变量的任何程序,但结果将是错误的,因为终端本身不会按照指示的方式行事。 - jpkotta
谢谢,我现在明白了,如果我想要一些非愚蠢的东西,最好使用Emacs term - Tomas Varga
1个回答

12
让我分两部分回答您的问题:第一部分是技术上回答您的问题,但并不是很有用;第二部分则是可能能够回答您需要的内容,尽管它采取了不同的方法。
在shell中设置TERM
当使用“M-x shell”启动shell-mode时,Emacs会启动您想要的shell(通常与登录shell相同,但如果您真的想要更改,则可以更改),然后基于shell的名称查找文件(如果存在)。它查找的位置如下:
1. ~/.emacs_$SHELLNAME 2. ~/.emacs.d/init_${SHELLNAME}.sh
您可以在此文件中设置TERM。例如,如果您使用zsh,并创建了该文件,请添加以下内容:
# ~/.emacs_zsh or ~/.emacs.d/init_zsh.sh
export TERM=emacs

那么你会得到你所要求的东西:使用 M-x shell 启动的 shell 将把 TERM 设为 emacs,而不是 dumb

虽然这在技术上回答了你的问题,但并不特别有用——如果你尝试它,当你启动一个 shell 时,你将得到以下结果:

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

问题在于shell-mode并没有实现任何终端仿真。换句话说,dumb恰好是使用的正确TERM值。(请注意,确实有一些模式可以模拟终端——例如,参见M-x ansi-term——它们设置TERM=eterm-color或类似的值,但是它们被设计成让您使用Emacs作为可视模式下的xterm替代品来运行shell命令,而M-x shell则被设计成让您在与输入和输出交互的同时以Emacs方式运行shell命令。)当您选择一个不受termcap支持的TERM值时,您将会得到上述错误,并且一些程序可能会对所发生的情况感到困惑(有些甚至会拒绝运行)。如果您选择一个功能齐全的TERM值,如xterm,那么当程序尝试向不存在的终端仿真器发送格式化代码时,您将得到“线噪声”字符。您可能可以找到一些能力有限的termcap,以使噪声对您的影响不太大,但如果这只是让您区分Emacs交互式shell与Emacs非交互式shell或非Emacs交互式shell,那么有更好的选择。实际上,这个选择甚至还不够。因为这个特殊的shell脚本被shell-mode和TRAMP都加载,所以上述方法仍然不能让您区分这两者——您将在两种情况下都得到emacs而不是dumb!因此,更好的选择就出现了:使用INSIDE_EMACS环境变量。正如您所指出的那样,交互式shell和TRAMP默认都将TERM设置为dumb,但它们还设置了一个环境变量INSIDE_EMACS。它的存在(或不存在)对于您的shell启动脚本非常有用,但对于您的用例来说,它的价值在于其值——对于交互式(M-x shell)使用,它的值类似于25.2.2,comint,但对于TRAMP,则是25.2.2,tramp。因此,要检查这三种情况,您可以这样做(这也是我自己多年来在我的~/.zshrc中所做的):
# 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

我们在case语句中不会将TERM重置为其他值,因为正是它应该的样子。

请注意,在case语句的部分中,您可以像您在问题中提到的那样将TERM设置为其他内容,但这是一个坏主意,因为Emacs实际上会读取并执行从TRAMP shell获得的响应,并且线路噪声会成为更大的问题。 TRAMP可以做一些非常惊人的事情,但前提是它所读取的shell输出格式符合TRAMP的期望。

最后一件事:使用上述代码时,由于dumb终端检查嵌套在INSIDE_EMACS检查之内,因此我们没有一个单独的地方来放置要在所有类型的Emacs生成的shell中运行的代码,包括M-x ansi-term及其类似物。您可以在shell配置中为此编写单独的语句...但这正是~/.emacs.d/init_${SHELLNAME}.sh的用途,因此如果您出于某种原因需要此功能,则可能是更好的选择。


2
Tomas,我刚意识到你的问题不是几周前而是一年前——你会认为到了三月份我应该知道现在是2019年而不是2018年!但我希望答案仍然足够有用(即使对你没有用)考虑一下! - Trey
3
这是一个非常好而且详尽的答案。我相信人们会发现它很有用。 - phils

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接