Должен ли я заботиться о ненужных кошках?

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

Сравнение

sed s/bla/blaha/ data \
| grep blah \
| grep -n babla

и

cat data \
| sed s/bla/blaha/ \
| grep blah \
| grep -n babla

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

49 голосов | спросил user4518 8 J000000Friday11 2011, 17:33:51

4 ответа


45

«Окончательный» ответ, конечно, представлен вам Бесполезное использование cat Награда .

  

Цель cat - конкатенация (или «catenate») файлов. Если это только один файл, объединение его ни с чем не происходит, это пустая трата времени и стоит вам процесса.

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

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

Вы даже можете стать творческим, ГДЕ вы его пишете. Обычно он будет помещен в конце команды, прежде чем указывать любые перенаправления или каналы вывода следующим образом:

sed s/blah/blaha/ < data | pipe

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

< data \
    sed s/bla/blaha/ |
    grep blah |
    grep -n babla

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

function fix_blahs () {
    sed s/bla/blaha/ |
    grep blah |
    grep -n babla
}

fix_blahs < data

Затем вы можете продолжить с помощью fix_blahs < data | fix_frogs | reorder | format_for_sql. Шифра, которая читается так, очень проста, и отдельные компоненты могут легко отлаживаться в соответствующих функциях.

ответил Caleb 8 J000000Friday11 2011, 17:35:55
20

Вот краткое изложение некоторых из недостатков:

cat $file | cmd

над

< $file cmd
  • Во-первых, примечание: есть (намеренно для целей обсуждения) отсутствующие двойные кавычки вокруг $file выше. В случае cat это всегда проблема, кроме zsh; в случае перенаправления это только проблема для bash или ksh88, а для некоторых других оболочек - только при интерактивном (не в скриптах).
  • Наиболее часто упоминаемый недостаток - это дополнительный процесс, порождаемый. Обратите внимание, что если cmd встроен, это даже 2 процесса в некоторых оболочках, таких как bash.
  • Все еще на фронте производительности, за исключением оболочек, в которых встроен cat, который также выполняет дополнительную команду (и, конечно же, загружен и инициализирован (и библиотеки, к которым она также связана)) .
  • По-прежнему на фронте производительности для больших файлов это означает, что системе придется поочередно планировать процессы cat и cmd и постоянно заполнять и очищать буфер буфера , Даже если cmd выполняет системный вызов 1GB больших read() за один раз, управление должно идти туда и обратно между cat и cmd, потому что канал не может хранить больше нескольких килобайт данных за раз.
  • Некоторые cmd s (например, wc -c) могут делать некоторые оптимизации, когда их stdin является обычным файлом, который они не могут сделать с помощью cat | cmd, так как их stdin - это просто труба. С помощью cat и канала, это также означает, что они не могут seek() внутри файла. Для таких команд, как tac или tail, это имеет огромное значение в производительности, так как это означает, что с помощью cat им нужно хранить весь ввод в памяти .
  • cat $file и даже более корректная версия cat -- "$file" не будут работать должным образом для некоторых определенных имен файлов, таких как - (или --help или что-нибудь, начиная с -), если вы забудете --). Если вы настаиваете на использовании cat, он должен, вероятно, использовать cat < "$file" | cmd вместо этого для надежности.
  • Если $file не может быть открыт для чтения (доступ запрещен, не существует ...), < "$file" cmd будет сообщать о непротиворечивой ошибке (оболочкой) и not запустить cmd, а cat $file | cmd все равно будет работать cmd, но с его stdin выглядит как пустой файл. Это также означает, что в таких вещах, как < file cmd > file2, file2 не сбрасывается, если file не может быть открыт.
ответил Stéphane Chazelas 26 PM00000030000004431 2015, 15:36:44
13

Ввод <file в конец конвейера менее читабельен, чем наличие cat file в начале. Натуральный английский читается слева направо.

Полагая <file, начало конвейера также менее читаемо, чем кошка, я бы сказал. Слово более читаемо, чем символ, особенно символ, который, кажется, указывает неверный путь.

Использование cat сохраняет команду command | command | command.

ответил Jim 24 FebruaryEurope/MoscowbSun, 24 Feb 2013 02:58:21 +0400000000amSun, 24 Feb 2013 02:58:21 +040013 2013, 02:58:21
7

Одна вещь, на которую другие ответы здесь, по-видимому, прямо не затрагивает, заключается в том, что использование cat, как это, не является «бесполезным» в том смысле, что «происходит посторонний процесс кошки, нет работы"; это бесполезно в том смысле, что «происходит процесс кошки, который делает только ненужную работу».

В случае этих двух:

sed 's/foo/bar/' somefile
<somefile sed 's/foo/bar/'

оболочка запускает sed-процесс, который считывает из somefile или stdin (соответственно), а затем выполняет некоторую обработку - он считывает до тех пор, пока он не попадет в новую строку, заменяет первое «foo» (если есть) в этой строке с «bar» , затем печатает эту строку в stdout и циклах.

В случае:

cat somefile | sed 's/foo/bar/'

Оболочка порождает кошачий процесс и процесс sed и прокладывает stdout кота на stdin sed. Кошачий процесс читает несколько килобайт или, возможно, мегабайтный фрагмент из файла, а затем записывает его на его stdout, где sed sommand поднимается оттуда, как и во втором примере выше. В то время как sed обрабатывает этот кусок, кошка читает еще один кусок и записывает его в свой stdout для sed, чтобы работать дальше.

Другими словами, дополнительная работа, связанная с добавлением команды cat, - это не просто дополнительная работа по созданию дополнительного процесса cat, это также дополнительная работа чтение и запись байтов файла дважды, а не один раз. Теперь, практически говоря, и на современных системах, это не имеет большого значения - это может заставить вашу систему сделать несколько микросекунд ненужной работы. Но если для сценария, который вы планируете распространять, потенциально для людей, использующих его на компьютерах, которые уже недостаточно, несколько микросекунд могут складываться по множеству итераций.
ответил godlygeek 30 J0000006Europe/Moscow 2014, 20:24:33

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132