Почему оболочка автоматически не исправляет «бесполезное использование кота»? [закрыто]

Si su administrador de archivos muestra el dispositivo, ya está montado. Intente desmontar el dispositivo a través del administrador de archivos y vuelva a ejecutar jmtpfs /media/mobile.

28
11.04.2019, 10:25
8 ответов

2 команды не эквивалентны :учитывать обработку ошибок:

cat <file that doesn't exist> | lessсоздаст пустой поток, который будет передан в конвейерную программу... таким образом, вы получите дисплей, на котором ничего не отображается.

< <file that doesn't exist> lessне сможет открыть бар, а затем вообще не откроет меньше.

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

27
27.01.2020, 19:38

«Бесполезное использование cat» больше относится к тому, как вы пишете свой код, чем к тому, что на самом деле запускается при выполнении сценария. Это своего рода дизайн против -шаблона , способ сделать то, что, вероятно, можно было бы сделать более эффективным образом. Это провал в понимании того, как лучше всего комбинировать данные инструменты для создания нового инструмента. Я бы сказал, что объединение нескольких команд sedи/или awkвместе в конвейер также иногда можно назвать признаком того же анти--паттерна.

Исправление экземпляров «бесполезного использования cat» в скрипте — это в первую очередь вопрос исправления исходного кода скрипта вручную. В этом может помочь такой инструмент, как ShellCheck , указывающий на очевидные случаи:

$ cat script.sh
#!/bin/sh
cat file | cat
$ shellcheck script.sh

In script.sh line 2:
cat file | cat
    ^-- SC2002: Useless cat. Consider 'cmd < file |..' or 'cmd file |..' instead.

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

Оболочка не обязательно знает cat, что такое. Потенциально это может быть любая команда из любого места в вашем $PATHили функция.

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

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

Этот вопрос похож (в очень общем смысле )на " Существуют ли какие-либо компиляторы, которые пытаются исправить синтаксические ошибки самостоятельно? " (на сайте Software Engineering StackExchange ), хотя этот вопрос, очевидно, касается синтаксических ошибок, а не бесполезных шаблонов проектирования. Однако идея автоматического изменения кода в зависимости от намерения во многом такая же.

53
27.01.2020, 19:38

Потому что это не бесполезно.

В случае cat file | cmdfd0(stdin )из cmdбудет каналом, а в случае cmd <fileэто может быть обычный файл, устройство и т. д.

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

  • обычный файл нельзя select(2)редактировать или poll(2)редактировать осмысленным образом; a select(2)на нем всегда будет возвращать «готово». Расширенные интерфейсы, такие как epoll(2)в Linux, просто не будут работать с обычными файлами.

  • в Linux есть системные вызовы (splice(2), vmsplice(2), tee(2)), которые работают только с каналами [1]

Поскольку catтак часто используется, его можно было бы реализовать как встроенную оболочку -, в которой не будет дополнительного процесса, но как только вы начнете этот путь, то же самое можно будет сделать с большинством команд --. ] превращая оболочку в более медленную и громоздкую perlили python. возможно, лучше написать другой язык сценариев с простым в использовании синтаксисом -типа конвейера для продолжений ;-)

[1] Если вам нужен простой пример, не придуманный по случаю, вы можете посмотреть мой "exec binary from stdin" git gist с некоторыми пояснениями в комментарии здесь . Реализация catвнутри него, чтобы заставить его работать без UUoC, увеличила бы его в 2 или 3 раза.

36
27.01.2020, 19:38

Добавление к ответу @Kusalananda (и комментарию @alephzero ), кошка может быть чем угодно:

alias cat='gcc -c'
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

или

echo 'echo 1' > /usr/bin/cat
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

Нет никакой причины, по которой cat (сам по себе )или /usr/bin/cat в системе на самом деле является cat средством объединения.

2
27.01.2020, 19:38

Команда catможет принимать -в качестве маркера для stdin .(POSIX , " Если файл ' -', утилита cat должна прочитать стандартный ввод в этой точке последовательности. " )Это позволяет легко обрабатывать файл или стандартный ввод , где в противном случае это было бы запрещено.

Рассмотрим эти две тривиальные альтернативы, где аргумент оболочки $1равен-:

cat "$1" | nl    # Works completely transparently
nl < "$1"        # Fails with 'bash: -: No such file or directory'

Другой случай catполезен, когда он преднамеренно используется как операция no -просто для поддержания синтаксиса оболочки:

file="$1"
reader=cat
[[ $file =~ \.gz$ ]] && reader=zcat
[[ $file =~ \.bz2$ ]] && reader=bzcat
"$reader" "$file"

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

cat file.txt

В любой другой ситуации могут потребоваться операции самого cat.

11
27.01.2020, 19:38

Потому что обнаружить бесполезного кота действительно очень сложно.

У меня был сценарий оболочки, в котором я написал

cat | (somecommand <<!
...
/proc/self/fd/3
...
!) 0<&3

Сценарий оболочки не работал в рабочей среде, если catбыл удален из-за того, что он был вызван через su -c 'script.sh' someuser. Явно лишний catзаставил владельца стандартного ввода измениться на пользователя, от имени которого выполнялся скрипт, так что его повторное открытие через /procсработало.

17
27.01.2020, 19:38

tl;dr:Оболочки не делают этого автоматически, потому что затраты превышают вероятные выгоды.

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

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

Затем вы должны рассмотреть затраты и преимущества каждого подхода. Преимущества достаточно просты:

  1. В любом случае избегайте exec (изcat)
  2. Во втором случае, когда возможна перенаправленная замена, избегание форка.
  3. В тех случаях, когда вам приходится использовать конвейер, может быть иногда возможно избежать fork/vfork, но часто нет. Это связано с тем, что эквивалент cat -должен запускаться одновременно с остальной частью конвейера.

Таким образом, вы сэкономите немного процессорного времени и памяти, особенно если сможете избежать форка. Конечно, вы экономите это время и память только тогда, когда функция действительно используется. И вы действительно экономите только время fork/exec; с большими файлами время в основном является временем ввода-вывода (, т. е. чтением файла с диска ). Таким образом, вы должны спросить :, как часто catиспользуется (бесполезно )в сценариях оболочки, где производительность действительно имеет значение? Сравните его с другими распространенными встроенными командами оболочки, такими как test— трудно представить, что catиспользуется (бесполезно )даже в десять раз реже, чем testиспользуется в важных местах. Это предположение, которое я не измерял, и это то, что вы хотели бы сделать перед любой попыткой реализации. (Или аналогичным образом попросить кого-то еще реализовать, например, запрос функции.)

Затем вы спрашиваете :, каковы затраты. Две затраты, которые приходят на ум, это ()дополнительный код в оболочке, который увеличивает его размер (и, следовательно, возможно использование памяти ), требует дополнительных работ по обслуживанию, является еще одним местом для ошибок и т. д. ; и (b )обратная совместимость удивляет, POSIX catопускает многие функции, например, GNU coreutils cat, так что вы должны быть осторожны, что именно будет реализовывать встроенный cat.

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

  2. Что касается анализа конвейера, я не думаю, что в настоящее время оболочки делают что-то подобное (некоторые распознают конец конвейера и могут избежать разветвления ). По сути, вы добавляете в оболочку (примитивный )оптимизатор; оптимизаторы часто оказываются сложным кодом и источником множества ошибок.И эти ошибки могут быть удивительными — небольшие изменения в сценарии оболочки могут привести к тому, что ошибка будет устранена или спровоцирована.

Постскриптум:Вы можете применить аналогичный анализ к вашему бесполезному использованию кота. Преимущества :легче читать (хотя, если command1 будет принимать файл в качестве аргумента, вероятно, нет ). Стоит :лишнее fork и exec (и если command1 может принимать файл в качестве аргумента, вероятно больше запутанных сообщений об ошибках ). Если ваш анализ говорит вам бесполезно использовать кошку, тогда вперед.

13
27.01.2020, 19:38

Помимо всего прочего, проверкаcat-добавит дополнительную нагрузку на производительность и создаст путаницу в отношении того, какое использование catна самом деле бесполезно, ИМХО, потому что такие проверки могут быть неэффективными и создавать проблемы с законным catиспользованием.

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

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

(find /proc -type l | xargs ls -l | fgrep 'pipe:[20043922]') 2>/dev/null

, как показано в связанном посте. Это еще 3 команды (, так что дополнительные fork()s и exec()s )и рекурсивные обходы (так много readdir()вызовов ).

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

# adding header and footer to file
( cmd; cat file; cmd ) | cmd
# tr command does not accept files as arguments
cat log1 log2 log3 | tr '[:upper:]' '[:lower:]'

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

0
27.01.2020, 19:38

Теги

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