Testando se um descritor de arquivo é válido

7

Gostaria de fazer com que um script bash fornecesse informações adicionais aos descritores de arquivos (FDs) maiores ou iguais a 3, quando eles estivessem abertos. Para testar se um FD está aberto, eu criei o seguinte truque:

if (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

Isso é suficiente para minhas necessidades, mas estou curioso para saber se existe uma maneira mais idiomática de testar se um FD é válido. Estou especialmente interessado em saber se existe um mapeamento do fcntl(1) syscall para um comando shell, o que permitiria a recuperação de sinalizadores FD ( O_WRONLY e O_RDWR para testar se o FD é gravável e O_RDONLY e O_RDWR para testar se o FD é legível).

    
por Witiko 01.06.2015 / 10:55

5 respostas

6

Em ksh (variantes AT & T e pdksh) ou zsh , você pode fazer:

if print -nu3; then
  echo fd 3 is writeable
fi

Eles não escrevem nada nesse fd, mas ainda verificam se o fd é gravável (usando fcntl(3, F_GETFL) ) e relatam um erro do contrário:

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing

(que você pode redirecionar para /dev/null ).

Com bash , acho que sua única opção é verificar se um dup() é bem-sucedido em sua abordagem, embora isso não garanta que o fd seja gravável (ou chame um utilitário externo ( zsh / perl ...) para fazer o fcntl() ).

Observe que, em bash (como a maioria dos shells), se você usar (...) em vez de {...;} , isso resultará em um processo extra. Você pode usar:

if { true >&3; } 2> /dev/null

, em vez de evitar a bifurcação (exceto no shell Bourne, em que o redirecionamento de comandos compostos sempre causa um subshell). Não use : em vez de true , pois isso é um especial incorporado, portanto, o shell sairá quando o bash estiver no modo de conformidade POSIX.

Você pode, no entanto, encurtá-lo para:

if { >&3; } 2> /dev/null
    
por 01.06.2015 / 17:43
6

Na descrição POSIX command Uso do aplicativo você vai encontrar o seguinte:

There are some advantages to suppressing the special characteristics of special built-ins on occasion. For example:

command exec > unwritable-file

does not cause a non-interactive script to abort, so that the output status can be checked by the script.

É por isso que você pode fazer:

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2>/dev/null

Ou ...

{ command >&3
  printf %s\n%.0d  string "0$(($??8:0))" >&"$(($??1:3))"
} 2>/dev/null

O qual gravará string seguido por% e_line% ewline para stdout ou 3 e ainda transmitirá um status de saída diferente de zero quando 3 não estiver aberto porque a matemática é feita em \n wind se não converter o octal 08 para % decimal mas trunca para nada em octal 00 .

Ou ...

command exec >&3 || handle_it

Mas se você estiver usando $? , basta fazer:

fds

Para uma lista de descritores de arquivos abertos. Adicione ksh93 para ver onde eles vão.

    
por 01.06.2015 / 18:19
3

Os descritores de arquivos abertos podem ser encontrados em /proc/<pid>/fd . Para listar, por exemplo, os descritores de arquivos abertos do shell atual, você pode emitir ls -l /proc/$$/fd , o que deve lhe dar algo como:

total 0
lrwx------ 1 testuser testuser 64 jun  1 09:11 0 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 1 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 2 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:39 255 -> /dev/pts/3

Quando você abre um arquivo usando:

touch /tmp/myfile
exec 7</tmp/myfile

Ele deve ser listado por um novo ls -l /proc/$$/fd :

lr-x------ 1 testuser testuser 64 jun  1 09:11 7 -> /tmp/myfile

Se você fechar o descritor de arquivo novamente usando exec 7>&- , ele também não será listado em /proc/$$/fd .

    
por 01.06.2015 / 11:17
3

Seu truque parece fofo; mas para uma maneira idiomática eu me pergunto por que você não usou:

if ( exec 1>&3 ) 2>&-
    
por 01.06.2015 / 11:16
-1

Se você estiver interessado em uma solução de bifurcação baixa para usá-la repetidamente, sugiro esta função:

checkfd() {
    exec 2>/dev/null
    if exec >&3 ; then
        exec 1>/dev/tty
        echo "fd3 OK"
    else
        echo "fd3 KO"
    fi
    exec 2>/dev/tty
}

E aqui está o que produz com um zsh :

$ checkfd            
fd3 KO
$ checkfd 3>/dev/null
fd3 OK
$
    
por 01.06.2015 / 18:45