Почему использование является циклом оболочки к тексту процесса, который рассматривают плохой практикой?

Просто используйте pkill. Хотя не стандартная команда, это найдено во многих Нельдах и выделено этому виду задач.

pkill -f 'vmstat 1'

Также обратите внимание что Ваш grep будет соответствовать vmstat 1 но также и vmstat 10 и grep vmstat 1 (так был бы это pkill выше), и awk надмножество grep. Чтобы быть более устойчивыми, Вы могли сделать вместо этого:

ps -Ao pid,args | awk '$2 == "vmstat" && $3 == "1" {print $1}' | xargs kill

Или

pkill -xf 'vmstat 1'
202
13.04.2017, 15:36
4 ответа

Вы также можете обратиться к bash за помощью в ответе на эти вопросы, используя set -x . Этот флаг указывает bash для отображения команды и ее расширенных аргументов перед запуском:

Сначала некоторые установки:

$ a=some-value
$ b=some-other-value
$ set -x

Теперь посмотрим, как оболочка интерпретирует эти команды:

$ a=b
+ a=b

Значение переменной a устанавливать к литералу последовательности «b».

Теперь попробуем $ b :

$ a=$b
+ a=some-other-value

Здесь мы попросим оболочку вычислить $ b и вставить значение b в команду.

Это показывает некоторые из значений оболочки - она интерпретирует переменные и подстановочные знаки вместо того, чтобы заставить отдельные команды выполнять работу. Например, ls не должен знать, как интерпретировать «* .txt», потому что оболочка выполняет работу до запуска ls :

$ ls *.txt
+ ls a.txt b.txt c.txt
a.txt  b.txt  c.txt
-121--246734-

Да, мы видим ряд вещей, таких как:

while read line; do
  echo $line | cut -c3
done

Или хуже:

for line in `cat file`; do
  foo=`echo $line | awk '{print $2}'`
  echo whatever $foo
done

(не смейтесь, я видел много таких).

Как правило, от начинающих сценариев оболочки. Это наивные буквенные переводы того, что бы вы делали в императивных языках, таких как C или python, но это не так, как вы делаете вещи в оболочках, и эти примеры очень неэффективны, совершенно ненадежны (потенциально приводят к проблемам безопасности), и если вам когда-нибудь удастся исправить большинство ошибок, ваш код становится неразборчивым.

Концептуально

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

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

Одной из замечательных вещей, представленных Unix, был pipe и те потоки stdin/stdout/stderr по умолчанию, которые обрабатываются всеми командами.

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

У вас есть режущий инструмент и транслитный инструмент, и вы можете просто сделать:

cut -c4-5 < in | tr a b > out

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

Вызов инструмента, хотя и имеет стоимость (и мы разработаем это в точке производительности). Эти инструменты могут быть написаны с тысячами инструкций в C. Процесс должен быть создан, инструмент должен быть загружен, инициализирован, затем очищен, процесс уничтожен и ожидание.

Вызов cut подобен открытию кухонного ящика,взять нож, использовать, вымыть, высушить, положить обратно в ящик. Когда вы делаете:

while read line; do
  echo $line | cut -c3
done < file

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

Некоторые из этих инструментов ( читать и echo ) встроены в большинство оболочек, но это вряд ли имеет значение, поскольку echo и cut все еще должны выполняться в отдельных процессах.

Это как резать лук, но мыть нож и положить его обратно в кухонный ящик между каждым ломтиком.

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

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

Далее читайте в хорошем ответе Брюса . Внутренние инструменты обработки текста низкого уровня в оболочках (за исключением zsh ) ограничены, громоздки и обычно не подходят для общей обработки текста.

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

Как было сказано ранее, выполнение одной команды требует затрат. Огромная стоимость, если эта команда не builtin, но даже если они builtin, стоимость большая.

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

Кроме того, оболочки выполняют команды в отдельных процессах. Эти строительные блоки не имеют общей памяти или состояния. Когда вы делаете fgets () или fputs () в C, это функция в stdio. stdio хранит внутренние буферы для ввода и вывода для всех функций stdio, чтобы избежать слишком частого выполнения дорогостоящих системных вызовов.

Соответствующие утилиты оболочки ( читать , эхо , printf ) не могут этого сделать. читать предназначено для чтения одной строки. Если он считывается после символа новой строки, это означает, что следующая команда будет пропущена. Таким образом, чтение должно считывать входные данные по одному байту за раз (некоторые реализации имеют оптимизацию, если входные данные являются обычным файлом в том, что они считывают порции и ищут назад, но это работает только для обычных файлов и bash , например, только считывает порции 128 байтов, что все еще намного меньше, чем это делают текстовые утилиты).

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

Очевидно, что выполнение команд последовательно означает, что вы должны ждать их, это небольшой танец планировщика, который дает управление от оболочки, к инструментам и обратно. Это также означает (в отличие от использования длинных экземпляров инструментов в конвейере), что нельзя использовать несколько процессоров одновременно, когда они доступны.

Между при чтении цикла и (предположительно) эквивалентным вырезом -c3 < файл в моем быстром тесте коэффициент времени ЦП составляет около 40000 в моих тестах (одна секунда против половины дня). Но даже если вы используете только сборки оболочки:

while read line; do
  echo ${line:2:1}
done

(здесь с bash ), это все равно около 1:600 (одна секунда против 10 минут).

Надежность/разборчивость

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

read - это удобный инструмент, который может делать много разных вещей. Он может считывать входные данные от пользователя, разделять их на слова для хранения в различных переменных. строка считывания делает не считывает строку ввода, или, возможно, считывает строку очень особым образом. Фактически из входных данных считываются слова , разделенные $ IFS , где обратная косая черта может использоваться для удаления разделителей или символа новой строки.

При значении по умолчанию $ IFS на входе, например,

   foo\/bar \
baz
biz

строка чтения сохранит «foo/bar baz» в $ line , а не «foo\/bar\» , как ожидалось.

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

IFS= read -r line

Это не очень интуитивно, но так оно и есть, помните, раковины не предназначались для такого использования.

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

И конечно, есть типичное забывание цитировать вашу переменную , в которую все попадают. Так что это больше:

while IFS= read -r line; do
  printf '%s\n' "$line" | cut -c3
done < file

Теперь, еще несколько предостережений:

  • за исключением zsh , что не работает, если ввод содержит символы NUL, в то время как по крайней мере GNU текстовые утилиты не будут иметь проблемы.
  • Если после последней новой строки есть данные, они будут пропущены
  • внутри цикла, stdin перенаправляется, так что вы должны обратить внимание, что команды в нем не считываются из stdin.
  • для команд в контурах, мы не обращаем внимания на то, успешно ли они или нет. Обычно, ошибки (диск заполнен, ошибки чтения...) условия будут плохо обрабатываться, как правило, более плохо, чем с правильный эквивалент.

Если мы хотим решить некоторые из этих проблем выше, это становится:

while IFS= read -r line <&3; do
  {
    printf '%s\n' "$line" | cut -c3 || exit
  } 3<&-
done 3< file
if [ -n "$line" ]; then
    printf '%s' "$line" | cut -c3 || exit
fi

Это становится все менее разборчивым.

Существует ряд других проблем с передачей данных командам через аргументы или извлечением их выходных данных в переменных:

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

Соображения безопасности

Когда вы начинаете работать с переменными оболочки и аргументами команд , вы вводите минное поле.

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

При необходимости использования циклов.

TBD

263
27.01.2020, 19:27

До концептуальный и четкость идут, оболочки обычно интересуются файлами. Их "адресуемая единица" является файлом, и "адрес" является именем файла. Оболочки имеют все виды методов тестирования на существование файла, тип файла, форматирование имени файла (начинающийся globbing). Оболочки имеют очень немного примитивов для контакта с содержанием файла. Программисты Shell должны вызвать другую программу для контакта с содержанием файла.

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

43
27.01.2020, 19:27

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

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

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

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

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

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

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

я также попытался бы придерживаться стандартных инструментов, которые доступны в большинстве систем и пытаются сохранить мое использование портативным, хотя это не всегда возможно. И если вашим любимым языком будет Python или Ruby, то возможно, вы не будете возражать против дополнительного усилия по проверке, что это установлено на каждой платформе, ваше программное обеспечение должно работать на инструментах :-)

Simple, включают голова , хвост , grep, , вид , сократил , tr, sed, соединение (при слиянии 2 файлов), и awk остроты, среди многих других. Удивительно, что некоторые люди могут сделать с сопоставлением с образцом и команды sed.

, Когда это становится более сложным, и действительно необходимо применить некоторую логику к каждой строке, , awk является хорошим вариантом - любой острота (некоторые люди помещают целые awk сценарии в 'одну строку', хотя это не очень читаемо), или в коротком внешнем сценарии.

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

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

И наконец, существует старый добрый C, если вам нужно максимальная скорость и высокая гибкость (хотя обработка текста немного утомительна). Но это - вероятно, очень плохое использование вашего времени для записи новой программы C для каждой различной обрабатывающей файл задачи, с которой вы сталкиваетесь. Я работаю с файлами CSV много, таким образом, я записал несколько универсальных утилит в C, который я могу снова использовать во многих различных проектах. В действительности это разворачивает диапазон 'простого,быстрые инструменты Unix, которые я могу назвать из своих сценариев оболочки, таким образом, я могу обработать большинство проектов только пишущий сценарии, который намного быстрее, чем запись и сделанный на заказ C отладки, кодируют каждый раз!

Некоторые заключительные подсказки:

  • не забывают запускать ваш основной сценарий оболочки с LANG=C экспорта, или много инструментов будут рассматривать ваши файлы простого ASCII, поскольку Unicode, делая их намного намного медленнее
  • также рассматривает настройку LC_ALL=C экспорта, если вы хотите вид произвести последовательное упорядочивание, независимо от среды!
  • , если вам нужно к вид ваши данные, которые, вероятно, займут больше времени (и ресурсы: ЦП, память, диск), чем все остальное так попытка минимизировать количество вид команды и размер файлов, которые они сортируют
  • единственный конвейер, если это возможно, является обычно самым эффективным - выполняющий несколько конвейеров в последовательности, с промежуточными файлами, может быть более читаемым и способным отладкой, но увеличит время, когда ваша программа берет
26
27.01.2020, 19:27

Да, но ...

правильный ответ Стефана Шазеласа основан на концепции делегирования каждой текстовой операции определенным двоичным файлам, таким как grep , awk , sed и другие.

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

Образец см. В этом сообщении:

https://stackoverflow.com/a/38790442/1765658

и

https://stackoverflow.com/a/7180078/1765658

проверить и сравнить ...

Конечно

Нет никакого внимания к пользовательскому вводу и безопасности !

Не пишите веб-приложение под !!

Но для множества задач администрирования сервера, где может использоваться вместо , использование встроенных команд bash может быть очень эффективным.

Я имею в виду:

Инструменты для написания, такие как bin utils , - это не тот же вид работы, что и системное администрирование.

Так не те же люди!

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

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

15
27.01.2020, 19:27

Теги

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