Как исключить каталог в find.команда - PullRequest
1149 голосов
/ 18 ноября 2010

Я пытаюсь запустить команду find для всех файлов JavaScript, но как исключить конкретный каталог?

Вот код find, который мы используем.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done

Ответы [ 33 ]

1759 голосов
/ 01 апреля 2013

Если -prune не работает для вас, это будет:

find -name "*.js" -not -path "./directory/*"

Предупреждение: требует обхода всех нежелательных каталогов.

890 голосов
/ 18 ноября 2010

Используйте переключатель -prune.Например, если вы хотите исключить каталог misc, просто добавьте -path ./misc -prune -o в команду поиска:

find . -path ./misc -prune -o -name '*.txt' -print

Вот пример с несколькими каталогами:

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print

Здесь мы исключаем dir1 , dir2 и dir3 , поскольку в выражениях find это действие, которое действует по критериям -path dir1 -o -path dir2 -o -path dir3 (если dir1 или dir2 или dir3 ), а также type -d.

Дальнейшее действие - -o print, просто напечатайте.

416 голосов
/ 16 мая 2013

Мне легче рассуждать о следующих решениях, чем о других предлагаемых решениях:

find build -not \( -path build/external -prune \) -name \*.js
# you can also exclude multiple paths
find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

Важное примечание: пути, которые вы вводите после -path, должны точно соответствовать тому, что find напечатает без исключения. Если это предложение сбивает вас с толку, просто используйте полные пути через команду whole , например: find <strong>/full/path/</strong> -not \( -path <strong>/full/path/exclude/this</strong> -prune \) .... См. Примечание [1], если вы хотите лучшего понимания.

Внутри \( и \) - это выражение, которое будет точно соответствовать build/external (см. Важное примечание выше) и, в случае успеха, позволит избежать обхода чего-либо ниже, Затем это группируется как одно выражение с экранированными круглыми скобками и начинается с префикса -not, что заставит find пропускать все, что соответствует этому выражению.

Кто-то может спросить, не добавит ли -not все остальные файлы, скрытые -prune, и ответ будет отрицательным. -prune работает так, что все, что, когда оно достигнуто, файлы в этом каталоге постоянно игнорируются.

Это происходит из реального случая использования, когда мне нужно было вызвать yui-compress для некоторых файлов, сгенерированных wintersmith, но пропустить другие файлы, которые нужно отправить как есть.


Примечание [1] : Если вы хотите исключить /tmp/foo/bar, и вы запускаете поиск как этот "find /tmp \(...", то вы должны указать -path /tmp/foo/bar. Если, с другой стороны, вы запускаете find как этот cd /tmp; find . \(..., то вы должны указать -path ./foo/bar.

187 голосов
/ 04 июля 2014

Здесь явно есть некоторая путаница относительно того, каким должен быть предпочтительный синтаксис для пропуска каталога.

Мнение GNU

To ignore a directory and the files under it, use -prune

Из GNU найдите страницу руководства

Аргументация

-prune останавливает find от спуска в каталог.Просто указав -not -path, вы все равно попадете в каталог skipped , но -not -path будет иметь значение false, когда find проверяет каждый файл.

Проблемы с -prune

-prune делает то, для чего он предназначен, но все же есть некоторые вещи, о которых вы должны позаботиться при его использовании.

  1. find отпечаткисокращенный каталог.

    • TRUE Это предполагаемое поведение, оно просто не сходит в него.Чтобы вообще не печатать каталог, используйте синтаксис, который логически его опускает.
  2. -prune работает только с -print и никакими другими действиями.

    • НЕ ИСТИНА .-prune работает с любым действием, кроме -delete. Почему это не работает с delete? Чтобы -delete работал, find должен пройти через каталог в порядке DFS, так как -delete сначала удалит листья, затем родители листьев и т. Д.... Но для того, чтобы указать -prune, чтобы иметь смысл, find необходимо нажать на каталог и прекратить его убывание, что явно не имеет смысла при -depth или -delete на

Производительность

Я настроил простой тест из трех самых популярных ответов на этот вопрос (заменил -print на -exec bash -c 'echo $0' {} \;, чтобы показать другой пример действия).Результаты ниже

----------------------------------------------
# of files/dirs in level one directories
.performance_test/prune_me     702702    
.performance_test/other        2         
----------------------------------------------

> find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 23513814

> find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 10670141

> find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 864843145

Заключение

Оба f10bit синтаксис и Даниэль С. Собрал занял 10-25msбежать в среднем. Синтаксис GetFree , который не использует -prune, занял 865 мс.Так что, да, это довольно экстремальный пример, но если вы заботитесь о времени выполнения и делаете что-то дистанционно, вы должны использовать -prune.

Примечание Синтаксис Даниэля С. Собрала показал лучший из двух синтаксисов -prune;но я сильно подозреваю, что это результат некоторого кеширования, так как переключение порядка, в котором эти два прогона приводили к противоположному результату, в то время как версия без обрезки всегда была самой медленной.

ТестСкрипт

#!/bin/bash

dir='.performance_test'

setup() {
  mkdir "$dir" || exit 1
  mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
    "$dir/other"

  find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
  find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
  touch "$dir/other/foo"
}

cleanup() {
  rm -rf "$dir"
}

stats() {
  for file in "$dir"/*; do
    if [[ -d "$file" ]]; then
      count=$(find "$file" | wc -l)
      printf "%-30s %-10s\n" "$file" "$count"
    fi
  done
}

name1() {
  find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
}

name2() {
  find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
}

name3() {
  find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
}

printf "Setting up test files...\n\n"
setup
echo "----------------------------------------------"
echo "# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo "----------------------------------------------"

printf "\nRunning performance test...\n\n"

echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
name1
s=$(date +%s%N)
name1_num=$(name1 | wc -l)
e=$(date +%s%N)
name1_perf=$((e-s))
printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"

echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
name2
s=$(date +%s%N)
name2_num=$(name2 | wc -l)
e=$(date +%s%N)
name2_perf=$((e-s))
printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"

echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
name3
s=$(date +%s%N)
name3_num=$(name3 | wc -l)
e=$(date +%s%N)
name3_perf=$((e-s))
printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"

echo "Cleaning up test files..."
cleanup
59 голосов
/ 18 ноября 2010

Один из вариантов - исключить все результаты, содержащие имя каталога, с помощью grep.Например:

find . -name '*.js' | grep -v excludeddir
46 голосов
/ 15 марта 2018

Это единственный, который работал для меня.

find / -name NameOfFile ! -path '*/Directory/*'

Поиск "NameOfFile", исключая "Directory".Сделай акцент на звезды *.

39 голосов
/ 18 ноября 2010

Я предпочитаю запись -not ... это более читабельно:

find . -name '*.js' -and -not -path directory
18 голосов
/ 18 ноября 2010

Используйте опцию -prune.Итак, что-то вроде:

find . -type d -name proc -prune -o -name '*.js'

'-type d -name proc -prune' ищет только каталоги с именем proc для исключения.
'-o' является оператором 'ИЛИ'.

15 голосов
/ 18 января 2016

Это формат, который я использовал для исключения некоторых путей:

$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"

Я использовал это, чтобы найти все файлы не по пути ". *":

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
14 голосов
/ 17 ноября 2017

-prune определенно работает и является лучшим ответом, потому что он предотвращает спуск в каталог, который вы хотите исключить.-not -path, который по-прежнему ищет исключенный каталог, он просто не печатает результат, что может быть проблемой, если исключенный каталог является подключенным сетевым томом или у вас нет прав доступа.

Хитростьfind очень внимательно относится к порядку аргументов, поэтому, если вы не получите их правильно, ваша команда может не сработать.Порядок аргументов обычно такой:

find {path} {options} {action}

{path}: сначала поставить все аргументы, связанные с путем, например . -path './dir1' -prune -o

{options}: у меня больше всего успеховпоместив -name, -iname, etc как последний вариант в этой группе.Например, -type f -iname '*.js'

{action}: вы захотите добавить -print при использовании -prune

Вот рабочий пример:

# setup test
mkdir dir1 dir2 dir3
touch dir1/file.txt; touch dir1/file.js
touch dir2/file.txt; touch dir2/file.js
touch dir3/file.txt; touch dir3/file.js

# search for *.js, exclude dir1
find . -path './dir1' -prune -o -type f -iname '*.js' -print

# search for *.js, exclude dir1 and dir2
find . \( -path './dir1' -o -path './dir2' \) -prune -o -type f -iname '*.js' -print
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...