масштабирование / подстановка имени файла zsh

#autocomplete #zsh #glob

#автозаполнение #zsh #глобус

Вопрос:

Я пытаюсь создать свой первый сценарий завершения 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
  

Поэтому я пытаюсь (извините за мою наивность) :

 echo ${(s/*/)$(ls /etc/network.d/*(.))}

/etc/network.d/ethernet-dhcp /etc/network.d/wireless-wpa-config
  

Кажется, это не работает, я уверен, что должен быть какой-то умный способ сделать это, разбив на массив и получив последнюю часть, но, как я уже сказал, я полный новичок в этом.

С благодарностью принимаю любые советы.

Ответ №1:

Общее примечание: Нет необходимости использовать 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' /etc/network.d/*(.))"}#/etc/network.d/}
  

Или, если вы все равно собираетесь использовать подоболочку (т. Е. Замену команды в предыдущем примере), просто вставьте компакт-диск в каталог, чтобы он не был частью расширения glob (плюс, вам не нужно повторять имя каталога):

 echo ${(0)"$(cd /etc/network.d amp;amp; printf '%s' *(.))"}
  

Комментарии:

1. Жаль, что у меня есть только 1, чтобы указать. Три хороших варианта, все лучше, чем у меня. 🙂

Ответ №2:

Ответ Криса Джонсена полон полезной информации о zsh, однако в нем не упоминается гораздо более простое решение, которое работает в данном конкретном случае:

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

При этом используется t модификатор истории в качестве квалификатора глобуса.

Комментарии:

1. Хорошее решение, но оно неправильно работает с пробелами в именах файлов.

2. @AntonK Часть с глобализацией работает должным образом с произвольными именами файлов. Единственная часть, которая имеет проблему, это echo . В реальном примере вы бы не использовали echo , вы бы передали список любой команде, которую хотите обработать файлы.

Ответ №3:

Спасибо за ваши предложения, ребята, после еще большего прочтения ZSH и возвращения к проблеме пару дней спустя, я думаю, что у меня есть очень краткое решение, которым я хотел бы поделиться в ваших интересах.

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

Комментарии:

1. Использование подстановки процесса — плохая идея: это искажает имена файлов, содержащие пробелы, это более сложно и работает медленнее. Почему бы нет echo /etc/network.d/*(.:t) (или любой другой реальной команды, которую вы хотите заменить echo ), которая была бы проще, надежнее и эффективнее?

Ответ №4:

Я привык видеть, как basename(1) удаляются компоненты каталога; кроме того, вы можете использовать echo /etc/network/* для получения списка файлов без запуска внешней ls программы. (Запуск внешних программ может замедлить завершение больше, чем хотелось бы; я не нашел встроенного zsh для basename, но это не значит, что его нет.)

Вот кое-что, что, я надеюсь, поможет:

 haig% for f in /etc/network/* ; do basename $f ; done
if-down.d
if-post-down.d
if-pre-up.d
if-up.d
interfaces
  

Комментарии:

1. ${param#pattern} Расширение параметра — это способ удаления префиксов. ${param%pattern} для удаления суффиксов.