那我还要 “tab” 干嘛,那么接下来的目标是在保留 bash-completion 的情况下,消除卡顿。
03bash-completion 概述
那么我们先来了解一下 bash-completion 吧
bash-completion 是 bash shell 的一个命令行命令补全的集合,其用途是规定参数怎么自动补全。
其是通过一个复杂的脚本来实现可编程的补全程序,减少系统管理员日常维护工作,提高工作效率。
如果是用 apt-get 或者 yum 进行安装的,那么 bash-completion 的脚本位置位于
/usr/share/bash-completion/
其中 bash_completion 是主配置文件,定义了许多通用函数;completions 目录是用于存储规定各命令怎样补全的脚本。
那么对于bash-completion默认不支持补全功能的一些命令,就可以自行编写或下载对应的补全脚本放在 /usr/share/bash-completion/completions 目录下即可。
04解决方案
根据上文,我们知道 /usr/share/bash-completion/completions/ 目录下的文件即是用于存储各命令补全的脚本,那么解决卡顿问题最简单粗暴的方法就是删除对应的补全脚本文件。
mv /usr/share/bash-completion/completions/rsync /tmp/rsync
mv /usr/share/bash-completion/completions/scp /tmp/scp
退出终端后,测试,果然 rsync 与 scp 的 tab 补全失效了
这虽然也解决了问题,但是却让 rsync 与 scp 的全部命令补全功能失效,因此方案需要进一步改进。
接下来查看一下 rsync 命令补全文件:
bash completion for rsync -*- shell-script -*-
_rsync()
{
cur 表示当前光标下的单词
prev 表示上一个单词
words 数组,存放当前命令行中输入的所有单词
cword 整数,当前光标下输入的单词位于 words 数组的索引
local cur prev words cword split
调用主配置文件的 init_completion() 函数初始化命令补全
_init_completion -s -n : || return
匹配当前光标的上一个单词
case $prev in
–config|–password-file|–include-from|–exclude-from|–files-from|
–log-file|–write-batch|–only-write-batch|–read-batch)
compopt +o nospace
调用 filedir() 函数输出当前目录下的文件与目录
_filedir
return
;;
–temp-dir|–compare-dest|–backup-dir|–partial-dir|–copy-dest|
–link-dest|-!(-*)T)
compopt +o nospace
调用 filedir() 函数加 -d 参数输出当前目录下的目录
_filedir -d
return
;;
–rsh|-!(-*)e)
compopt +o nospace
COMPREPLY:候选的补全结果
compgen 内置补全命令,根据不同的参数,生成匹配单词的候选补全列表
-W 参数为指定空格分隔的单词列表
COMPREPLY=(
(
c
o
m
p
g
e
n
−
W
′
r
s
h
s
s
h
′
−
−
"
(compgen -W 'rsh ssh' -- "
(compgen−W′rshssh′−−"cur") )
return
;;
–compress-level)
compopt +o nospace
COMPREPLY=(
(
c
o
m
p
g
e
n
−
W
′
1..9
′
−
−
"
(compgen -W '{1..9}' -- "
(compgen−W′1..9′−−"cur") )
return
;;
esac
$split && return
_expand || return
匹配当前光标下的单词
case $cur in
-*)
COMPREPLY=(
(
c
o
m
p
g
e
n
−
W
′
−
−
v
e
r
b
o
s
e
−
−
q
u
i
e
t
−
−
n
o
−
m
o
t
d
−
−
c
h
e
c
k
s
u
m
.
.
.
(
此处省略
)
′
−
−
"
(compgen -W '--verbose --quiet --no-motd --checksum ...(此处省略) ' -- "
(compgen−W′−−verbose−−quiet−−no−motd−−checksum...(此处省略)′−−"cur") )
[[ $COMPREPLY == *= ]] || compopt +o nospace
;;
*😗)
获取远程主机下的文件路径
find which remote shell is used
local i shell=ssh
for (( i=1; i < cword; i++ )); do
if [[ "
w
o
r
d
s
[
i
]
"
=
=
−
@
(
e
∣
−
r
s
h
)
]
]
;
t
h
e
n
s
h
e
l
l
=
{words[i]}" == -@(e|-rsh) ]]; then shell=
words[i]"==−@(e∣−rsh)]];thenshell={words[i+1]}
break
fi
done
[[ $shell == ssh ]] && _xfunc ssh _scp_remote_files
;;
*)
调用 known_hosts_real() 函数,补全已知的主机名列表
_known_hosts_real -c -a – “$cur”
调用命令补全脚本 ssh 下的 scp_local_files() 函数,补全本地文件列表
_xfunc ssh _scp_local_files
;;
esac
} &&
complete -F _rsync -o nospace rsync
ex: filetype=sh
scp 命令补全脚本基本也一致,其获取目标主机下的文件列表的代码如下
case $cur in
!(*😗)/*|[.~]*) ;; # looks like a path
*😗) _scp_remote_files ; return 0 ;;
esac
此时就有一个解决方案就是注释掉 rsync 和 scp 的获取目标主机下文件列表的代码,这样就不会造成卡顿,rsync 和 scp 的其它命令补全仍然可用。但是这样也有一个缺点就是,在目标主机后使用 tab 就什么都 tab 不出来。
而线上服务器的很多文件路径都是一致的,因此想让在目标主机后 tab 出现的是本地文件路径。
那么下面让我们再来看看补全脚本中补全目标主机下的文件路径与补全本地文件路径的代码。
rsync:
*😗)
获取远程主机下的文件路径
find which remote shell is used
local i shell=ssh
for (( i=1; i < cword; i++ )); do
if [[ "
w
o
r
d
s
[
i
]
"
=
=
−
@
(
e
∣
−
r
s
h
)
]
]
;
t
h
e
n
s
h
e
l
l
=
{words[i]}" == -@(e|-rsh) ]]; then shell=
words[i]"==−@(e∣−rsh)]];thenshell={words[i+1]}
break
fi
done
[[ $shell == ssh ]] && _xfunc ssh _scp_remote_files
;;
*)
调用 known_hosts_real() 函数,补全已知的主机名列表
_known_hosts_real -c -a – “$cur”
调用命令补全脚本 ssh 下的 scp_local_files() 函数,补全本地文件列表
_xfunc ssh _scp_local_files
;;
scp:
case $cur in
!(*😗)/*|[.~]*) ;; # looks like a path
*😗) _scp_remote_files ; return 0 ;;
esac
补全远程主机下的文件路径主要是通过调用 ssh 的 scp_remote_files() 函数,而补全本地文件路径则是通过调用 ssh scp_local_files() 函数。
下面先看一下 scp_remote_files 函数:
Complete remote files with ssh. If the first arg is -d, complete on dirs
only. Returns paths escaped with three backslashes.
_scp_remote_files()
{
local IFS=$‘\n’
remove backslash escape from the first colon
将当前光标下的单词中的 “:” 转换为 “:”
cur=${cur/\😕:}
获取目标主机名
local userhost=${cur%%?(\)😗}
获取路径
local path=${cur#*:}
unescape (3 backslashes to 1 for chars we escaped)
path=KaTeX parse error: Can't use function '\(' in math mode at position 19: …ed -e 's/\\\\\\\̲(̲'_scp_path_esc’)/\\1/g’ <<<“$path” )
default to home dir of specified user on remote host
if [[ -z
p
a
t
h
]
]
;
t
h
e
n
p
a
t
h
=
path ]]; then path=
path]];thenpath=(ssh -o ‘Batchmode yes’ $userhost pwd 2>/dev/null)
fi
local files
到远程去补全路径
if [[ $1 == -d ]]; then
escape problematic characters; remove non-dirs
files=$( ssh -o ‘Batchmode yes’
u
s
e
r
h
o
s
t
c
o
m
m
a
n
d
l
s
−
a
F
1
d
L
"
userhost \ command ls -aF1dL "
userhost commandls−aF1dL"path*" 2>/dev/null |
sed -e ‘s/’KaTeX parse error: Expected 'EOF', got '&' at position 22: …ath_esc'/\\\\\\&̲/g' -e '/[^\/]/d’ )
else
escape problematic characters; remove executables, aliases, pipes
and sockets; add space at end of file names
files=$( ssh -o ‘Batchmode yes’
u
s
e
r
h
o
s
t
c
o
m
m
a
n
d
l
s
−
a
F
1
d
L
"
userhost \ command ls -aF1dL "
userhost commandls−aF1dL"path*" 2>/dev/null |
sed -e ‘s/’KaTeX parse error: Expected 'EOF', got '&' at position 22: …ath_esc'/\\\\\\&̲/g' -e 's/[\*@|…//g’
-e ‘s/[^/]$/& /g’ )
fi
COMPREPLY+=( $files )
}
scp_local_files() 函数如下
This approach is used instead of _filedir to get a space appended
after local file/dir completions, and -o nospace retained for others.
If first arg is -d, complete on directory names only. The next arg is
an optional prefix to add to returned completions.
_scp_local_files()
{
local IFS=$‘\n’
local dirsonly=false
if [[ $1 == -d ]]; then
dirsonly=true
shift
fi
if $dirsonly ; then
COMPREPLY+=( $( command ls -aF1dL KaTeX parse error: Undefined control sequence: \* at position 4: cur\̲*̲ 2>/dev/null | …_scp_path_esc/\\&/g" -e ‘/[^/]$/d’ -e “s/^/$1/”) )
else
COMPREPLY+=( $( command ls -aF1dL KaTeX parse error: Undefined control sequence: \* at position 4: cur\̲*̲ 2>/dev/null | …_scp_path_esc/\\&/g" -e ‘s/[*@|=]KaTeX parse error: Undefined control sequence: \/ at position 16: //g' \ -e 's/[^\̲/̲]/& /g’ -e “s/^/$1/”) )
fi
}
从上述看出,当执行下列命令后,bash-completion 首先会调用 rsync 命令补全脚本进行匹配,然后通过一个判断选择匹配到 “:” 项,调用 scp_remote_file 函数获取目标主机路径。scp_remote_file() 函数中,此时 cur 变量的值为"compute:",那么 path 变量的值为空 ,在通过一个 if 语句判断如果 path 值为空,则到 ssh 到远程目标主机执行 pwd 命令并将结果赋值给 path,然后通过 ssh 远程到目标主机执行 ls -aF1dL “$path*” 命令获取所有以 $path 开头的最小路径。
rsync -av compute:[按 tab键]
scp_local_files() 函数也是一样,不过只是少了 ssh 到远程主机执行对应的补全命令。
因此我们若是想要让在目标主机后按 tab 键补全的是本地文件路径,那么我们就可以调用 scp_local_files 函数进行补全,不过在调用之前,需要将 cur 的值进行一个切割,只保留其后面的路径部分,因此对于 rsync 命令补全脚本,我们可以这么改。
将 /usr/share/bash-completion/completions/rsync 配置文件中 *😗) 选项下的命令更改为
*😗)
cur=${cur#*:}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数软件测试工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年软件测试全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上软件测试开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注软件测试)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
,基本涵盖了95%以上软件测试开发知识点,真正体系化!**
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注软件测试)
[外链图片转存中…(img-KRnkWuxy-1712912459985)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!