如何在 Tmux 中按工作目录选择窗口?

如何在 Tmux 中按工作目录选择窗口?

我正在使用 emacs,我希望可以运行一个命令,通过该窗口的工作目录来选择窗口,如果没有这样的窗口 - 则创建一个。我可以通过该命令创建 tmux 窗口

tmux new-window -c some_directory

但是我找不到如何按目录选择窗口,有这样的命令吗?或者,也许应该以不同的方式完成,在循环中运行验证,比较密码和目录?

答案1

我希望我可以运行一个命令,通过该窗口的工作目录来选择窗口,[…]。我可以通过该命令创建 tmux 窗口:

tmux new-window -c some_directory

该命令创建一个包含单个窗格的窗口。在 tmux 中,有pane_current_path一种格式可以打印窗格的当前工作目录(如果可能)。针对某个窗口,该格式将打印窗口中活动窗格的当前工作目录。这实际上会忽略非活动窗格。如果每个窗口都有一个窗格,则每个窗格在其各自的窗口中都是活动的。否则至少有一个非活动窗格。

注意:man 1 tmux在我的 Debian 中,术语“非活动窗格”通常用于表示不活动的窗格;这是我在此处使用的含义。但手册中很少使用“非活动窗格”,而它应该是“死窗格”;这不是我在此处使用的含义。

我不确定您是否想忽略非活动窗格。如果一个窗口中有多个窗格,则很容易忘记在选择另一个窗口时将一个特殊窗格设为活动窗格。在我的工作流程中,我从不关心离开窗口时哪个窗格处于活动状态。因此,我假设您想选择一个窗格通过该窗格的工作目录。


_tmux-find-cwd我在指向的目录中的名称下有以下脚本PATH

#!/bin/sh

if [ "$#" -ne 1 ]; then
   printf '%s\n' "exactly 1 argument needed" >&2
   exit 1
fi

if [ "$CD_TO_PANE" ]; then
   cd "$(tmux display -p '#{pane_current_path}')"
fi

tpth="$(realpath -e "$1//")" || exit 1

tmux list-panes -s -F '#{window_id} #{pane_id} #{pane_current_path}' \
| while IFS=' ' read -r wndw pne pth; do
    [ "$pth" = "$tpth" ] && tmux select-pane -t "$pne" && tmux select-window -t "$wndw" && exit 0
done || tmux new-window -c "$tpth"

在 shell 中我像这样使用它:

_tmux-find-cwd some_directory

脚本会尝试查找具有匹配工作目录的窗格。将选择第一个匹配项,不再测试其他窗格。如果没有匹配项,则会按照您的要求创建一个窗口。

通常,脚本不会更改其工作目录,因此some_directory相对路径也可以工作。当涉及符号链接时,您可能会得到意外结果,请参阅


然后我的里面有这一行~/.tmux.conf

bind -T root C-M-j command-prompt -p 'dir:' "run-shell 'CD_TO_PANE=1 2>&1 _tmux-find-cwd \"%%\"'"

这样,当我按下Ctrl++时Altjtmux 会要求输入目录并为我运行脚本。非空CD_TO_PANE变量使脚本cd成为活动窗格的工作目录,因此相对路径可以方便地相对于我当前的窗格工作。

请注意,如果我输入typed string,tmux 将使用以下命令字符串生成一个 POSIX shell:

CD_TO_PANE=1 2>&1 _tmux-find-cwd "typed string"

键入的字符串隐式地包含在双引号中,因此我不必担心等dir with spaces。波浪号扩展不起作用,但来自生成的 shell 的环境的变量被扩展(例如$HOME有效)。命令替换($(…))也有效。这不是注入代码的唯一方法。考虑这个键入的字符串:

/"; rm "foo

它将执行以下命令行:

CD_TO_PANE=1 2>&1 _tmux-find-cwd "/"; rm "foo"

所以请注意输入的内容。此漏洞是不可避免的,因为它run-shell接受一个字符串,并且显然将其与 一起使用sh -c。如果 tmux 可以首先创建一个适当的参数数组,那就更好了。在U&L SE 上的这个答案我们的代码%%就像是{}嵌入在 shell 代码中。我认为我们无能为力。单引号实际上帮不上什么忙,因为人们总是可以输入一个结束的引号,注入代码并重新打开引号。这可能是偶然的。例如,人们忘记了隐式引号,想要引用一个危险的字符串,使用额外的引号并有效地生成了被解析的未加引号的危险字符串。先见之明导致灾难。

也许最好选择这个变体:

bind -T root C-M-j command-prompt -p '$ _tmux-find-cwd' "run-shell 'CD_TO_PANE=1 2>&1 _tmux-find-cwd %%'"

这导致

CD_TO_PANE=1 2>&1 _tmux-find-cwd typed string

然后,您需要在需要时明确引用。波浪号扩展将起作用。代码注入就像输入一样简单;。乍一看,这似乎更加危险,但我故意更改了提示符,使其看起来像是在交互式中输入命令sh。如果您忘记是否应该引用,与 shell 的相似性应该是一个线索。


附加说明、怪癖和缺陷:

  • realpath在这里非常有用,但是 POSIX 并不要求它。
  • 如果我的前提是错误的,而你真的想测试窗口的工作目录(不是所有窗格),那么你应该选择

    tmux list-windows -F '#{window_id} #{pane_current_path}'
    

    并相应调整其余代码。

  • 我的代码一步即可检索window_idpane_idpane_current_path。通常,最好先获取pane_ids 列表,然后对其进行迭代,然后为每个窗格分别检索其他信息(tmux display -p …每个窗格可能具有多个信息)。理由:

    • pane_current_path可能包含换行符(换行符是文件名中的有效字符)。这会破坏我原来的代码。如果您一次检索一个路径,就可以解决这个问题。
    • 更复杂的逻辑可能依赖于您无法在一行中明确分隔(使用空格或其他方式)的格式。逐个检索它们可以解决问题。
  • 命令替换$(…)会删除所有尾随换行符。我的代码使用它来解析路径。对于以换行符结尾的路径,这将失败。我想我可以修复代码,但会很麻烦。我不使用带换行符的路径,我想你也不用。
  • 您可能想知道为什么我使用realpath -e "$1//";为什么不?如果扩展到非目录的现有文件,realpath -e "$1"后一个命令不会失败。结尾的斜杠告诉我期望一个目录,它可以节省我的时间。$1realpath[ -d … ]
  • 为什么斜线然后而不是一个?为了避免 bare //when$1扩展到 sole /(以防万一,请参阅这个答案)。

相关内容