Autocomplete zsh文件名globbling/substitution

Autocomplete zsh文件名globbling/substitution,autocomplete,zsh,glob,Autocomplete,Zsh,Glob,我正在尝试创建我的第一个zsh完成脚本,在本例中是为命令netcfg创建的 听起来可能很蹩脚,但我已经在第一个障碍上卡住了,免责声明,我知道如何粗略地做到这一点,但我寻求“ZSH方式”来做到这一点 我需要列出/etc/networking中的文件,但只列出文件,而不列出目录组件,因此我执行以下操作 echo $(ls /etc/network.d/*(.)) /etc/network.d/ethernet-dhcp /etc/network.d/wireless-wpa-config 我想要

我正在尝试创建我的第一个zsh完成脚本,在本例中是为命令netcfg创建的

听起来可能很蹩脚,但我已经在第一个障碍上卡住了,免责声明,我知道如何粗略地做到这一点,但我寻求“ZSH方式”来做到这一点

我需要列出/etc/networking中的文件,但只列出文件,而不列出目录组件,因此我执行以下操作

echo $(ls /etc/network.d/*(.))

/etc/network.d/ethernet-dhcp /etc/network.d/wireless-wpa-config
我想要的是:

ethernet-dhcp wireless-wpa-config
所以我试着(原谅我的出生):

这似乎不起作用,我相信一定有一些聪明的方法可以做到这一点,将其拆分成一个数组并获取最后一部分,但正如我所说,我完全不知道这一点


感谢您的建议。

我习惯于看到
basename(1)
剥离目录组件;此外,您还可以使用
echo/etc/network/*
获取文件列表,而无需运行外部
ls
程序。(运行外部程序会比您希望的更慢完成速度;我没有找到basename的zsh内置程序,但这并不意味着没有。)

以下是我希望能有所帮助的事情:


一般说明:不需要使用ls生成文件名。您还可以使用
echo some*glob
。但是,如果您想保护可能嵌入的换行符,即使这是一个坏主意。下面的第一个示例直接将全局变量放入数组中,以保护嵌入的换行符。第二种方法使用printf生成NUL终止的数据,以完成相同的任务,而不使用变量

如果您愿意使用变量,则很容易做到:

typeset -a entries
entries=(/etc/network.d/*(.)) # generate the list
echo ${entries#/etc/network.d/}  # strip the prefix from each one
您也可以不使用变量,但隔离单个条目的额外内容有点难看:

# From the inside, to the outside:
# * glob the entries
#   * NUL terminate them into a single string
# * split at NUL
#   * strip the prefix from each one
echo ${${(0)"$(printf '%s\0' /etc/network.d/*(.))"}#/etc/network.d/}
或者,如果仍要使用子shell(即上一个示例中的命令替换),只需将cd复制到目录,这样它就不是全局扩展的一部分(另外,不必重复目录名):


谢谢你们的建议,伙计们,我已经阅读了更多关于ZSH的文章,几天后又回到了这个问题上,我想我已经有了一个非常简洁的解决方案,我想与大家分享一下


echo${$(print/etc/network.d/*():t}

Chris Johnsen的回答中充满了关于zsh的有用信息,但是没有提到在这种特殊情况下工作的更简单的解决方案:

echo /etc/network.d/*(:t)

这是使用
t
history修饰符作为全局限定符。

${param#pattern}
参数扩展是删除前缀的方法<代码>${param%pattern}删除后缀。遗憾的是我只得到+1。三个不错的选择,都比我的好。:)很好的解决方案,但不能正确处理文件名中的空格。@AntonK globbing部分可以正确处理任意文件名。唯一有问题的部分是
echo
。在实际示例中,您不会使用
echo
,而是将列表传递给要处理文件的任何命令。使用进程替换是一个坏主意:它会损坏包含空格的文件名,更复杂,速度也更慢。为什么不使用更简单、健壮和高效的
echo/etc/network.d/*(:t)
(或者任何您想要的实际命令,而不是
echo
)呢?
# From the inside, to the outside:
# * glob the entries
#   * NUL terminate them into a single string
# * split at NUL
#   * strip the prefix from each one
echo ${${(0)"$(printf '%s\0' /etc/network.d/*(.))"}#/etc/network.d/}
echo ${(0)"$(cd /etc/network.d && printf '%s\0' *(.))"}
echo /etc/network.d/*(:t)