39

Когда я передаю несколько Unix-команд, таких как grep, sed, tr и т.д., Я склонен указывать входной файл, который обрабатывается с помощью cat. Так что-то вроде cat file | grep ... | awk ... | sed ...

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

Я посмотрел на проблему и наткнулся на статью Википедии о UUOC и награде «Бесполезное использование кошки», и мне кажется, что приведенные аргументы с точки зрения эффективности.

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

Я предполагаю, что лагерь UUOC предлагает использовать cmd1 args < file | cmd2 args | cmd3 .. или если команда имеет опцию для чтения из файла, то передать файл в качестве аргумента.

Но мне cat file | cmd1 ... | cmd2 кажется намного легче читать и понимать. Мне не нужно запоминать разные способы отправки входных файлов для разных команд, и процесс логически протекает слева направо. Сначала ввод, потом первый процесс ... и так далее.

Я не понимаю, какие аргументы приводятся в отношении бесполезного использования кошки? Я понимаю, что если я запускаю задание cron, которое запускается каждые 2 секунды и выполняет много операций, то в этом случае cat может быть расточительным. Но в противном случае, каково общее согласие относительно использования кошки?

8 ответов8

20

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

Но cat намного мощнее, чем просто cat somefile . Проконсультируйтесь с man cat или прочитайте, что я написал в этом ответе . Но если вам абсолютно положительно нужно только содержимое одного файла, вы можете получить некоторое преимущество в производительности, не используя cat для получения содержимого файла.

Что касается читабельности, это зависит от ваших личных вкусов. Я люблю cat ИНГ файлов в другие команды по той же причине, особенно если аспекты производительности являются незначительными.

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

14

В повседневном использовании командной строки это не сильно отличается. Особенно вы не заметите никакой разницы в скорости, так как время на ЦП, которое можно избежать, не используя cat , ваш ЦП просто будет простаивать. Даже если вы просматриваете сотни или тысячи (или даже сотни тысяч) элементов во всей практичности, это не будет иметь большого значения, если вы не находитесь в очень загруженной системе (Load Average / N CPU> 1).

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

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

Речь идет не о сохранении одного дескриптора файла, 17 КБ ОЗУ и 0,004 секунды процессорного времени. Речь идет о всей философии использования UNIX. "Сила левого поворота" на моей иллюстрации - это не просто перенаправление ввода, это философия UNIX. Полное погружение в это сделает вас лучше, чем окружающие, и вы заслужите уважение тех, кто понимает.

11

Я думаю, что позиция некоторых из тех, кто комментирует что-то, что является UUOC, заключается в том, что если человек действительно понимает синтаксис Unix и оболочки, он не будет использовать cat в этом контексте. Это похоже на использование плохой грамматики: я могу написать предложение, используя плохую грамматику, и все же донести свою мысль, но я также демонстрирую свое плохое понимание языка и, соответственно, мое плохое образование. Поэтому говорить, что что-то является UUOC, - это еще один способ сказать, что кто-то не понимает, что он делает.

Что касается эффективности, то, если вы выполняете конвейер из командной строки, машине требуется меньше времени для выполнения cat somefile | чем думать о том, может быть более эффективно использовать < somefile . Это просто не имеет значения.

10

Я часто использую cat file | myprogram в примерах. Иногда меня обвиняют в бесполезном использовании кошки (http://www.iki.fi/era/unix/award.html). Я не согласен по следующим причинам:

  • Легко понять, что происходит.

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

    cat foo | program1 -o option -b option | program2
    

    легче читать, чем

    program1 -o option -b option < foo | program2
    

    Если вы переместите перенаправление в начало, вы вводите в заблуждение людей, которые не привыкли к этому синтаксису:

    < foo program1 -o option -b option | program2
    

    и примеры должны быть легкими для понимания.

  • Это легко изменить.

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

  • Подчеркивается, что программа не даст сбой, если STDIN не является файлом.

    Не безопасно предполагать, что если работает program1 < foo то cat foo | program1 также будет работать. Тем не менее, на практике можно предположить обратное. Эта программа работает, если STDIN является файлом, но завершается неудачей, если вход является каналом, потому что она использует поиск:

    # works
    < foo perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
    
    # fails
    cat foo | perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
    

Я посмотрел на снижение производительности на http://oletange.blogspot.dk/2013/10/useless-use-of-cat.html Вывод: не используйте cat file | если сложность обработки похожа на простой grep, а производительность важнее, чем удобочитаемость. Для других ситуаций cat file | Это хорошо.

3

Я не знал о награде до сегодняшнего дня, когда какой-то новобранец пытался прикрепить UUOC на меня для одного из моих ответов. Это был cat file.txt | grep foo | cut ... | cut ... Я дал ему часть своего разума, и только после этого посетил ссылку, которую он дал мне, ссылаясь на происхождение награды и практику этого. Дальнейшие поиски привели меня к этому вопросу. К сожалению, несмотря на сознательное рассмотрение, ни один из ответов не содержал моего обоснования.

Я не хотел защищаться, обучая его. В конце концов, в молодости я написал бы команду как grep foo file.txt | cut ... | cut ... потому что всякий раз, когда вы выполняете частые одиночные grep вы изучаете размещение аргумента файла, и вы уже знаете, что первым является шаблон, а последующими - имена файлов.

Это был осознанный выбор, когда я ответил на вопрос с префиксом « cat частично из-за причины "хорошего вкуса" (по словам Линуса Торвальдса), но главным образом из-за убедительной причины функционирования.

Последняя причина более важна, поэтому я изложу ее в первую очередь. Когда я предлагаю трубопровод в качестве решения, я ожидаю, что его можно будет использовать повторно. Вполне вероятно, что конвейер будет добавлен в конце или объединен в другой конвейер. В этом случае имея файл аргумент Grep винты до повторного, и вполне возможно , сделать это тихо и без сообщения об ошибке , если файл существует аргумент. И. е. grep foo xyz | grep bar xyz | wc , сколько строк в xyz содержит bar а вы ожидаете количество строк, которые содержат как foo и bar . Необходимость изменить аргументы команды в конвейере перед ее использованием подвержена ошибкам. Добавьте к этому возможность молчаливых неудач, и это становится особенно коварной практикой.

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

Тем не менее, я постараюсь также осознать прежнюю причину "хорошего вкуса", которую я упомянул. Эта причина связана с ортогональным духом дизайна Unix. grep не cut и ls не grep . Поэтому, по крайней мере, grep foo file1 file2 file3 идет вразрез с духом дизайна. Ортогональный способ сделать это - cat file1 file2 file3 | grep foo Теперь, grep foo file1 - это просто особый случай grep foo file1 file2 file3 , и если вы не относитесь к нему одинаково, вы, по крайней мере, используете мозговые циклы, пытаясь избежать бесполезного вознаграждения кошек.

Это приводит нас к аргументу о том, что grep foo file1 file2 file3 объединяется, а cat объединяется, поэтому он подходит для cat file1 file2 file3 а потому что cat не объединяет в cat file1 | grep foo поэтому мы нарушаем дух и cat и всемогущего Unix. Что ж, если бы это было так, тогда Unix потребовалась бы другая команда, чтобы прочитать вывод одного файла и выложить его на стандартный вывод (не разбивать его на страницы или что-то еще, просто наплевать на стандартный вывод). Таким образом, у вас может возникнуть ситуация, когда вы говорите « cat file1 file2 или « dog file1 и добросовестно не забываете избегать « cat file1 чтобы избежать получения награды, и в то же время избегать « dog file1 file2 так как, надеюсь, дизайн « dog выдаст ошибку, если несколько файлов будут указано.

Надеемся, что в этот момент вы сочувствуете разработчикам Unix за то, что вы не включили отдельную команду для разбивки файла на стандартный вывод, а также назвали cat для сцепления вместо того, чтобы давать ему другое имя. <edit> есть такая собака, несчастный < оператор. К сожалению, его размещение в конце трубопровода препятствует легкому составлению. Нет синтаксически или эстетически чистого способа разместить его в начале. К сожалению, недостаточно универсален, поэтому вы начинаете с собаки, а просто добавляете другое имя файла, если хотите, чтобы оно обрабатывалось после предыдущего. > с другой стороны , это не половина так плохо. У него почти идеальное расположение в конце. Как правило, это не многократно используемая часть конвейера, и, соответственно, он различается символически.)</edit>

Следующий вопрос: почему так важно иметь команды, которые просто выплевывают файл или объединяют несколько файлов в стандартный вывод без какой-либо дальнейшей обработки? Одна из причин - избегать использования каждой отдельной команды Unix, работающей со стандартным вводом, чтобы знать, как анализировать хотя бы один аргумент файла командной строки и использовать его в качестве ввода, если он существует. Вторая причина заключается в том, чтобы пользователям не приходилось запоминать: (а) куда идут аргументы имени файла; и (b) избежать ошибки тихого конвейера, как упомянуто выше.

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

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

1

Что было бы действительно хорошо, так это оболочка, которая поддерживает синтаксис, такой как:

< filename cmd | cmd2 cmd2arg1... | cmd3

В то же время, я думаю, что cat filename | realcmd1... является приемлемым, поскольку он поддерживает синтаксис, стандартизированный с начальными командами, которые требуют имя файла в качестве аргумента.

0

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

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

Я также добавлю этот комментарий, так как давний пользователь Linux и администратор / инженер ... (и нас много), это заставляет нас кровоточить, когда мы видим это. Зачем? Потому что он использует ресурсы в системах, над которыми мы жестко контролируем ресурсы. Команда cat и сам канал используют дополнительную память и файловые дескрипторы, которые совершенно бесполезны. Вы связали ресурсы, которые нужны моей системе бесплатно, и вы НИЧЕГО не получили, которые могут объяснить использование этих ресурсов. Это огромное нет нет.

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

Как домашний пользователь, вы можете учиться на моих советах и узнавать, как лучше делать что-либо, или вы можете выбрать ослепление "запахом" кошек, ваш выбор ... но знайте, что если вы открыто используете эту практику, вас будут звать на этой практике все время, и вам придется молча признать, что они правы, а вы упрямы, потому что это правда. :-)

0

В защиту бесполезного использования кота

(Несколько абзацев, чтобы помочь сбалансировать цунами ворчащих комментариев против этой практики)

Я слишком много лет использую bash как оболочку, так и в качестве языка сценариев для небольших сценариев (и иногда, к сожалению, для не очень маленьких). Давным-давно я узнал о "бесполезном использовании кошки" (UUoC). Я по-прежнему виновен в этом, по крайней мере, каждую неделю, но, честно говоря, я редко чувствую себя вынужденным избегать этого. Я полагаю, что использование cat vs < file больше касается вкуса, чем технических различий, и я написал этот ответ, чтобы защитить людей, плохо знакомых с Linux, которые разделяют мой вкус к cat от мысли, что в их пути есть что-то серьезно неправильное (и обратите внимание на несколько случаев, когда является). Как и Линус Торвальдс, я также считаю, что зачастую вкус важнее, чем умение. Это не значит, что мой вкус лучше, чем у вас, но это значит, что если что-то имеет плохой вкус, я не сделаю это, не приобретя чего-то достойного.

Уже очевидно, что, как и автор вопроса, я чувствую, что использование cat очень естественно при работе над REPL, например, bash, где я исследую проблему, постепенно создавая сложные команды. Вот очень типичный пример: у меня есть текстовый файл, и я не знаю о нем много. Я наберу cat file чтобы получить представление о содержимом. Если результат будет слишком большим, я нажму на стрелку вверх и в зависимости от обстоятельств добавлю | head или | grep foo или | what_ever расширяло мою предыдущую команду, добавляя этапы обработки. Этот способ постепенного перехода от простой команды к более сложной путем добавления одного шага обработки за другим мне кажется очень естественным (я делаю то же самое на ipython, и мне нравится, как pyfunctional и подобные инструменты программирования охватывают этот стиль), Поэтому, работая над оболочкой bash, я уверен, что прерывание моего потока для удаления cat более бесполезно, чем позволить ей быть и страдать ... ну, в 99,9% случаев это не имеет никакого значения.

Конечно, при написании сценариев все может измениться. Но даже когда я пишу сценарии, я считаю, что люди, которые насмехаются над UUoC, игнорируют важные уроки: «Преждевременная оптимизация - корень всего зла». И если вы не делаете что-то нетипичное, для UUoC действительно трудно быть местом, где потребуется оптимизация. Конечно, вам определенно нужно знать, что в этом неэффективно (это, кстати, дополнительный вызов процесса, поскольку об этом, похоже, мало кто упоминает). Обладая этими знаниями, если вам доведется работать в тех редких системах, где вызов процесса стоит дорого (например, некоторые встроенные системы или CygWin в меньшей степени), вы будете знать, что делать, если этого требует особая ситуация. Например, если вы обнаружите, что вызываете cat много раз в секунду в цикле (кстати, если вы все-таки оказались в этом положении, спросите себя, является ли bash подходящим инструментом для работы). Опять же, хотя: «сначала заставьте его работать правильно, а затем оптимизируйте его, если это необходимо».

И как вы объясните цунами жалоб на UUoC Ника?

Помимо того, что не у всех есть свой вкус, я считаю, что основная причина, по которой многие люди жалуются на UUoC, не техническая, а человеческая: большинство новичков Unix не знают о идиоме < file command поэтому более искушенному человеку играть в "Старый Гуру" им. У него также будет возможность использовать причудливые слова ("вызов процесса") и прикоснуться к любимому предмету "оптимизации". Хорошее впечатление гарантировано, поэтому очень трудно устоять. Тогда новички примут совет Гуру за чистую монету и в течение долгого времени передадут его другим как "Единственная Истина" (и проголосуйте за этот ответ :-). Забавное примечание: вероятно, настолько легко исправить bash, чтобы избежать неэффективности UUoC, что возникает вопрос, почему никто не добавил эту функцию или не сделал < filename cat файлом через столько лет. Темная душа предположила бы, что некоторые хакеры с седой бородой хотели бы оставить возможность насмехаться над нами ;-)

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .