Почему некоторые оболочки `read` builtin не могут прочитать всю строку из файла в` / proc`?

В некоторых оболочках, подобных Bourne, встроенное read не может прочитать всю строку из файла в /proc (команда, приведенная ниже, должна быть запущена в zsh , заменить $=shell на $shell другими оболочками):

 $ for shell in bash dash ksh mksh yash zsh schily-sh heirloom-sh "busybox sh"; do printf '[%s]\n' "$shell" $=shell -c 'IFS= read x </proc/sys/fs/file-max; echo "$x"' done [bash] 602160 [dash] 6 [ksh] 602160 [mksh] 6 [yash] 6 [zsh] 6 [schily-sh] 602160 [heirloom-sh] 602160 [busybox sh] 6 

стандарт read требует, чтобы стандартный ввод был текстовым файлом , вызывает ли это требование разнообразное поведение?


Прочитайте определение текстового файла POSIX, я делаю некоторую проверку:

 $ od -ta </proc/sys/fs/file-max 0000000 6 0 2 1 6 0 nl 0000007 $ find /proc/sys/fs -type f -name 'file-max' /proc/sys/fs/file-max 

В содержании /proc/sys/fs/file-max нет символа NUL , а также find его как обычный файл (это ошибка в find ?).

Я предполагаю, что оболочка сделала что-то под капотом, например file :

 $ file /proc/sys/fs/file-max /proc/sys/fs/file-max: empty 

4 Solutions collect form web for “Почему некоторые оболочки `read` builtin не могут прочитать всю строку из файла в` / proc`?”

Проблема в том, что эти файлы /proc в Linux отображаются как текстовые файлы в отношении stat()/fstat() , но не ведут себя как таковые.

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

Утилита read должна читать содержимое файлов по одному байту за раз, чтобы не прочесть символ новой строки. Вот что делает dash :

  $ strace -fe read dash -c 'read a < /proc/sys/fs/file-max' read(0, "1", 1) = 1 read(0, "", 1) = 0 

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

 $ strace -e lseek,read bash -c 'read a' < /proc/sys/fs/file-max lseek(0, 0, SEEK_CUR) = 0 read(0, "1628689\n", 128) = 8 

При использовании bash вас все еще будут проблемы с файлами proc размером более 128 байтов и могут быть прочитаны только в одном системном вызове.

Кажется, что bash отключает эту оптимизацию, когда используется опция -d .

ksh93 делает оптимизацию еще больше, чтобы стать фиктивным. Чтение ksh93 выполняет поиск назад, но запоминает дополнительные данные, которые он прочитал для следующего read , поэтому следующее read (или любые другие его встроенные функции, которые читают данные, такие как cat или head ) даже не пытается read данные (даже если что данные были изменены другими командами между ними):

 $ seq 10 > a; ksh -c 'read a; echo test > a; read b; echo "$a $b"' < a 1 2 $ seq 10 > a; sh -c 'read a; echo test > a; read b; echo "$a $b"' < a 1 st 

Если вам интересно узнать почему? это так, вы можете увидеть ответ в источниках ядра здесь :

  if (!data || !table->maxlen || !*lenp || (*ppos && !write)) { *lenp = 0; return 0; } 

В принципе, поиск ( *ppos не 0) не реализован для чтения ( !write ) значений sysctl, которые являются числами. Всякий раз, когда чтение выполняется из /proc/sys/fs/file-max , подпрограмма __do_proc_doulongvec_minmax() вызывается из записи для file-max в таблице конфигурации в том же файле.

Другие записи, такие как /proc/sys/kernel/poweroff_cmd , реализуются через proc_dostring() что позволяет proc_dostring() , поэтому вы можете сделать dd bs=1 на нем и без проблем прочитать из своей оболочки.

Обратите внимание, что поскольку ядро ​​2.6 чтение большинства /proc было реализовано через новый API, называемый seq_file, и это поддерживает запросы, поэтому, например, чтение /proc/stat не должно вызывать проблем. В /proc/sys/ реализация, как мы видим, не использует этот api.

С первой попытки это выглядит как ошибка в оболочках, которые возвращают меньше реальной оболочки Bourne или ее производных (sh, bosh, ksh, реликвия).

Оригинальная Bourne Shell пытается прочитать блок (64 байта), новее вариантов Bourne Shell читает 128 байтов, но они начинают читать снова, если нет нового символа линии.

Предыстория: / procfs и аналогичные реализации (например, установленный виртуальный файл /etc/mtab ) имеют динамический контент, а вызов stat() не вызывает повторного создания динамического содержимого. По этой причине размер такого файла (от чтения до EOF) может отличаться от того, что возвращает stat() .

Учитывая, что стандарт POSIX требует, чтобы утилиты ожидали коротких чтений в любое время, программное обеспечение, которое считает, что read() который возвращает меньше, чем упорядоченное количество байтов, является индикацией EOF. Правильно реализованная утилита вызывает read() второй раз в случае, если она возвращает меньше ожидаемого – до тех пор, пока не будет возвращено значение 0. В случае read встроенного, конечно, было бы достаточно читать до EOF или до тех пор, пока не увидит NL .

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

В этом особом случае, похоже, это ошибка ядра Linux, см.

 $ sdd -debug bs=1 if= /proc/sys/fs/file-max Simple copy ... readbuf (3, 12AC000, 1) = 1 writebuf (1, 12AC000, 1) 8readbuf (3, 12AC000, 1) = 0 sdd: Read 1 records + 0 bytes (total of 1 bytes = 0.00k). sdd: Wrote 1 records + 0 bytes (total of 1 bytes = 0.00k). 

Ядро Linux возвращает 0 со вторым, и это, конечно, неверно.

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

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

  • Как определить ошибку в определенном файле при проверке контрольной суммы (какой файл имеет проблему при проверке) в сценарии оболочки
  • Как подсчитать вхождения каждого слова, принадлежащего файлу, во все количество файлов `n`, переданных в качестве аргументов?
  • Как применить одно и то же действие awk к различным файлам?
  • Команды командной строки терминала
  • Несколько файлов переименования
  • Очень медленное автозаполнение в Linux для Windows
  • Почему моя находка не рекурсивна?
  • Что делает выход в блоке if в сценарии оболочки?
  • Как выполнить xargs grep для вывода grep с пробелами?
  • Игнорировать специальные символы в сравнении с сценарием оболочки
  • Объединить некоторые файлы с разделителями табуляции
  • Interesting Posts

    Как запустить приложение при запуске в качестве другого пользователя?

    Как файл идентифицировал этот файл?

    Virtualbox: найдите имя DNS для хоста Windows в гостевой системе Debian

    Игнорировать или улавливать деление на ноль

    Как сделать паузу в сценарии оболочки?

    Как разрешить пользователю выполнять скрипт, принадлежащий root? Сетуид больше не работает

    Инструмент для добавления иерархической структуры для вывода, подобно дереву

    Что означает «стат» в этом предложении?

    использование cron для запуска скрипта

    Установка спящего раздела Windows NT NTFS на Linux Mint

    Перенос сервера CentOS

    Какой «правильный» формат для переменной среды HTTP_PROXY? Колпаки или колпачки?

    Почему nullglob влияет на завершение вкладок?

    Один из моих pdf-файлов на моем сервере apache можно получить, а другой не может, с теми же правами и одним и тем же каталогом

    Используя startx на Fedora 17, пользователь без полномочий root не может присоединиться к новым беспроводным сетям – как исправить?

    Linux и Unix - лучшая ОС в мире.