OS X, bash: menos funciona em descritores de arquivos abertos, cat não

10

Em um script bash em que estou trabalhando (que precisa ser executado no Ubuntu e no OS X), preciso redirecionar a saída de centenas de comandos para um arquivo.
Em vez de anexar &>... a todos eles, simplesmente

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

Até aí tudo bem, mas no meio de todos esses comandos, eu preciso ler tudo o que foi escrito até agora, mantendo o descritor de arquivos aberto.
Agora, no Ubuntu eu posso simplesmente fazer

cat /dev/fd/5

ou

tee </dev/fd/5

mas no OS X, nada é impresso (e os comandos saem imediatamente).
No entanto, usando less , posso ver o conteúdo do arquivo em ambos. Eu posso alcançar o efeito acima (trabalhando em ambos os sistemas operacionais) usando

less /dev/fd/5 | tee

mas isso parece um hack.

Então, por que less aparentemente pode ver coisas que cat não pode no OS X? (Ou todos os descendentes de BSD são afetados?)
Ou estou fazendo algo errado?

    
por Siguza 14.06.2015 / 20:34

1 resposta

13

No OS X, como em todos os sistemas em que são suportados exceto Linux , abrir /dev/fd/x é como fazer um dup(x) , o fd resultante aponta mais ou menos para a mesma descrição de arquivo aberto como em fd x e, em particular, terá o mesmo offset dentro do arquivo.

O Linux é a exceção aqui. No Linux, /dev/fd/x é um link simbólico para /proc/self/fd/x e /proc/self/fd/x é um pseudo-symlink para o arquivo aberto no fd x. No Linux, quando você faz um open("/dev/fd/x", somemode) , você obtém uma nova descrição do arquivo aberto para o mesmo arquivo que está aberto em x . O novo fd que você obtém não está relacionado a fd x de forma alguma. Em particular, o deslocamento será no início do arquivo (exceto se você abrir com O_APPEND é claro) e o modo (leitura / gravação / anexação ...) pode ser diferente daquele em fd x (você pode até obter algo bastante diferente do que está no fd x, como a outra extremidade do tubo ao abri-lo no modo oposto). (Isso também significa que isso não funciona para sockets, por exemplo, que você não pode abrir () ).

Então, no Linux, quando você faz

exec 5<> file
echo test >&5

O deslocamento do fd 5 está no final do arquivo. Se você fizer

cat <&5

Você não ganha nada.

Ainda quando você faz:

cat /dev/fd/5

Você vê test porque cat obtém um novo fd somente leitura para file não relacionado ao fd 5.

Em outros sistemas, após

cat /dev/fd/5

cat obtém um fd que é uma duplicata do fd 5, portanto ainda com um deslocamento no final do arquivo.

O motivo pelo qual ele funciona com less é que, por algum motivo, less faz um lseek() nesse fd para o início do arquivo (faz um lseek(1); lseek(0) para determinar se o arquivo é procurado ou não ).

Aqui, você provavelmente deseja ter um fd para leitura e um para escrever, se quiser que ambos tenham diferentes deslocamentos:

exec 5< file 9>&1 > file

Ou você terá que reabrir o arquivo, se ainda estiver lá, ou fazer um lseek() como less .

ksh93 e zsh são os únicos shells com um operador lseek() incorporado:

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

Ou:

cat /dev/fd/5 5<#((0))  # ksh93
sysseek -u 5 0; cat /dev/fd/5 # zsh
    
por 14.06.2015 / 23:47