Такие оболочки, как Bash и Zsh, расширяют подстановочный знак в аргументы, столько аргументов, сколько соответствует шаблону:
$ echo *.txt
1.txt 2.txt 3.txt
Но что, если я хочу, чтобы был возвращен только первый матч, а не все?
$ echo *.txt
1.txt
Я не возражаю против решений, специфичных для оболочки, но мне бы хотелось, чтобы решение работало с пробелами в именах файлов.
Ответы:
Один надежный способ в bash - развернуть в массив и вывести только первый элемент:
(Вы можете даже просто
echo $files
, отсутствующий индекс обрабатывается как [0].)Это безопасно обрабатывает пробел / tab / newline и другие метасимволы при расширении имен файлов. Обратите внимание, что действующие настройки локали могут изменить то, что «первое».
Вы также можете сделать это интерактивно с помощью функции завершения bash :
Это связывает
_echo
функцию для завершения аргументовecho
команды (переопределяя нормальное завершение). В приведенном выше коде добавлено дополнительное «*», вы можете просто нажать «tab» на частичном имени файла, и, надеюсь, правильная вещь произойдет.Код немного запутанным, а не набор или предположить
nullglob
(shopt -s nullglob
) мы проверяем ,compgen -G
можно расширить Glob в некоторых матчах, то мы расширим благополучно в массив, и , наконец , установить COMPREPLY так , чтобы процитировать надежен.Вы можете частично сделать это (программно расширить глобус) с помощью bash
compgen -G
, но это не надежно, так как выводит без кавычек в стандартный вывод.Как обычно, завершение довольно чревато, это нарушает завершение других вещей, включая переменные среды (см.
_bash_def_completion()
Функцию здесь для деталей эмулирует поведение по умолчанию).Вы также можете просто использовать
compgen
вне функции завершения:Следует отметить, что «~» - это не глобус, он обрабатывается bash на отдельной стадии раскрытия, как и переменные $ и другие раскрытия.
compgen -G
просто выполняет глобализацию имени файла, ноcompgen -W
дает вам все расширения bash по умолчанию, хотя, возможно, слишком много расширений (включая``
и$()
). В отличие от-G
,-W
это безопасно цитируется (я не могу объяснить несоответствие). Поскольку цель-W
состоит в том, что он расширяет токены, это означает, что он расширит «a» до «a», даже если такого файла не существует, поэтому, возможно, он не идеален.Это легче понять, но может иметь нежелательные побочные эффекты:
Затем:
touch $'curious \n filename'
echo curious*
tabОбратите внимание на использование
printf %q
для безопасного цитирования значений.Последний вариант - использовать вывод с разделителями 0 для утилит GNU (см. Часто задаваемые вопросы по bash ):
Эта опция дает вам немного больше контроля над порядком сортировки (порядок при развертывании глоба будет зависеть от вашей локали /
LC_COLLATE
и может или не может сложить регистр), но в остальном довольно большой молоток для такой маленькой проблемы ;-)источник
В zsh используйте
[1]
спецификатор glob . Обратите внимание, что хотя этот особый случай возвращает не более одного совпадения, он все равно остается списком, и глобусы не раскрываются в контекстах, которые ожидают одно слово, например, присваивания (кроме присвоений массива).В ksh или bash вы можете поместить весь список совпадений в массив и использовать первый элемент.
В любой оболочке вы можете установить позиционные параметры и использовать первый.
Это сжимает позиционные параметры. Если вы не хотите этого, вы можете использовать подоболочку.
Вы также можете использовать функцию, которая имеет собственный набор позиционных параметров.
источник
*.txt([1,n])
Пытаться:
Обратите внимание, что расширение имени файла сортируется в соответствии с последовательностью сортировки, действующей в текущей локали.
источник
Простое решение:
Или используйте,
printf
если хотите.источник
Я просто наткнулся на этот старый вопрос, удивляясь тому же. Я закончил с этим:
echo $(ls *.txt | head -n1)
Можно, конечно, заменить
head
сtail
и-n1
с любым другим номером.Вышеописанное не сработает, если вы работаете с файлами, в имени которых есть переводы строк. Для работы с новыми строками вы можете использовать любой из них:
ls -b *.txt | head -n1 | sed -E 's/\\n/\n/g'
(Не работает на BSD)ls -b *.txt | head -n1 | sed -e 's/\\n/\'$'\n/g'
ls -b *.txt | head -n1 | perl -pe 's/\\n/\n/g'
echo -e "$(ls -b *.txt | head -n1)"
(Работает с любым специальным персонажем)источник
Случай использования, с которым я часто сталкиваюсь, определяет либо верхний / нижний каталог после расширения glob (например, каталог, полный версионных SDK или инструментов сборки). В этой ситуации я обычно хочу сохранить это имя каталога в переменной для использования в нескольких местах внутри сценария оболочки.
Эта команда обычно делает это для меня:
Отказ от ответственности: расширение Glob не будет сортировать ваши папки по semver; Вы были предупреждены. Это замечательно, если у вас есть
Dockerfile
только одна версия каталога, но эта версия каталога может отличаться от изображения к изображению 🤷источник
mkdir "$(echo one; echo two)"
и посмотрите, что я имею в виду.tail
?dirname
берет только одно имя пути, поэтому вы не можете полагаться на то, что он работает с несколькими путями, если вы не знаете, что ваша конкретная реализация поддерживает его.