Как использовать команду find для вывода списка имен файлов, но не пути?
Я проверил несколько ответов в Интернете, но они не работают, когда -name
в команде есть параметр (возможно, я что-то делаю не так, не уверен). Я хотел бы указать только имена файлов (а не каталог / путь), и, чтобы усложнить, у меня такое же имя файла с другим расширением, поэтому мне нужно отфильтровать результат, используя -name
и -o
, проблема в предложениях, которые я видел использовать либо -printf
или basename
, и они не работают, когда есть -name *.txt -o name *.pdf
. Есть ли другой способ решить эту проблему?
Пример моих файлов:
/dir1/dir2/dir3/dir4/
/file1.txt
/file1.pdf
/file1.ods ...etc.
Меня интересует только перечисление одного или нескольких типов файлов для каждого списка (с использованием -name
для фильтрации моих результатов.
Можно ли сделать это, используя только 1 find
команду, или мне нужно сделать это за 2 шага (сохранить результат во временном файле, а затем отфильтровать / удалить каталог из временного файла)? Результатом, которого я пытаюсь добиться, является текстовый файл с именем файла в каждой строке:
file1.txt
file1.pdf
file1.ods
file2.txt
file2.pdf
etc.
Я использовал команду
find /dir1/dir2/dir3/dir4/ -type f -name '*.txt' -o -name '*.pdf' -o -name '*.ods' -printf "%f\n"
Я использую GNU find.
Обновить
Оказалось, я должен поставить \(...\)
, как напомнил αғsнιη
Ответы
При использовании нескольких выражений в режиме логического ИЛИ -o
вам необходимо заключить их все внутрь \( ... \)
, иначе всегда будет учитываться результат последнего выражения; и -printf '%f\n'
используется для вывода имен файлов только с удаленными ведущими каталогами.
find . -type f \( -name '*.pdf' -o -name '*.txt' \) -printf '%f\n'
см man findпод ОПЕРАТОРАМИ раздела о ( expr )
:
ОПЕРАТОРЫ
...(expr)
Приоритет силы. Поскольку круглые скобки являются специальными для оболочки, вам обычно нужно заключать их в кавычки.
поэтому вы можете использовать \( ... \)
или '(' ... ')'
(пробелы после и перед круглыми скобками важны), чтобы избежать их.
Как уже было сказано, вам нужно сгруппировать свои -name
тесты в скобках. Эти круглые скобки должны быть экранированы \( ... \)
или '(' ... ')'
заключены в кавычки, например, для защиты от оболочки (где в противном случае они обозначили бы подоболочку).
Используя стандарт find
, а также немного упростив обработку суффиксов имени файла:
set -- txt pdf ods
for suffix do
set -- "$@" -o -name "*.$suffix"
shift
done
shift
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename {} \;
Это сначала устанавливает позиционные параметры в список суффиксов имени файла, которые мы хотели бы видеть (это также может быть взято из командной строки, если это сценарий), и создает список -name
тестов по ИЛИ . Затем это используется в find
командной строке.
Затем сама find
команда вызывает basename
утилиту для каждого найденного имени.
В качестве альтернативы мы могли бы вместо этого использовать подстановку параметра для удаления всех компонентов каждого пути, кроме имени файла, но для этого мы должны передать имена пути во встроенный sh -c
скрипт:
# [...] as before [...]
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec sh -c '
for pathname do
printf "%s\n" "${pathname##*/}"
done' sh {} +
Я ожидал, что это будет работать быстрее, чем вариант с использованием basename
, по крайней мере, если есть много имен файлов для обработки, хотя с GNU basename
из coreutils вы можете использовать -a
или --multiple
нравится так:
find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename -a {} +
Это будет звонить basename
как можно реже. Но опять же, если у вас установлен coreutils, у вас также может быть findutils с GNU find
, и вы можете использовать его -printf '%f\n'
для того же.