поскольку по сравнению с находкой в Bash

Я действительно думал об одной ситуации, в которой что-то как Щенок Linux будет довольно безопасен (или таким образом, я буду думать - я приветствую комментарии.) При выполнении его с Живого CD в системе без монтируемых устройств хранения (что не означает жесткого диска в системе или по крайней мере не того, который Вы когда-либо используете), затем даже при посещении веб-сайта, который использует некоторую дыру в неисправленном браузере, в следующий раз, Вы перезагружаете свою систему, будет чистым.* Конечно, между временем при посещении такого веб-сайта и перезагрузки мог быть некоторый клавиатурный перехватчик, ловя любые пароли, которые Вы вводите, таким образом, необходимо было бы быть осторожными, возможно, только посетив отмеченные веб-сайты, если Вы не планировали войти в систему где угодно. Вы могли сохранить файлы на карте флэш-памяти с интерфейсом USB, хотя снова необходимо будет быть осторожными, о каком просмотре веб-страниц Вы сделали, в то время как он был соединен (или прежде чем он был соединен).

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

28
14.05.2016, 06:05
9 ответов

1.

Первый:

for f in *; do
  echo "$f"
done

сбои для названных файлов -n, -e и варианты как -nene и с некоторым развертыванием удара, с именами файлов, содержащими обратные косые черты.

Второе:

find * -prune | while read f; do 
  echo "$f"
done

сбои еще для большего количества случаев (названные файлы !, -H, -name, (, имена файлов, которые запускаются или заканчиваются пробелами или содержат символы новой строки...),

Это - оболочка, которая расширяется *, find действительно только печатает файлы, которые это получает как аргументы. Вы, возможно, также использовали printf '%s\n' вместо этого, который как printf встроено, также избежал бы слишком многих args потенциальная ошибка.

2.

Расширение * отсортирован, можно сделать его немного быстрее, если Вам не нужна сортировка. В zsh:

for f (*(oN)) printf '%s\n' $f

или просто:

printf '%s\n' *(oN)

bash не имеет никакого эквивалента насколько я могу сказать, таким образом, необходимо было бы обратиться к find.

3.

find . ! -name . -prune ! -name '.*' -print0 |
  while IFS= read -rd '' f; do
    printf '%s\n' "$f"
  done

(выше использования GNU/BSD -print0 нестандартное расширение).

Это все еще включает порождение находки, управляют и используют медленное while read цикл, таким образом, это, вероятно, будет медленнее, чем использование for цикл, если список файлов не огромен.

4.

Кроме того, вопреки подстановочному расширению оболочки, find сделает a lstat системный вызов на каждом файле, таким образом, маловероятно, что несортировка компенсирует это.

С GNU/BSD find, этого можно избежать при помощи их -maxdepth расширение, которое инициирует оптимизацию, сохраняющую lstat:

find . -maxdepth 1 ! -name '.*' -print0 |
  while IFS= read -rd '' f; do
    printf '%s\n' "$f"
  done

Поскольку find начинает производить имена файлов, как только это находит их (за исключением выходной буферизации stdio), где это может быть быстрее, то, если то, что Вы делаете в цикле, является трудоемким, и список имен файлов является больше, чем буфер stdio (4/8 КБ). В этом случае обработка в цикле запустится прежде find закончил находить все файлы. На GNU и системах FreeBSD, можно использовать stdbuf заставить это происходить раньше (отключающий stdio буферизующий).

5.

POSIX/стандарт/портативное устройство путь к командам выполнения для каждого файла с find должен использовать -exec предикат:

find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'

В случае echo хотя, это менее эффективно, чем выполнение цикличного выполнения в оболочке, поскольку оболочка будет иметь встроенную версию echo в то время как find должен будет породить новый процесс и выполниться /bin/echo в нем для каждого файла.

Если необходимо выполнить несколько команд, можно сделать:

find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'

Но остерегайтесь этого cmd2 только выполняется если cmd1 успешно.

6.

Канонический способ выполнить сложные команды для каждого файла состоит в том, чтобы назвать оболочку с -exec ... {} +:

find . ! -name . -prune ! -name '.*' -exec sh -c '
  for f do
    cmd1 "$f"
    cmd2 "$f"
  done' sh {} +

То время, мы вернулись к тому, чтобы быть эффективным с echo так как мы используем shвстроенный и -exec + версия мечет икру как немногие sh как возможный.

7.

В моих тестах на каталоге с 200 000 файлов с краткими названиями на ext4, zsh один (абзац 2.) является безусловно самым быстрым, сопровождается первым простым for i in * цикл (хотя, как обычно, bash намного медленнее, чем другие оболочки для этого).

9
27.01.2020, 19:39
  • 1
    , что делает ! в команде находки? –  rubo77 17.02.2014, 11:43
  • 2
    @rubo77, ! для отрицания. ! -name . -prune more... сделаю -prunemore... с тех пор -prune всегда возвращает true) для каждого файла, но .. Таким образом, это сделает more... на всех файлах в ., но исключит . и не будет убывать в подкаталоги .. Таким образом, это - стандартный эквивалент GNU -mindepth 1 -maxdepth 1. –  Stéphane Chazelas 17.02.2014, 12:04

Я попробовал это на каталоге с 2 259 записями и использовал time команда.

Вывод time for f in *; do echo "$f"; done (минус файлы!):

real    0m0.062s
user    0m0.036s
sys     0m0.012s

Вывод time find * -prune | while read f; do echo "$f"; done (минус файлы!):

real    0m0.131s
user    0m0.056s
sys     0m0.060s

Я несколько раз выполнял каждую команду, чтобы устранить неудачные обращения в кэш. Это предлагает удержать его bash (поскольку я в...), более быстро, чем использование find и передача по каналу вывода (к bash)

Только для полноты, я отбросил канал от find, с тех пор в Вашем примере, это совершенно избыточно. Вывод просто find * -prune :

real    0m0.053s
user    0m0.016s
sys     0m0.024s

Кроме того, time echo * (вывод не является разделенной новой строкой, увы):

real    0m0.009s
user    0m0.008s
sys     0m0.000s

На данном этапе я подозреваю причину echo * более быстро, это не производит столько новых строк, таким образом, вывод не прокручивает столько же. Давайте протестируем...

time find * -prune | while read f; do echo "$f"; done > /dev/null

урожаи:

real    0m0.109s
user    0m0.076s
sys     0m0.032s

в то время как time find * -prune > /dev/null урожаи:

real    0m0.027s
user    0m0.008s
sys     0m0.012s

и time for f in *; do echo "$f"; done > /dev/null урожаи:

real    0m0.040s
user    0m0.036s
sys     0m0.004s

и наконец: time echo * > /dev/null урожаи:

real    0m0.011s
user    0m0.012s
sys     0m0.000s

Часть изменения может составляться случайными факторами, но это кажется ясным:

  • вывод является медленным
  • передача по каналу затрат немного
  • for f in *; do ... медленнее, чем find * -prune, самостоятельно, но для конструкций выше вовлечения каналов, более быстро.

Кроме того, как в стороне, оба подхода, кажется, обрабатывают имена с пробелами очень хорошо.

Править:

Синхронизации для find . -maxdepth 1 > /dev/null по сравнению с. find * -prune > /dev/null:

time find . -maxdepth 1 > /dev/null:

real    0m0.018s
user    0m0.008s
sys     0m0.008s

find * -prune > /dev/null:

real    0m0.031s
user    0m0.020s
sys     0m0.008s

Так, дополнительное заключение:

  • find * -prune медленнее, чем find . -maxdepth 1 - в первом оболочка обрабатывает шарик, затем создавая (большую) командную строку для find. NB: find . -prune возвраты просто ..

Больше тестов: time find . -maxdepth 1 -exec echo {} \; >/dev/null:

real    0m3.389s
user    0m0.040s
sys     0m0.412s

Заключение:

  • самый медленный способ сделать это до сих пор. Как был указан в комментариях для ответа, где этот подход был предложен, каждый аргумент порождает оболочку.
18
27.01.2020, 19:39
  • 1
    Какой канал избыточен? можно ли показать строку, которую Вы использовали без канала? –  rubo77 22.10.2013, 11:33
  • 2
    @rubo77 find * -prune | while read f; do echo "$f"; done имеет избыточный канал - весь канал делает, производит точно что find выводы самостоятельно. Без канала это было бы просто find * -prune Канал только избыточен конкретно, потому что вещь с другой стороны канала просто копирует stdin в stdout (по большей части). Это - дорогое нет. Если Вы хотите сделать материал с выводом находки, кроме просто плевать это отступает снова, это отличается. –  Phil 22.10.2013, 11:36
  • 3
    Возможно, трудоемкое основное *. Поскольку BitsOfNix заявил: Я все еще настоятельно рекомендую не использовать * и . для find вместо этого. –  rubo77 22.10.2013, 11:45
  • 4
    @rubo77 кажется тем путем. Я предполагаю, что пропустил это. Я добавил результаты для своей системы. Я принимаю find . -prune быстрее потому что find будет читать запись каталога дословно, в то время как оболочка будет делать аналогично, потенциально соответствуя против шарика (мог бы оптимизировать для *), затем создавая большую командную строку для find. –  Phil 22.10.2013, 11:57
  • 5
    find . -prune печать только . в моей системе. Это не делает почти никакой работы вообще. Это нисколько не то же как find * -prune это показывает все имена в текущем каталоге. Пустое read f исказит имена файлов с продвижением пробелов. –  Ian D. Allen 22.10.2013, 17:13
find * -prune | while read f; do 
    echo "$f"
done

бесполезное использование find - То, что Вы говорите, эффективно "для каждого файла в каталоге (*), не находите файлы. Кроме того, это не безопасно по нескольким причинам:

  • Обратные косые черты в путях рассматривают особенно без -r опция к read. Это не проблема с for цикл.
  • Новые строки в путях повредили бы любую нетривиальную функциональность в цикле. Это не проблема с for цикл.

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

7
27.01.2020, 19:39
  • 1
    @I0b0 Что относительно находки - соединяет '. каналом /*' - сокращают или находят - соединяют '. каналом / [^.] *' - чернослив (для предотвращения скрытых файлов и каталогов) как лучшая конструкция - в полной форме: найдите - соединяют '. каналом /*' - сокращают-print0 | xargs-0 шеннонов-c '...'? –  AsymLabs 23.10.2013, 18:59
  • 2
    Ни один find -print0 ни xargs' -0 совместимый POSIX, и Вы не можете вставить произвольные команды sh -c ' ... ' (одинарных кавычек нельзя оставить в одинарных кавычках), таким образом, это не совсем настолько просто. –  l0b0 24.10.2013, 01:54

Но мы - любители вопросов о производительности! Этот запрос на эксперимент делает по крайней мере два предположения, которые делают его не ужасно допустимым.

A. Предположите, что они находят те же файлы …

Ну, они найдут те же файлы сначала, потому что они оба выполняют итерации по тому же шарику, а именно, *. Но find * -prune | while read f страдает от нескольких дефектов, которые делают это довольно возможным, что это не найдет все файлы, которые Вы ожидаете:

  1. Находка POSIX, как гарантируют, не примет больше чем один параметр пути. Большинство find реализации делают, но тем не менее, Вы не должны полагаться на это.
  2. find * может повредиться, когда Вы совершаете нападки ARG_MAX. for f in * не будет, потому что ARG_MAX относится exec, не builtins.
  3. while read f может порвать с запуском имен файлов и окончанием пробелом, который будет неизолированным. Вы могли преодолеть это с while read и его параметр по умолчанию REPLY, но это все еще не поможет Вам когда дело доходит до имен файлов с новыми строками в них.

B. echo. Ничья попытка сделать это только для повторения названия файла. Если Вы хотите это, просто сделайте один из них:

printf '%s\n' *
find . -mindepth 1 -maxdepth 1 # for dotted names, too

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

Для ответа на вопрос вот, результаты в моем каталоге, который имеет 184 файла и каталоги в нем.

$ time bash -c 'for i in {0..1000}; do find * -prune | while read f; do echo "$f"; done >/dev/null; done'

real    0m7.998s
user    0m5.204s
sys 0m2.996s
$ time bash -c 'for i in {0..1000}; do for f in *; do echo "$f"; done >/dev/null; done'

real    0m2.734s
user    0m2.553s
sys 0m0.181s
$ time bash -c 'for i in {0..1000}; do printf '%s\n' * > /dev/null; done'

real    0m1.468s
user    0m1.401s
sys 0m0.067s

$ time bash -c 'for i in {0..1000}; do find . -mindepth 1 -maxdepth 1 >/dev/null; done '

real    0m1.946s
user    0m0.847s
sys 0m0.933s
4
27.01.2020, 19:39
  • 1
    я не согласовываю с оператором цикла с условием продолжения, порождает подоболочку - в худшем случае случай, новый поток: следующее пытается показать прежде и после, извинения за плохое форматирование $ ps ax | grep bash 20784 pts/1 Ss 0:00 -bash 20811 pts/1 R+ 0:00 grep bash $ while true; do while true; do while true; do while true; do while true; do sleep 100; done; done; done; done; done ^Z [1]+ Stopped sleep 100 $ bg [1]+ sleep 100 & $ ps ax | grep bash 20784 pts/1 Ss 0:00 -bash 20924 pts/1 S+ 0:00 grep bash –  Phil 22.10.2013, 21:37
  • 2
    Технически я неправильно произнес: канал вызывает неявную подоболочку, не цикл с условием продолжения. Я отредактирую. –  kojiro 22.10.2013, 21:43

Я пошел бы определенно с находкой, хотя я изменю Вашу находку на просто это:

find . -maxdepth 1 -exec echo {} \;

Мудрая производительность, find намного быстрее в зависимости от Ваших потребностей, конечно. С чем Вы в настоящее время имеете for это только отобразит файлы/каталоги в текущем каталоге, но не содержании каталогов. Если Вы используете, находят, что это также покажет содержание подкаталогов.

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

Как пример, в одной из систем, которые я в настоящее время использую, существует несколько каталогов с более чем 2 миллионами файлов (<100k каждый):

find *
-bash: /usr/bin/find: Argument list too long
10
27.01.2020, 19:39
  • 1
    я добавил -prune сделать эти два примера более подобными. и я предпочитаю канал с в то время как, таким образом, легче применить больше команд в цикле –  rubo77 22.10.2013, 10:03
  • 2
  • 3
    , изменяющий жесткий предел, является едва надлежащим обходным решением от моего POV. Особенно при разговоре приблизительно 2 + миллион файлов. Без отклонения от Вопроса для простых случаев, поскольку один каталог уровня для быстрее, но если Вы измените свой файл/структуру каталогов, то будет более трудно мигрировать. В то время как с находкой и это - огромное количество опций, Вы можете быть лучше подготовлены. Тем не менее я все еще настоятельно рекомендую не использовать * и. для находки вместо этого. Это было бы более портативно, чем *, где Вы не смогли управлять жестким пределом... –  BitsOfNix 22.10.2013, 10:37
  • 4
    , Которое породит один процесс эха на файл (в то время как в оболочке для цикла, это - эхо, встроенное, который будет использоваться, не разветвляя дополнительный процесс), и будет убывать в каталоги, таким образом, это будет намного медленнее. Также обратите внимание, что это будет включать точечные файлы. –  Stéphane Chazelas 22.10.2013, 13:52
  • 5
    Вы правы, я добавил maxdepth 1 так, чтобы он придерживался текущего уровня только. –  BitsOfNix 22.10.2013, 13:53

Хорошо для начинающих for ключевое слово оболочки, встроенное в Bash, в то время как find отдельный исполняемый файл.

$ type -a for
for is a shell keyword

$ type -a find
find is /usr/bin/find

for цикл только найдет файлы от globstar символа, когда он расширится, он не рекурсивно вызовет ни в какие каталоги, которые он находит.

Найдите на otherhand, будет также дан список, расширенный globstar, но он рекурсивно найдет все файлы и каталоги ниже этого расширенного списка и передаст каждого по каналу до while цикл.

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

Это обо всем, о чем я могу думать стоящий комментария эти 2 подхода.

1
27.01.2020, 19:39
  • 1
    , который я добавил - чернослив к команде находки, таким образом, они более подобны. –  rubo77 22.10.2013, 10:08

find * не будет работать правильно если * производит маркеры, которые похожи на предикаты, а не пути.

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

Для устранения этой проблемы, можно использовать find ./* вместо этого. Но затем это не производит точно те же строки как for x in *.

Отметьте это find ./* -prune | while read f .. на самом деле не использует функциональность сканирования find. Это - globbing синтаксис ./* который на самом деле пересекает каталог и генерирует имена. Затем find программа должна будет работать, по крайней мере, a stat проверьте каждое из тех имен. У Вас есть издержки запуска программы, и наличие его получают доступ к этим файлам и затем выполнению ввода-вывода для чтения его вывода.

Трудно вообразить, как это могло быть совсем не менее эффективно, чем for x in ./* ....

2
27.01.2020, 19:39

В течение многих лет я использовал этот:-

find . -name 'filename'|xargs grep 'pattern'|more

искать определенные файлы (например, *.txt), которые содержат шаблон, который grep может искать и передать его по каналу в больше так, чтобы он не прокручивал от экрана. Иногда я использую>> канал для записи результатов в другой файл, на который я могу посмотреть позже.

Вот образец результата:-

./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:Message-ID: <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:In-Reply-To: <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:  <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:  <A165CE5C-61C5-4794-8651-66F5678ABCBF@usit.net>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2008-August.txt:Message-ID: <448E53556A3F442ABC58203D6281923E@hypermax>
./Documents/Organ_docos/Rodgerstrio321A/rodgersmylist/2011-April.txt:URL: http://mylist.net/private/rodgersorganusers/attachments/20110420/3f
0
27.01.2020, 19:39

Если все файлы, возвращенные находкой, могут быть обработаны единственной командой (очевидно, не применимый к Вашему примеру эха выше), можно использовать xargs:

find * |xargs some-command
0
27.01.2020, 19:39

Теги

Похожие вопросы