Почему некоторые оболочки `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 для разделения полей внутри файла. Кажется, что чтение не в состоянии справиться с этим.

  • разница между "function foo () {}" и "foo () {}"
  • Возможно ли выполнить некоторые команды в подоболочке, не выходя сразу после этого?
  • cd в каталог символически связанного файла
  • Почему читать -r есть текст?
  • bash скрипт переименовывает входной файл при копировании
  • du skip символические ссылки
  • Заменить символы в строке соответствия
  • Выполнение сценария оболочки на нескольких серверах
  • Что задает переменную окружения $ SHELL?
  • Что такое отображение эха $ -1?
  • Почему переменные PATH отличаются при запуске через sudo и su?
  • Interesting Posts

    при установке ошибки centos заключается в том, что 'sda должен иметь метку gpt'

    Как сортировать с разделителем длиной несколько символов?

    вопрос CMake на RedHat Linux 64

    Почему iptables отбрасывает / регистрирует, даже если соответствующий пакет должен быть принят?

    Linux Mint 17 Cinnamin + MySQL + выключить автозапуск

    Может ли 0-й физический ядро ​​использовать асимметрично в linux?

    Bash: путь или ссылка на строку в файле?

    Нельзя использовать fatsort как непривилегированный пользователь больше

    Скрипт для Ping IP и создание журнала, показывающего дату / время любого сбоя, раз более 500 мс, а traceroute – отказ

    Nmtui как пользователь без полномочий пользователя Centos 7.1

    biosdevname дает мне «p120p1» вместо p1p1 – это правильно?

    sed – как исключить несколько шаблонов в матче

    Как установить размер mate-панели через командную строку (а не через dconf)?

    Ограничить резервное копирование без пароля с помощью SFTP

    sudo: извините, вам не разрешено устанавливать следующие переменные среды

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