
Я хотел бы открыть случайно выбранный файл через терминал. Я нашел shuf
, который, похоже, является как раз той утилитой, которая мне нужна, хотя я не могу придумать, как сделать команду, с которой я работаю, рекурсивной.
Изначально я использовал open $(ls | gshuf -n1)
, что отлично работает, если у меня плоская структура каталогов. Как мне сделать так, чтобы это рекурсивно проходило в любое количество подкаталогов и отфильтровывало файлы вроде .DS_Store
и папки?
решение1
- Не анализировать
ls
. - Цитируйте правильно(если вы не знаете, что ваша оболочка исправляет это, например
zsh
).
Правильный инструмент — find
. Он работает рекурсивно, это решает вашу главную проблему. Вы можете исключить различные шаблоны, если знаете как.
Базовое использование будет таким:
open "$(find . -type f | shuf -n1)"
Переводы строк в именах файлов нарушат это. Ваши инструменты могут поддерживать или не поддерживать не-POSIX-опции, которые позволяют передавать данные с NUL-терминатором. Этот фрагмент работает в моем Debian:
find . -type f -print0 | shuf -z -n1
хотя если вы вставите его в $(…)
, конечные переносы строк (если таковые имеются) все равно будут удалены.
Чтобы исключить имена, можно использовать синтаксис вроде ! -name .DS_Store
, но чтобы исключить целые подкаталоги, нужно-prune
. Есть подводные камни:
- Порядок операндов имеет значение, например,
-prune
для каталога он должен быть перед-type f
,-print
/-print0
обычно находится в конце. - Логическое «или» (
-o
)часто требуются скобкии это не так интуитивно понятно, как вам бы хотелось. - Пропуск
-print
/-print0
может дать вам больше результатов, чем вы ожидаете. При сложной логике хорошо явно включать-print
/-print0
.
Изучите man 1 find
, чтобы узнать больше. Это рабочий пример, который исключает два каталога и два шаблона имен:
find . \( -name dir1 -o -name "dir 2" \) -prune -o -type f ! \( -name "*.txt" -o -name "echo*" \) -print
Поскольку вам нужно $(…)
, и я сказал вам, правильно цитировать, вы должны знать, что кавычки внутри $(…)
разбираются отдельно. Например, это правильно цитируется:
open "$(find . -type f ! -name "not this file" | shuf -n1)"
(сравниватьэтот ответ, причуда 2).