Многие люди используют oneliners и скрипты, содержащие код вдоль строк
cat "$MYFILE" | command1 | command2 > "$OUTPUT"
Первый cat
часто называют «бесполезным использованием кошки», потому что технически это требует запуска нового процесса (часто /usr/bin/cat
), где этого можно избежать, если команда была
< "$MYFILE" command1 | command2 > "$OUTPUT"
потому что тогда нужно только запустить оболочку command1
и просто указать ее stdin
на данный файл.
Почему оболочка не выполняет это преобразование автоматически? Я чувствую, что синтаксис «бесполезного использования кота» легче читать, и оболочка должна иметь достаточно информации, чтобы автоматически избавиться от бесполезного кота. Это cat
определено в стандарте POSIX, поэтому оболочке следует разрешить реализовать его внутри, а не использовать двоичный файл в пути. Оболочка может даже содержать реализацию только для одной версии аргумента и возврата к двоичному в пути.
источник
lseek
по-прежнему является определенным поведением и может привести к другому результату, другое поведение блокировки может быть семантически значимым и т. Д. Было бы допустимо внести изменения, если бы вы знали, что были другие команды, и знали, что им все равно, или если вы просто не заботитесь о совместимости на этом уровне, но выгода довольно мала. Я предполагаю, что отсутствие выгоды ведет к ситуации больше, чем стоимость соответствия.cat
себя, правда, или любую другую утилиту. Также разрешено знать, как работают другие утилиты, принадлежащие системе (например, он может знать, как ведет себя внешняяgrep
реализация , поставляемая с системой ). Это вполне жизнеспособно, поэтому справедливо задаться вопросом, почему они этого не делают.grep
. Иsed
. Иawk
. Иdu
. А сколько сотен, если не тысяч других утилит?Ответы:
2 команды не эквивалентны: рассмотрим обработку ошибок:
cat <file that doesn't exist> | less
создаст пустой поток, который будет передан в конвейерную программу ... как таковой, вы получите отображение, ничего не показывающее.< <file that doesn't exist> less
не удастся открыть бар, а затем не открыть вообще.Попытка заменить первое на второе может сломать любое количество сценариев, которые ожидают запуска программы с потенциально пустым вводом.
источник
cat
всегда будет выполнять вторую команду в конвейере, тогда как вариант с простым перенаправлением ввода вообще не будет выполнять команду, если входной файл отсутствует.<"missing-file" grep foo | echo 2
не будет выполняться,grep
но будет выполнятьсяecho
.«Бесполезное использование
cat
» - это больше о том, как вы пишете свой код, чем о том, что на самом деле выполняется при выполнении скрипта. Это своего рода анти-шаблонный дизайн , способ сделать что-то, что, вероятно, можно сделать более эффективным способом. Это ошибка в понимании того, как наилучшим образом комбинировать данные инструменты для создания нового инструмента. Я бы сказал, что объединение несколькихsed
и / илиawk
команд в конвейер также иногда можно назвать признаком того же самого анти-паттерна.Исправление случаев «бесполезного использования
cat
» в скрипте - это, прежде всего, вопрос ручного исправления исходного кода скрипта. Инструмент, такой как ShellCheck, может помочь с этим, указав на очевидные случаи:Заставить оболочку делать это автоматически было бы сложно из-за природы сценариев оболочки. Способ выполнения сценария зависит от среды, унаследованной от его родительского процесса, и от конкретной реализации доступных внешних команд.
Оболочка не обязательно знает, что
cat
есть. Это может быть любая команда из любого места$PATH
или функция.Если бы это была встроенная команда (которая может быть в некоторых оболочках), она могла бы реорганизовать конвейер так, как она знала бы семантику своей встроенной
cat
команды. Прежде чем сделать это, он должен был бы дополнительно сделать предположения о следующей команде в конвейере, после оригинальнойcat
.Обратите внимание, что чтение из стандартного ввода ведет себя немного иначе, когда он подключен к каналу и когда он подключен к файлу. Канал не является доступным для поиска, поэтому в зависимости от того, что делает следующая команда в конвейере, он может или не может вести себя по-разному, если конвейер был переставлен (он может определить, является ли ввод доступным для поиска, и решить, что делать по-другому, если он есть или если это не так, в любом случае это будет вести себя иначе).
Этот вопрос похож (в очень общем смысле) на « Существуют ли какие-либо компиляторы, которые пытаются исправить синтаксические ошибки самостоятельно? » (На сайте Software Engineering StackExchange), хотя этот вопрос, очевидно, касается синтаксических ошибок, а не бесполезных шаблонов проектирования. , Идея автоматического изменения кода, основанного на намерениях, во многом совпадает.
источник
cat
есть, и другие команды в конвейере (правило «как будто») и ведут себя соответственно, их здесь просто нет, потому что это бессмысленно и слишком сложно.cat /dev/tty
это интересный, который будет отличаться<
.cat
самом деле делает команда, не выполнив ее . Все, что вы (и оболочка) знаете, у ОП есть наcat
своем пути команда, представляющая собой интерактивную имитацию кота, «myfile» - это просто сохраненное состояние игры,command1
иcommand2
они обрабатывают некоторую статистику о текущем игровом сеансе ...Потому что это не бесполезно.
В случае
cat file | cmd
, fd0
(stdin)cmd
будет труба, а в случаеcmd <file
это может быть обычный файл, устройство и т. Д.Канал имеет семантику, отличную от обычного файла, и его семантика не является подмножеством семантики обычного файла:
обычный файл нельзя
select(2)
редактировать илиpoll(2)
редактировать осмысленным образом;select(2)
на нем всегда будет возвращать «готов». Расширенные интерфейсы, такие какepoll(2)
в Linux, просто не будут работать с обычными файлами.на Linux есть системные вызовы (
splice(2)
,vmsplice(2)
,tee(2)
) , которые только работа на трубах [1]Поскольку
cat
он используется очень часто, его можно реализовать как встроенную оболочку, которая позволит избежать лишнего процесса, но как только вы начнете работать по этому пути, то же самое можно будет сделать с большинством команд - преобразовать оболочку в более медленную и более медленную.perl
илиpython
. вероятно, лучше написать другой язык сценариев с простым в использовании конвейерным синтаксисом для продолжения ;-)[1] Если вы хотите, чтобы простой пример не был скомпонован по этому случаю, вы можете взглянуть на мой git gist "exec binary from stdin" с некоторыми пояснениями в комментарии здесь . Реализация
cat
внутри него для того, чтобы он работал без UUoC, увеличил бы его в 2 или 3 раза.источник
cat
внутренне.cat /dev/urandom | cpu_bound_program
запускаетread()
системные вызовы в отдельном процессе. Например, в Linux фактическая работа ЦП по генерированию большего количества случайных чисел (когда пул пуст) выполняется в этом системном вызове, поэтому использование отдельного процесса позволяет использовать преимущество отдельного ядра ЦП для генерации случайных данных в качестве входных данных. Например, в разделе Какой самый быстрый способ создания текстового файла размером 1 ГБ, содержащего случайные цифры?lseek
что не будет работать.cat foo.mp4 | mpv -
будет работать, но вы не можете искать в обратном направлении дальше, чем кеш-буфер mpv или mplayer. Но с вводом, перенаправленным из файла, вы можете.cat | mpv -
это один из способов проверить, имеет ли MP4 свойmoov
атом в начале файла, поэтому его можно воспроизводить без поиска конца и назад (т. е. подходит ли он для потоковой передачи). Легко представить другие случаи, когда вы хотите протестировать программу на наличие файлов без возможности поиска, запустив ее/dev/stdin
сcat
перенаправлением.xargs cat | somecmd
. Если пути к файлам выходят за пределы ограничения буфера команд, ониxargs
могут запускатьсяcat
несколько раз, что приводит к непрерывному потоку, в то время какxargs somecmd
прямое использование часто приводит к сбою, потому чтоsomecmd
не может быть выполнено в несколько раз для достижения плавного результата.Потому что обнаружить бесполезного кота действительно очень сложно.
У меня был сценарий оболочки, где я написал
Сценарий оболочки завершился неудачно, если
cat
был удален, потому что он был вызван с помощьюsu -c 'script.sh' someuser
. По-видимому, излишняяcat
причина в том, что владелец стандартного ввода сменил пользователя, на котором выполнялся сценарий, чтобы снова открыть его через/proc
.источник
cat
следует ровно один параметр, поэтому оболочка должна использовать реальныйcat
исполняемый файл вместо оптимизированного ярлыка. Хороший вопрос о возможных других учетных данных или нестандартном стандартном вводе данных для реальных процессов.tl; dr: оболочки не делают этого автоматически, потому что затраты превышают вероятные выгоды.
Другие ответы указывают на техническое различие между stdin, являющимся каналом, и файлом. Учитывая это, оболочка может выполнить одно из следующих действий:
cat
как встроенную, сохраняя при этом различие между файлом и каналом. Это позволило бы сэкономить на затратах и, возможно, вилке.Далее вы должны рассмотреть затраты и преимущества каждого подхода. Преимущества достаточно просты:
cat
)Таким образом, вы экономите немного процессорного времени и памяти, особенно если вы можете избежать форка. Конечно, вы экономите это время и память только тогда, когда эта функция действительно используется. И вы только экономите время на форк / exec; для больших файлов это время ввода-вывода (например, cat читает файл с диска). Поэтому вы должны спросить: как часто
cat
(бесполезно) используется в сценариях оболочки, где производительность действительно имеет значение? Сравните это с другими обычными встроенными оболочками, такими какtest
- трудно представить, чтоcat
он используется (бесполезно) даже на десятую часть от того, чтоtest
используется в местах, которые имеют значение. Это предположение, я не измерил, это то, что вы хотели бы сделать перед любой попыткой реализации. (Или аналогичным образом, попросив кого-то еще реализовать, например, запрос функции).Далее вы спросите: каковы расходы. На ум приходят две затраты: (а) дополнительный код в оболочке, который увеличивает его размер (и, следовательно, возможно, использует память), требует больше работы по обслуживанию, является еще одним местом для ошибок и т. Д .; и (b) неожиданности обратной совместимости, POSIX
cat
пропускает множество функций, например, GNU coreutilscat
, поэтому вам нужно быть осторожным в точности, что быcat
встроенная программа реализовала.Дополнительная встроенная опция, вероятно, не так уж и плоха - добавление еще одной встроенной функции, в которой группа уже существует. Если у вас есть данные профилирования, показывающие, что это поможет, вы, вероятно, можете убедить авторов вашей любимой оболочки добавить их.
Что касается анализа конвейера, я не думаю, что оболочки делают что-то подобное в настоящее время (некоторые распознают конец конвейера и могут избежать разветвления). По сути, вы бы добавили (примитивный) оптимизатор в оболочку; оптимизаторы часто оказываются сложным кодом и источником множества ошибок. И эти ошибки могут вызывать удивление - небольшие изменения в сценарии оболочки могут привести к тому, что вы избежите или вызовете ошибку.
Постскриптум: Вы можете применить подобный анализ к вашим бесполезным использованиям кошек. Преимущества: легче читать (хотя, если command1 примет файл в качестве аргумента, вероятно, нет). Затраты: дополнительная ветвь и exec (и если команда1 может принять файл в качестве аргумента, возможно, более запутанные сообщения об ошибках). Если ваш анализ говорит вам бесполезно использовать кошку, тогда продолжайте.
источник
cat
Команда может принять в-
качестве маркеров для стандартного ввода . ( POSIX , « Если файл является '-', утилита cat должна читать из стандартного ввода в этой точке последовательности. ") Это позволяет легко обрабатывать файл или стандартный ввод, если в противном случае это было бы запрещено.Рассмотрим эти два тривиальных альтернативы, где аргумент оболочки
$1
является-
:Другое
cat
полезное время - это когда его намеренно используют в качестве запрета, просто чтобы поддерживать синтаксис оболочки:Наконец, я считаю, что единственное время, когда UUOC действительно может быть правильно вызван, - это когда
cat
используется имя файла, которое, как известно, является обычным файлом (т. Е. Не устройством или именованным каналом), и что командам не передаются флаги:В любой другой ситуации сами по
cat
себе могут потребоваться.источник
Команда cat может делать то, что оболочка не может делать (или, по крайней мере, не может делать легко). Например, предположим, что вы хотите напечатать символы, которые в противном случае могли бы быть невидимыми, такие как вкладки, возврат каретки или перевод строки. Может быть, * есть * способ сделать это только с помощью встроенных команд оболочки, но я не могу думать ни о чем из головы. GNU-версия cat может сделать это с
-A
аргументом или-v -E -T
аргументами (хотя я не знаю о других версиях cat). Вы также можете использовать префикс каждой строки с помощью номера строки-n
(опять же, IDK, если версии не-GNU могут это делать).Еще одним преимуществом кошки является то, что она может легко читать несколько файлов. Для этого можно просто напечатать
cat file1 file2 file3
. Чтобы сделать то же самое с оболочкой, все будет сложно, хотя тщательно продуманная петля, скорее всего, может достичь того же результата. Тем не менее, вы действительно хотите потратить время на написание такого цикла, когда существует такая простая альтернатива? Я не!Чтение файлов с помощью cat, вероятно, потребляет меньше ресурсов ЦП, чем оболочка, поскольку cat является предварительно скомпилированной программой (очевидное исключение составляет любая оболочка со встроенным cat). При чтении большой группы файлов это может стать очевидным, но я никогда не делал этого на своих машинах, поэтому не уверен.
Команда cat также может быть полезна для принуждения команды к принятию стандартного ввода в тех случаях, когда это не так. Учтите следующее:
echo 8 | sleep
Число «8» не будет принято командой «sleep», так как оно никогда не предназначалось для приема стандартного ввода. Таким образом, сон будет игнорировать этот ввод, жаловаться на отсутствие аргументов и выход. Однако, если один печатает:
echo 8 | sleep $(cat)
Многие оболочки расширят это до
sleep 8
, и сон будет ждать 8 секунд перед выходом. Вы также можете сделать что-то подобное с ssh:command | ssh 1.2.3.4 'cat >> example-file'
Эта команда с добавлением файла примера на машине с адресом 1.2.3.4 с любым выводом из «команды».
И это (вероятно) просто царапает поверхность. Я уверен, что мог бы найти больше примеров того, как кошка может быть полезна, если бы захотел, но этот пост достаточно длинный. Итак, в заключение я скажу следующее: попросить оболочку предвидеть все эти сценарии (и несколько других) не реально.
источник
Помните, что у пользователя может быть
cat
свой,$PATH
который не совсем POSIXcat
(но, возможно, какой-то вариант, который может что-то регистрировать). В этом случае вы не хотите, чтобы оболочка удаляла его.Они
PATH
могут динамически меняться, и тогдаcat
это не то, во что вы верите. Было бы довольно сложно написать оболочку для оптимизации, о которой вы мечтаете.Также на практике
cat
это довольно быстрая программа. Есть несколько практических причин (кроме эстетики), чтобы избежать этого.См. Также прекрасную речь об анализе POSIX [s] Яна Регина-Джианаса на FOSDEM2018. Это дает другие веские причины избегать попыток делать то, о чем вы мечтаете, в оболочке.
Если бы производительность действительно была проблемой для оболочек, кто-то предложил бы оболочку, которая использует сложные методы оптимизации всего компилятора программы, статический анализ исходного кода и методы компиляции точно в срок (все эти три области имеют десятилетия прогресса и научных публикаций и посвящены конференции, например, под SIGPLAN ). К сожалению, даже как интересная тема исследования, которая в настоящее время не финансируется исследовательскими агентствами или венчурными капиталистами, и я полагаю, что это просто не стоит усилий. Другими словами, вероятно, нет значительного рынка для оптимизации оболочек . Если у вас есть полмиллиона евро на такие исследования, вы легко найдете кого-то, кто будет этим заниматься, и я считаю, что это даст достойные результаты.
С практической стороны, переписывая, чтобы улучшить свою производительность, обычно делается небольшой (не сто строк) сценарий оболочки на любом лучшем языке сценариев (Python, AWK, Guile, ...). И нецелесообразно (по многим причинам разработки программного обеспечения) писать большие сценарии оболочки: когда вы пишете сценарий оболочки длиной более ста строк, вам нужно подумать о перезаписи его (даже для удобства чтения и обслуживания) на более подходящем языке : как язык программирования оболочка очень плохая. Однако существует много больших сгенерированных сценариев оболочки, и по веским причинам (например, сгенерированные
configure
сценарии GNU autoconf ).Что касается огромных текстовых файлов, передача их
cat
в качестве одного аргумента не является хорошей практикой, и большинство системных администраторов знают это (когда запуск любого сценария оболочки занимает больше минуты, вы начинаете рассматривать его оптимизацию). Для больших гигабайты файлов,cat
не является и не инструмент хорошо их обработать.источник
cat some-huge-log | tail -n 5
чтобы бежать (где онtail -n 5 some-huge-log
мог прыгнуть прямо до конца, тогда какcat
читает только спереди назад), не согласится.cat
большой текстовый файл размером в десятки ГБ (который был создан для тестирования) занимает довольно много времени. Не рекомендую.Если добавить ответ @Kusalananda (и комментарий @alephzero), cat может быть чем угодно:
или
Нет никаких причин, по которым cat (сам по себе) или / usr / bin / cat в системе на самом деле является инструментом объединения cat.
источник
cat
определяется POSIX и поэтому не должно сильно отличаться.PATH=/home/Joshua/bin:$PATH cat ...
Вы уверены, что знаете, чтоcat
делает сейчас?cat
их можно переопределить, но мы оба знаем, что их не следует произвольно заменять чем-то другим. Мой комментарий указывает на то, что POSIX требует определенного (подмножества) поведения, которое можно разумно ожидать. Иногда я писал сценарий оболочки, который расширяет поведение стандартной утилиты. В этом случае сценарий оболочки действовал и вел себя так же, как инструмент, который он заменил, за исключением того, что он имел дополнительные возможности./bin/cat
. (И вы бы сделали эту опцию, которую вы могли бы отключить.) Или вы бы сделалиcat
встроенную оболочку (которая может/bin/cat
использовать несколько аргументов?), Чтобы пользователи могли контролировать, хотят ли они внешнюю версию нормальной Кстати, сenable cat
. Как дляkill
. (Я думал, что bashcommand cat
будет работать, но это не пропускает встроенные функции)cat
в этой среде больше не ссылается на обычнуюcat
. Очевидно, что оптимизация должна быть реализована после обработки псевдонимов. Я считаю встроенные оболочки для представления команд в виртуальном каталоге, который всегда предшествует вашему пути. Если вы хотите избежать встроенной в оболочку версии какой-либо команды (напримерtest
), вы должны использовать вариант с путем.Два «бесполезных» использования для кошки:
... здесь
cat
используется для смешивания ввода файлов и каналов.... здесь
xargs
можно принимать практически бесконечное количество имен файлов и запускатьcat
столько раз, сколько необходимо, при этом все они ведут себя как один поток. Так что это работает для больших списков файлов, где прямое использованиеxargs sort
не.источник
cat
вызывается только с одним аргументом. Особенно в случае, когдаsh
передается строка иxargs
будет вызыватьсяcat
напрямую, оболочка не сможет использовать свою встроенную реализацию.Помимо прочего,
cat
-check добавит дополнительные потери производительности и путаницу относительно того, какое использование наcat
самом деле бесполезно, IMHO, потому что такие проверки могут быть неэффективными и создавать проблемы с законнымcat
использованием.Когда команды имеют дело со стандартными потоками, им нужно заботиться только о чтении / записи в стандартные файловые дескрипторы. Команды могут знать, является ли stdin доступным для поиска / доступным или нет, что указывает на канал или файл.
Если мы добавим к проверке, какой процесс на самом деле предоставляет содержимое стандартного ввода, нам нужно будет найти процесс на другой стороне канала и применить соответствующую оптимизацию. Это можно сделать с точки зрения самой оболочки, как показано в сообщении SuperUser Кайла Джонса, и с точки зрения оболочки, это
как показано в связанном посте. Это еще 3 команды (поэтому дополнительные
fork()
s иexec()
s) и рекурсивные обходы (так многоreaddir()
вызовов).С точки зрения C и исходного кода оболочки, оболочка уже знает дочерний процесс, поэтому нет необходимости в рекурсии, но как мы узнаем, когда оптимизировать, а когда
cat
на самом деле бесполезно? Есть на самом деле полезные использования кошки , такие какВероятно, было бы бесполезно и бесполезно добавлять такую оптимизацию в оболочку. Как уже упоминалось в ответе Кусаланды, UUOC больше относится к отсутствию у пользователя понимания того, как наилучшим образом комбинировать команды для достижения наилучших результатов.
источник