Linux Bash函数挤压来自';工作';作业暂停时

Linux Bash函数挤压来自';工作';作业暂停时,linux,bash,unix,vim,command-line,Linux,Bash,Unix,Vim,Command Line,我正在使用上面的shell函数临时更改stty选项,以便CTRL-Q和CTRL-S可以在vim中用作键绑定 这可以很好地工作,但作为一个副作用,当我使用CTRL-Z暂停vim时,我无法再看到哪个文件对应于后台作业。我经常在后台处理多个会话,能够再次看到哪个作业是哪个作业真的很方便 具有后台任务的作业的当前输出: # allow CTRL-Q and CTRL-S keybindings vim() { ( # No ttyctl, so we need to save and the

我正在使用上面的shell函数临时更改stty选项,以便CTRL-Q和CTRL-S可以在vim中用作键绑定

这可以很好地工作,但作为一个副作用,当我使用CTRL-Z暂停vim时,我无法再看到哪个文件对应于后台作业。我经常在后台处理多个会话,能够再次看到哪个作业是哪个作业真的很方便

具有后台任务的作业的当前输出:

# allow CTRL-Q and CTRL-S keybindings
vim() {
  (
    # No ttyctl, so we need to save and then restore terminal settings
    # osx users, use stty -g
    local STTYOPTS="$(stty --save 2> /dev/null)"
    trap "stty $STTYOPTS" EXIT
    stty stop '' start '' -ixoff
    command vim "$@"
  )
}
这样的未包装输出将是理想的:

root@rock64:~# jobs
[1]+  Stopped                 ( local STTYOPTS="$(stty --save 2> /dev/null)"; trap "stty $STTYOPTS" EXIT; stty stop '' start '' -ixoff; command vim "$@" )
root@rock64:~#
是否有其他方法可以在不挤压后台作业列表的情况下实现相同的效果(使用“作业完成时还原”临时更改STTY选项)


目前我正在运行Bash 4.4.x,但如果需要,我可以很容易地编译一个新版本。

因此,如果没有更好的代码格式,我在评论中的建议很难传达。我只是建议移除周围的
。此外,还需要删除返回陷阱,所以我只做了第二个函数,当调用
RETURN
时,我们跳回原始函数并删除陷阱

root@rock64:~# jobs
[1]+  Stopped                 vim .bashrc
root@rock64:~#  
另类

您可以将其放入
$HOME/bin
中名为
vim
的脚本中:

runvim() {
    local STTYOPTS="$(stty --save 2> /dev/null)"
    trap "stty $STTYOPTS" RETURN # This may need to be changed to RETURN
    stty stop '' start '' -ixoff
    command vim "$@"
}

# allow CTRL-Q and CTRL-S keybindings
vim() {
    # No ttyctl, so we need to save and then restore terminal settings
    # osx users, use stty -g
    runvim "$@"
    trap - RETURN
}
然后通过将
导出路径=“$HOME/bin:$PATH”
添加到您喜爱的点文件,将该目录添加到
路径变量的前面。

编辑:

我继续改进了包装器,以查找路径中的下一个真正的vim,如果它在用户路径中作为名为“vim”的符号链接运行。这避免了在vimwrapper.sh使用指向“vim”的别名,并透明地将“vim”调用转发到实际的vim二进制文件。我想这基本上已经完成了

#!/bin/bash
STTYOPTS="$(stty --save 2> /dev/null)"
trap "stty $STTYOPTS" EXIT
stty stop '' start '' -ixoff
#/usr/bin/vim "$@" # or where ever your vim is
$( whereis vim | cut -d\  -f3) "$@"  # Here is a more generic version.

原职:

今天玩了一段时间之后,我想我找到了一个不错的解决办法。子shell是必要的,它可以定义/包含stty参数更改和我们希望受其影响的vim进程,但它不必是主shell环境中的匿名函数

#!/usr/bin/env bash
#
# vimwrapper.sh:  Wrapper script to enable use of CTRL-Q/S keybinds in Vim
#
# Using a wrapper script avoids clobbering the editor command line with an
# anonymous subshell that's hard to read when vim is suspended with ^Z. We need
# the scope of the subshell to trap our trap (aaayyyyy) and keep the stty magic
# out of our interactive environment's namespace.  The wrapper script just
# makes background jobs look sane if you interrupt vim with CTRL-Z.

# set -x

case $(basename "$0") in
  "vim")
    # Check if we're shadowing real vim by existing earlier in the path as a
    # symlink and call it directly if so. This lets us symlink vimwrapper.sh to
    # "$HOME/bin/vim", munge "$HOME:/bin" onto the beginning of the path and
    # transparently wrap calls to 'vim' without our script going recursive.

    for _v in $(which -a "vim"); do
      # I refuse to fork myself. You know what, fork you too.
      [[ $(realpath "$_v") == $(realpath "$0") ]] && continue
      #printf "found real vim in path at '%s'\n" "$(realpath $_v)"
      cmd="$_v" && break
    done

    if [[ -z "$cmd" ]]; then
      echo "$(basename $0): Unable to find real vim in path"
      exit 1
    fi
    ;;
  *)
    cmd="vim"
    ;;
esac

STTYOPTS="$(stty --save 2> /dev/null)"
trap "stty $STTYOPTS" EXIT
stty stop '' start '' -ixoff
command "$cmd" "$@"
将对包装器脚本的调用绑定为
alias vim=“~/foo/vimwrapper.sh”
可以很好地解决所有问题:

#!/usr/bin/env bash
#
# vimwrapper.sh:  Wrapper script to enable use of CTRL-Q/S keybinds in Vim
#                 For best results bind 'alias vim="/path/to/vimwrapper.sh"
#
# Using a wrapper and alias avoids clobbering the editor command line with an
# anonymous subshell that's hard to read when vim is suspended with ^Z. We need
# the scope of the subshell to trap our trap (aaayyyyy) and keep the stty magic
# out of our interactive environment's namespace.  The wrapper script just
# makes background jobs look sane if you interrupt vim with CTRL-Z.

# We'll be paranoid and make sure our wrapper script isn't the target of the
# 'command vim' call that comes next.
if [[ $(realpath $(basename "$0")) == $(realpath $(which vim)) ]]; then
  echo "$0: I refuse to fork myself. You know what, fork you too."
else
  # Save stty state and restore on exit.
  STTYOPTS="$(stty --save 2> /dev/null)"
  trap "stty $STTYOPTS" EXIT
  stty stop '' start '' -ixoff

  command vim "$*"
fi

exit 0
如果vimwrapper.sh的位置优先级也低于真实的vim,则可以将其符号链接为路径中的某个位置的“vim”。我添加了一个检查,这样它就不会意外地返回。我可能会对此进行一点扩展,这样脚本就可以在路径中隐藏真正的vim,并通过查看
which-a vim
并选择下一个不是它本身的条目来确定调用哪个命令是正确的


谢谢@Jason为我指明了正确的方向,我真的很感激。

啊,接球不错。看起来这样行得通。思考何时转储和保存stty选项时,我注意到,如果我也以错误的顺序退出我的vim进程,对我的vim()函数的多个调用可能会导致它们崩溃。我将使用减去子shell的函数运行,同时找出一种更可靠地保存stty状态的方法。无论如何,谢谢@是的。您所做的类似于调用一个名为vim的脚本,其中包含函数体(减去
),因为它正在启动自己的shell。现在我想起来了,这可能是这个问题的另一个解决方案。你可以把这部分内容放进一个小脚本中,要么放在实际的
vim
二进制文件前面的
路径中,要么给它加个别名。是的,我想我需要这样做。在没有子shell的情况下处理陷阱会在整个shell会话中删除陷阱。我将执行类似于
printf“\n\nhi mom!\n\n”
的测试设置为返回陷阱,每次运行子shell时它都会触发。我认为实际的包装器脚本可能是最好的选择,然后我可以依赖包装器脚本的范围(主bash进程的子shell)来包含陷阱。谢谢,这给了我一些东西@科尔本达拉斯你说得有道理。我对原始解决方案进行了编辑,并对此进行了可能的变通。我没有测试,但应该很接近<代码>陷阱-
将移除陷阱。感谢您今天让我开始思考这个问题,我在下面发布了一个我相当满意的解决方案。将原始函数拆分为自己的脚本解决了所有名称空间问题,并将函数别名为“vim”使其可访问。您需要小心使用脚本跟踪真实的vim,因为在没有显式路径的情况下调用“vim”可能会递归。我添加了一个检查,以防有人将vimwrapper.sh作为“vim”放到他们的路径中,但我想我将添加一些逻辑来迭代
哪个-vim
,并调用第一个不是包装本身的。谢谢+谢谢你叫我自己去叉。我只需将
命令vim“$*”
更改为
命令vim“$@”
。使用
*
将把所有参数作为一个参数传递。如果使用
exec command vim“$@”
会发生什么情况?好吧,没有什么像“command”这样的东西是exec可以交互的。如果要从交互式shell执行vim“$@”
,它将运行vim,然后在退出时终止会话。如果你说的是在匿名子shell中运行exec vim“$@”,我会尝试一下,看看它是如何运行的,如果它能工作,那将是一个很好的解决方案。不幸的是,使用exec并不能改善情况。当您挂起vim时(即使使用了exec),整个匿名子shell块也会被中断,因此来自“jobs”的命令行输出仍然是垃圾。我认为,如果将函数调用包装在匿名子shell中,那么可以使别名和函数一起工作,但最终仍然会在流程命令行中到处都是垃圾。包装器脚本(带或不带别名)似乎是解决此问题的最干净的方法。
root@rock64:~/bin# vim vim.sh 


[1]+  Stopped                 ~/bin/vim.sh vim.sh