Используйте команду поиска, но исключите файлы в двух каталогах

86

Я хочу , чтобы найти файлы , которые заканчиваются _peaks.bed, но исключить файлы в tmpи scriptsпапках.

Моя команда такая:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

Но это не сработало. Файлы в tmpи по- scriptпрежнему будет отображаться папки.

Есть у кого-нибудь идеи по этому поводу?

Ханфэй Сунь
источник

Ответы:

190

Вот как вы можете указать это с помощью find:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

Пояснение:

  • find . - Начать поиск из текущего рабочего каталога (по умолчанию рекурсивно)
  • -type f- Укажите, findчто вам нужны только файлы в результатах
  • -name "*_peaks.bed" - Ищите файлы с именем, заканчивающимся на _peaks.bed
  • ! -path "./tmp/*" - Исключить все результаты, путь которых начинается с ./tmp/
  • ! -path "./scripts/*" - Также исключить все результаты, путь которых начинается с ./scripts/

Тестирование решения:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

Вы были довольно близки, -nameопция учитывает только базовое имя, а as -pathучитывает весь путь =)

Сампсон-Чен
источник
Хорошая работа. Однако вы забыли одну из вещей, которые хотел OP, - найти файлы, заканчивающиеся на _peaks.bed.
Alex
2
Здесь используется ряд расширений в GNU find, но поскольку вопрос помечен как Linux, это не проблема. Хороший ответ.
Джонатан Леффлер
1
Краткое примечание: если вы используете его .в исходном запросе поиска, вы должны использовать его для каждого исключаемого пути. Соответствие пути довольно строгое, оно не выполняет нечеткого поиска. Так что если вы воспользуетесь им, find / -type f -name *.bed" ! -path "./tmp/"это не сработает. вам нужно ! -path "/tmp"сделать его счастливым.
peelman
3
Важно отметить, что * важен. $ ! -path "./directory/*"
Томас Беннетт
3
Согласно страницам руководства: «Чтобы игнорировать все дерево каталогов, используйте, -pruneа не проверяйте каждый файл в дереве». Если ваши исключенные каталоги занимают очень много места или содержат множество файлов, и вы заботитесь о производительности, используйте эту -pruneопцию.
thdoan
8

Вот один из способов сделать это ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"
Алекс
источник
2
В этом есть преимущество работы с любой версией find, а не только с GNU find. Однако вопрос помечен как Linux, так что это не критично.
Джонатан Леффлер
2

Использовать

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

или

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

или

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

Порядок важен. Он оценивается слева направо. Всегда начинайте с исключения пути.

Объяснение

Не используйте -not(или !) для исключения всего каталога. Используйте -prune. Как объяснено в руководстве:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

и в руководстве по поиску GNU:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

Действительно, если вы используете -not -path "./pathname", find будет оценивать выражение для каждого узла ниже "./pathname".

Выражения find - это просто оценка условий.

  • \( \)- групповая работа (можно использовать -path "./tmp" -prune -o -path "./scripts" -prune -o, но более многословно).
  • -path "./script" -prune- если -pathвозвращает истину и является каталогом, вернуть истину для этого каталога и не спускаться в него.
  • -path "./script" ! -prune- оценивается как (-path "./script") AND (! -prune). Он меняет «всегда верно» для слова «всегда ложно». Это позволяет избежать печати "./script"совпадений.
  • -path "./script" -prune -false- поскольку -pruneвсегда возвращает истину, вы можете следовать за ним, -falseчтобы сделать то же самое, чем !.
  • -o- Оператор ИЛИ. Если между двумя выражениями не указан оператор, по умолчанию используется оператор AND.

Следовательно, \( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printрасширяется до:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

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

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printдобавляется с помощью find - поэтому в большинстве случаев вам не нужно добавлять его в выражение. А поскольку -pruneвернет true, он напечатает «./script» и «./tmp».

В других это не обязательно, потому что мы переключились -pruneна всегда возвращать false.

Подсказка: вы можете использовать, find -D opt expr 2>&1 1>/dev/nullчтобы увидеть, как он оптимизирован и расширен,
find -D search expr 2>&1 1>/dev/nullчтобы увидеть, какой путь отмечен.

f380cedric
источник
0

Попробуйте что-нибудь вроде

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

и не удивляйтесь, если я ошибаюсь. Если целью является выполнение (вместо печати), просто замените его на место.

DrC
источник
0

для меня это решение не сработало с командой exec с find, действительно не знаю почему, поэтому мое решение

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

Пояснение: то же, что и сампсон-чен, с добавлением

-prune - игнорировать исходящий путь ...

-o - Затем, если совпадений нет, распечатать результаты (обрезать каталоги и распечатать оставшиеся результаты)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz
al3x2ndru
источник
Принятый ответ не сработал, но это работает. Использование чернослива, find . -path ./scripts -prune -name '*_peaks.bed' -type f. Не знаете, как исключить несколько каталогов. Здесь также отображается исключенный каталог верхнего уровня, даже если typeон указан. Исключение с помощью Grep кажется более простым, если вы не хотите использовать prune для ускорения операции поиска.
Mohnish
У меня тоже были проблемы с исключением нескольких каталогов, но приведенные выше комментарии дали мне ответ, который сработал. Я использую несколько экземпляров «-not -path», и в каждое выражение пути я включаю полный префикс, который используется в первом параметре для «find», и заканчиваю каждый звездочкой (и избегаю любых точек).
jetset
0

Вы можете попробовать следующее:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'
Джеки Цзян
источник
2
На такой старый вопрос (4 года!) Вы хотите объяснить, почему этот новый ответ лучше или отличается от него, а не просто «сбросить» код.
Nic3500 06