OS X, bash: меньше работает с дескрипторами открытых файлов, cat не работает

10

В скрипте bash, над которым я работаю (который должен работать в Ubuntu и OS X), мне нужно перенаправить вывод сотен команд в файл.
Вместо того, чтобы присоединиться &>...ко всем из них, я просто делаю

exec 9>&1
exec 5<>/tmp/some-file.txt
exec 1>&5

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

cat /dev/fd/5

или

tee </dev/fd/5

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

less /dev/fd/5 | tee

но это похоже на взлом.

Итак, почему, lessочевидно, можно увидеть вещи, которые catне могут на OS X? (Или все потомки BSD затронуты?)
Или я что-то не так делаю?

Siguza
источник

Ответы:

13

В OS X, как и во всех системах, где они поддерживаются, кроме Linux , открытие /dev/fd/xпохоже на выполнение a dup(x), результирующий fd более или менее указывает на то же описание открытого файла, что и на fd x, и, в частности, будет иметь такое же смещение внутри файла.

Linux здесь исключение. В Linux /dev/fd/xэто символическая ссылка /proc/self/fd/xи /proc/self/fd/xпсевдосимвольная ссылка на файл, открытый на fd x. В Linux, когда вы делаете a open("/dev/fd/x", somemode), вы получаете совершенно новое описание открытого файла в том же файле, что и open x. Новый полученный вами fd никак не связан с fd x. В частности, смещение будет в начале файла (кроме случаев, когда вы, O_APPENDконечно, открываете его ), а режим (чтение / запись / добавление ...) может отличаться от режима на fd x (вы даже можете получить нечто совершенно отличное от того, что есть на fd x, например, другой конец трубы при открытии в противоположном режиме). (Это также означает, что это не работает для сокетов, например, которые вы не можете открыть () ).

Итак, в Linux, когда вы делаете

exec 5<> file
echo test >&5

Смещение fd 5 находится в конце файла. Если вы делаете

cat <&5

Вы ничего не получите.

Тем не менее, когда вы делаете:

cat /dev/fd/5

Вы видите, testпотому что catполучает новый доступный только для чтения fd, fileне связанный с fd 5.

В других системах, после

cat /dev/fd/5

cat получает fd, который является дубликатом fd 5, поэтому все еще со смещением в конце файла.

Причина, по которой он работает, lessзаключается в том, что по какой-то причине lessвыполняет lseek()поиск в этом файле до начала файла (делает, lseek(1); lseek(0)чтобы определить, является ли файл доступным для поиска или нет).

Здесь вы, вероятно, хотите иметь fd для чтения и один для записи, если вы хотите, чтобы оба имели разные смещения:

exec 5< file 9>&1 > file

Или вам придется заново открыть файл, если он все еще там, или сделать lseek()как less.

ksh93и zshявляются единственными оболочками со встроенным lseek()оператором:

cat <&5 <#((0)) # ksh93
{sysseek 0; cat} <&5 # zsh, zmodload zsh/system to enable that builtin

Или:

cat /dev/fd/5 5<#((0))  # ksh93
sysseek -u 5 0; cat /dev/fd/5 # zsh
Стефан Шазелас
источник