【问题标题】:How to make ctrl+c cancel a whole command when interacting with a subshell command?与子shell命令交互时如何使ctrl + c取消整个命令?
【发布时间】:2018-07-31 04:27:57
【问题描述】:

我有一个简单的 shell 别名

alias fz='vim -p $(fzf -m)'

fzf 是一个交互式终端程序,只有在它终止时才会在标准输出上发送一个文件名列表。然后这用于打开用 vim 选择的文件。

现在的问题是当我决定取消并按下 ctrl+c 时,shell 继续前进并仍然打开 vim(好像我刚刚运行了 vim -p)。这种说法很有道理。

现在,解决我当前遇到的问题的一种方法是简单地使我的别名更复杂,这样如果 fzf 的输出为空,它就不会启动 vim。或者,如果 fzf 的退出代码不为零,我可能会做一些事情来中止启动 vim。

但是,我很好奇当我 Ctrl + C 时如何命令我的 shell 终止 fz。当我在子shell上下文中时,这根本不可能吗?我可以通过以某种方式设置进程组 ID 来做到这一点吗?

我尝试了set -m,但它并没有改变行为。

【问题讨论】:

  • 我不确定,因为我还没有对此进行最终测试,但是,根据this answer,如果我将别名更改为脚本,它会做我想做的事。更新:这似乎不起作用。即使是set -m。不过,我可以确认 pgid 与 shell 脚本版本正确设置。
  • 文件名列表如何分隔? (也就是说,带有空格的文件名是如何表示的?)——在大多数情况下,这个别名不会与不寻常/令人惊讶的名称一起使用。
  • @CharlesDuffy 带有空格的文件名可能会破坏这一点,但这没关系(与问题的目的完全无关)。想象一下,如果我改用 find -print0 和 xargs -0
  • 我不确定“更安全”是不是这个短语。这是一个能力连续体——与使用别名相比,您可以用函数做的事情范围更广,这仅取决于函数的工作方式(拥有自己的堆栈框架,被调用)使用不同的参数列表,而不是依赖字符串替换在结束后抛出东西)。而且,就此而言,您可以使用独立脚本中无法执行的函数执行大量操作(除非您使用 source 调用该脚本)。
  • 可能是这样的? alias fz='F=$(fzf) && vim $F 带预览版alias fz='F=$(fzf --preview "bat --style=numbers --color=always --line-range :500 {}") && vim $F'

标签: linux bash unix process posix


【解决方案1】:

当您使用 Ctrl + C 提前退出时,使调用 fzf(或任何其他命令)的脚本退出

1。简单别名

这行得通:

# command
files_list=$(fzf -m) && vim -p $files_list

# alias
alias fz='$(fzf -m) && vim -p $files_list'

解释:

fzf正常退出时返回错误码0(注意:运行echo $?可以读取退出后的返回码)。但是,当fzf 被Ctrl + C 杀死时,它会返回错误代码130。 && 部分的意思是“如果左边的部分为零,只做右边的部分”——表示左边的命令成功完成。因此,当您在fzf 运行时按Ctrl + C 时,它会返回错误代码130,而右边的部分将永远不会执行。

2。更强大的功能

但是,如果您需要执行更复杂的操作,请使用函数(在您的 ~/.bashrc 或 ~/.bash_aliases 文件中,如果您愿意的话)而不是别名。这是上面别名的一个更强大的示例,采用函数形式。 按照我上面编写别名的方式,如果文件名中包含空格或奇怪字符,我希望它会失败。但是,即使在我认为的这些情况下,下面的函数也应该可以工作: p>

fz() {
    files_list="$(fzf -m)"
    return_code="$?"

    if [ "$return_code" -ne 0 ]; then
        echo "Nothing to do; fzf return_code = $return_code"
        return "$return_code"
    fi

    echo "FILES SELECTED:"
    echo "$files_list"

    # Convert the above list of newline-separated file names into an array
    # - See: https://stackoverflow.com/a/24628676/4561887
    SAVEIFS=$IFS   # Save current IFS (Internal Field Separator)
    IFS=$'\n'      # Change IFS to newline char
    files_array=($files_list) # split this newline-separated string into an array
    IFS=$SAVEIFS   # Restore original IFS

    # Call vim with each member of the array as an argument
    vim -p "${files_array[@]}"
}

注意:如果您不熟悉 [ 函数(是的,这是一个函数名称!),请运行 man [ 或 man test 以获取更多信息。 [ 函数也称为test。这就是为什么函数名后面的参数需要在它们之间有空格——它们是函数的参数! ] 符号只是 [ (test) 函数所需的结束参数。

参考资料:

  1. 我的array_practice.sh bash 脚本来自我的eRCaGuy_hello_world 存储库。在这里我学到并记录了很多这些东西。
  2. 如何将参数数组传递给命令:How can I create and use a backup copy of all input args ("$@") in bash?
  3. 如何将换行符分隔的字符串解析为元素数组:Convert multiline string to array
  4. 使用return 提前退出bash 函数:How to exit a function in bash

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 2016-04-06
    • 2013-12-30
    • 1970-01-01
    相关资源
    最近更新 更多