Исключить каталоги в поиске локаций

12

Поиск с помощью поиска locateпутей в файловой системе.
Зачастую вы априори знаете, что вас интересуют либо только файлы, либо только каталоги.
Поиск по местоположению часто возвращает много результатов. Было бы полезно включить в результат только один из типов, потому что это помогает сократить вывод.

Но есть более интересный аргумент, чтобы пропустить файлы или каталоги: потому что список путей к результатам может быть неоднозначным - не только в теории.

Приведенный ниже пример представляет собой реальный случай, и не является необычным:

$ locate --regex --basename "xfce4-keyboard-overlay$"
/usr/local/bin/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay

Хорошо, мы нашли что-то! Но ... файлы или каталоги?

$ file /usr/local/bin/xfce4-keyboard-overlay 
/usr/local/bin/xfce4-keyboard-overlay:   bash script

Так что это файл ...

$ file /usr/local/share/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay: directory

пока второго нет.

Эта двусмысленность делает длинные списки путей трудными для чтения, поэтому было бы очень хорошо отфильтровать каталоги, например, используя опцию командной строки для locate.

Существует ли что-то подобное? Даже если фильтр для каталогов отделен от locate?

По крайней мере, можно использовать скрипт для итерации всех имен файлов для проверки - что может быть медленным.

Volker Siegel
источник

Ответы:

3

С zsh:

print -rl ${(0)^"$(locate -0 ...)"}(N.)

(0)является флагом расширения параметра, который разделяется на символы NUL (как мы используем locate -0), сокращенно (ps:\0:).

С помощью ^, вместо добавления (N.)в конце массива, мы добавляем его к каждому элементу.

(N.)является .ограничителем глобуса, чтобы соответствовать только обычным файлам, Nчтобы удалить элемент, если он не совпадает (не существует или не является обычным файлом, или мы не можем проверить). Вы также можете использовать ^/вместо .сопоставления не-каталогов вместо только обычных файлов.

print -rlпечатает каждый аргумент raw в отдельной строке .

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

(обратите внимание, что он может потерпеть неудачу, если последний сообщенный файл locateоканчивается символами новой строки (неправильная подстановка команд присутствует во всех оболочках)).

Стефан Шазелас
источник
3

Это примерно так же не элегантно, как и другие ответы, но, возможно, менее неэффективно:

locate --regex --basename "xfce4-keyboard-overlay$" | 
        while IFS= read -r f; do [ -f "$f" ] && printf "%s\n" "$f"; done

(разбит на две строки для удобства чтения). Выше будут обрабатывать имена, содержащие пробелы. IFS=, Как представляется, необходимо обрабатывать имена с трейлинг пространства, и, конечно же , -rпозволяет обрабатывать обратные слэши.

locateПодход «давай трубу во что-то» может быть обречен на неудачу, если присутствуют пути, содержащие символы новой строки.


Для получения дополнительной информации IFSчитайте sh(1)или bash(1) (набрав man shили man bashв системе * nix и / или прочитав ее здесь , здесь , здесь и / или здесь ). Затем прочитайте Понимание IFS и Bash: прочитайте построчно, с IFS на Stack Exchange (сосредоточьтесь на ответах с более чем 5 голосами), и, если вам все еще не хватило, проверьте IFS на результатах поиска Грега в Wiki и IFS. в Bash Hackers Wiki (не в стеке).

G-Man говорит: «Восстанови Монику»
источник
Можете ли вы добавить информацию о том, что делает «IFS =» после вашего whileзаявления?
Роберт
Я так и сделал.
G-Man говорит: «Восстановите Монику»
обратная косая черта все еще будет проблемой во многих реализациях эха. Вы должны использовать printfдля произвольных данных .
Стефан Шазелас
может быть решение вашей проблемы с символами новой строки, используя параметр --null locateи увеличивайте его, readкак предложено здесь transnum.blogspot.ie/2008/11/…
robert
@ StéphaneChazelas: Хороший вопрос. Исправлена.
G-Man говорит: «Восстановите Монику»
2
locate --null --regex --basename "xfce4-keyboard-overlay$" |
  xargs -r0 sh -c 'find "$@" -prune ! -type d' sh
FloHimself
источник
На самом деле, это даже более грязно, чем кажется ... но хорошее вдохновение. Давайте представим, что это псевдокод, тогда это полезно :)
Volker Siegel
1
@Volker: я согласен, что это плохо: в вашем примере он перечислит /usr/local/share/xfce4-keyboard-overlay и каждый его подкаталог . Добавление -maxdepth 0помогает.
G-Man говорит: «Восстановите Монику»
Это идет еще лучше ...: D locate --regex --basename "xfce4-keyboard-overlay$" | xargs -I % sh -c "test -d % && echo %"
FloHimself
1
Использование xargsс findбыло хорошей идеей, я отредактировал его, чтобы сделать его устойчивым. Надеюсь, ты не возражаешь.
Стефан Шазелас
1

xargsбудет повторять команду для каждой строки, если вы укажете -L 1или -iпараметр.

Посмотреть здесь

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i bash -c '(test -d "{}" && echo "{}")'

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

РЕДАКТИРОВАТЬ: я не был очень доволен этим ответом, потому что это пиная новую оболочку для каждого файла. Это должно иметь только два процесса:

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i echo 'test -d "{}" && echo "{}"' | bash

Конечно, было бы неплохо, если бы мы вообще могли не пинать интерпретатора, но, xargsпохоже, его возможности ограничены в цепочке команд.

Роберт
источник
3
Тот только что перезагрузил мою машину (там был файл с именем, /home/evil/$(reboot)/xfce4-keyboard-overlayи я по глупости запустил его как root).
Стефан Шазелас
2
@ StéphaneChazelas +1 за смелость запускать «случайный кодез из интернета» от имени root;) (scnr)
Фолькер Зигель
0

Мои два цента:

while IFS= read i; \
do \
  if [ -f "$i" ]; \
  then \
    echo "$i"; \
  fi; \
done < <(locate --regex --basename "xfce4-keyboard-overlay$")

Это более или менее так, как G-Man сделал это в сочетании с заменой процесса.

Тристан Шторх
источник
На самом деле, это более или менее так, как я это сделал, в сочетании с заменой процесса, за исключением возможности обрабатывать имена файлов, содержащие обратную косую черту или конечный пробел. Также обратите внимание, что в заголовке вопроса указано «исключить каталоги», и этот ответ включает только каталоги.
G-Man говорит «Восстановить Монику»
Сожалею. Моя ошибка. Исправленный.
Тристан Шторх
-1

Что делать, если вы сочетаете locateс fileи grep? ...

$ for f in `locate --regex --basename "xfce4-keyboard-overlay$"`; do file $f; done | grep -vi directory
Petry
источник
Я не тестировал, но думаю, что это может быть медленно, потому что он создает процесс fileдля каждого отдельного пути. Обратите внимание, что часто есть много строк результатов для поиска. Мой текущий тест ищет "gnome", давая около 73000 путей для тестирования.
Фолькер Сигел
2
@Volker: Хуже того: для каждого $fфайла fileпрограмма откроет этот файл и прочитает его . Это очень дорого, когда все, что вам нужно сделать, это stat(). ………… Кроме того, это даст неправильные результаты для файлов, которые содержат «каталог» в своих именах (например, «phone_directory»). …………… (Кроме того, for f in `…`; do …синтаксис не может обрабатывать имена, содержащие пробелы.)
G-Man говорит «Восстановить Монику»