我编写了一个名为 的脚本prpsls
,它在目录中创建或编辑文件~/proposals
。
现在我正在为其编写自动完成脚本,并且我正在尝试以某种方式设置它,在我点击之后标签,无论当前工作目录如何,complete
内置都会列出目录中的文件和目录。~/proposals
我想要实现的行为与我所做的完全一样complete -f prpsls
,但我希望它始终完成目录的内容,而不是列出当前目录中的文件和目录~/proposals
。
例如,当我写ls
和标签, 表明:
$ pwd
/home/user/base_directory
$ ls
file_01 file_02 subdir_01/
如果我写subd
并且标签,它会自动完成为subdir_01
.如果我标签再次,它显示的内容dir_01
:
$ ls subdir_01/
file_03 subdir_02/
我正在寻找的行为是类似的:
$ pwd
/home/user/any/directory/it/should/not/matter
$ prpsls # Tab here
proposal_01 proposal_02 job_01/
$ prpsls job_01/ # Tab again
proposal_03 proposal_draft
complete
我的问题是:是否可以通过内置实现此行为?如果没有,我怎样才能实现这种行为?
我希望我可以通过标志告诉complete
哪个目录用作基本目录,但我看到的与目录相关的唯一标志是-d
which 与 相同-A directory
,它生成目录列表。
有趣的是,如果我使用compgen -f "~/proposals/"
,它做列出以下位置的文件和目录~/proposals
:
$ pwd
/home/user/any/directory/it/should/not/matter
$ compgen -f "~/proposals/"
~/proposals/job_01
~/proposals/proposal_01
~/proposals/proposal_02
我见过这问题,但是:
- 接受的答案不适用于嵌套目录。 (IE标签并查看目录的内容)
- 答案
CDPATH
会给其他命令带来更多混乱。 - 答案
compgen
也不适用于嵌套目录。
答案1
基于这个很棒的答案我为 command 编写了这个完成foo
,它递归地完成文件和目录~/foodir
:
FOO_DIR=$HOME/foodir/
_foo_compgen_filenames() {
local cur="$1"
# Files, excluding directories:
grep -v -F -f <(compgen -d -P ^ -S '$' -- $FOO_DIR"$cur") \
<(compgen -f -P ^ -S '$' -- $FOO_DIR"$cur") |
sed -e 's|^\^'$FOO_DIR'||' -e 's/\$$/ /'
# Directories:
compgen -d -S / -- $FOO_DIR"$cur" | sed -e 's|'$FOO_DIR'||'
}
_foo_complete() {
local cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(_foo_compgen_filenames "$cur") )
}
complete -o nospace -F _foo_complete foo
诀窍在于让compgen
补全引擎补全 中文件的完整路径FOO_DIR
,然后在FOO_DIR
将其发送到 之前从补全中删除该路径complete
。还有在目录名称末尾添加斜杠的问题,这不是给定的,也compgen
没有仅返回文件的本机方法(但确实有一种仅返回目录的方法)。
答案2
我尝试并找到了一些如何修复它的方法和想法。
专业人士可以改进我的方式并创建更好/完美的解决方案。
我的系统是 Debian GNU/Linux 10 (buster) 4.19.0-16-amd64,带有 KDE 和 GNOME
第一部分:
启动一个新的外壳
创建工作目录
/home/user/ComBash
并cd
进入其中创建一个脚本,
mytab
我们将此脚本绑定到完整的命令现在
mytab
看起来像:#!/bin/bash echo "MYTAB"
chmod +x mytab
px
为完整命令创建第二个脚本现在
px
看起来像:#/usr/bin/env bash echo "PX"
chmod +x px
现在我们开始我们的
px
脚本source px
或者
./px
或者
. ./px
输出应该是“PX”(它有效)
bin/
创建一个名为in 的新文件夹/home/user/ComBash
mkdir bin
我们的文件现在是
mytab | script px | script bin/ | folder
现在安装
mytab
在bin/
我们的workdir | 路径中/home/user/ComBash/bin
并mytab
删除/home/user/ComBash/
install mytab bin/mytab && rm mytab
现在将workdir
bin/
文件夹添加到$PATH
PATH="/home/user/ComBash/bin:${PATH}"
或者
PATH="${PATH}:/home/user/ComBash/bin"
检查与
echo $PATH
检查是否
mytab
有效:myt
+TAB并按Entermytab
按Enter- 输出是“MYTAB”(它有效)
在文件夹内创建一些文件夹、子文件夹和空文件,我们需要这些文件夹和文件来检查TAB+是否TAB有效并检查 TAB|TAB 显示的内容
现在我们有了第二部分的结构,该解决方案仅适用于当前 shell
如果我们关闭并启动一个新的 shell,/home/user/ComBash/bin
in$PATH
被删除并且 bash 找不到mytab
中的脚本/home/user/ComBash/bin
,那么每次启动新 shell 时都必须重复步骤 9.(设置 $PATH)。
如果您不想每次启动 shell 时都重复步骤 9,则可以永久添加/home/user/ComBash/bin
。$PATH
第二部分:
打开并编辑
px
文件我也尝试了很多这个命令,但我现在不会解释它们,你可以尝试查看结果,只需取消注释并编辑你想尝试的行。
现在 px 看起来像:
#/usr/bin/env bash #complete -f mytab #complete -D -F "/home/user/ComBash" mytab #complete -W "$(find /home/user/ComBash/ -path "" -prune -o \( -type d -ls \) -o \( -type f -ls \) | cut -d/ -f 5-)" mytab #complete -W "$(find /home/user/ComBash/ -path "" -prune -o \( -type d -ls \) -o \( -type f -ls \) | cut -d/ -f 2- | sed 's/$/\//')" mytab #complete -W "$(find /home/user/ComBash/ -path "" -prune -o \( -ls \)))" mytab #complete -W "$(ls -R ${MYPATH} | grep /)" mytab MYPATH="/home/user/ComBash/" complete -W "$(ls -R ${MYPATH})" mytab
我们将完整的命令绑定到
mytab
脚本中-W
意味着我们给出完整的单词列表我们得到带有 的单词列表
ls -R
。ls -R
列出给定路径中的所有文件、子文件、目录和子目录我们启动 px 脚本
source px
每次我们在脚本内进行更改时,都必须重新启动脚本(请参阅第 I 部分中的步骤 6)
source px
现在在 shell 中我们使用
mytab
+TAB/home/user/ComBash/
我们可以看到给定路径 ( )中所有文件、子文件、目录和子目录的列表我们可以选择每个文件、子文件、目录和子目录,并使用 [TAB] 自动完成
选择有效的子文件夹和 [TAB]
如果我们按下Enter脚本
mytab
将回显“MYTAB”mytab
让我们在 中编辑脚本/home/user/ComBash/bin
。现在
mytab
看起来像:#!/bin/bash MYPATH=/home/user/ComBash # echo $MYPATH echo $1 DATA=$(find ${MYPATH} -name ${1}) echo $DATA ####### DO SOMETHING #######
$1
px
输出我们从脚本 搜索中收到的文件夹或文件DATA
,并输出PATH
文件夹或文件的
现在我们可以对当前文件或文件夹执行某些操作
mytab
+Tab按Enter