Como o `cat <> file` funciona?

42

cat < fileimprime o conteúdo do arquivo em stdout.

cat > filelê stdin até que Ctrl+ Dseja detectado e o texto de entrada seja gravado no arquivo .

cat <> file, pelo menos na minha versão do Bash, imprime o conteúdo do arquivo com satisfação (sem erros), mas não modifica o arquivo nem atualiza o carimbo de data / hora da modificação.

Como o padrão Bash justifica o aparentemente ignorado >na terceira declaração - e, mais importante, está fazendo alguma coisa?

Qix
fonte

Respostas:

47

O Bash usa <>para criar um descritor de arquivo de leitura e gravação :

O operador de redirecionamento

[n]<>word

faz com que o arquivo cujo nome é a expansão da palavra seja aberto para leitura e gravação no descritor de arquivo n ou no descritor de arquivo 0 se n não for especificado. Se o arquivo não existir, ele será criado.

cat <> fileabre a fileleitura e gravação e o vincula ao descritor 0 (entrada padrão). É essencialmente equivalente a < filequalquer programa escrito de maneira sensata, já que ninguém provavelmente tenta escrever em entradas padrão normalmente, mas se alguém o fizesse, seria capaz.

Você pode escrever um programa C simples para testar isso diretamente - write(0, "hello", 6)vai escrever helloem filevia entrada padrão.

<>também deve funcionar em qualquer outro shell compatível com POSIX com o mesmo efeito.

Michael Homer
fonte
1
Escrevendo ... para stdin? ... Existe algum caso de uso válido para isso?
Qix
3
Prontamente, não consigo pensar em ninguém. Fornecer um descritor explícito ( 4<>file) é útil, e suponho que 0 seja um padrão tão bom quanto qualquer outro quando você o deixa de fora. Ler do stdout não é melhor.
Michael Homer
5
<>também é útil em alguns sistemas (como Linux) para abrir pipes nomeados sem bloquear até que outro processo o abra para gravação.
Stéphane Chazelas
1
@ Qix: Bem, escrever (0, "Senha:", 10) é uma boa maneira de solicitar uma senha, se você pretende solicitar algo como um tty. Estou acostumado a vê-lo apenas no stderr, mas não há nenhuma razão em particular que a mesma técnica não funcione no stdin.
27714 Joshua
3
@Qix - da justificativa POSIX - O <>operador pode ser útil ao escrever um aplicativo que funcione com vários terminais e que ocasionalmente deseje iniciar um shell. Esse shell, por sua vez, seria incapaz de executar aplicativos executados a partir de um terminal de controle comum, a menos que pudesse fazer uso de <>... como ... o pager more, que lê o erro padrão para obter seus comandos, portanto entrada e saída padrão ambos estão disponíveis para o uso habitual. cat food | more - >/dev/tty03 2<>/dev/tty03
mikeserv
38

<> fileabre o arquivo (em descritor de arquivo 0 (stdin) por padrão, como <) em + write leitura modo , sem cortes e criar o arquivo se ele não existia previamente .

Isso corresponde aos O_RDWR|O_CREATsinalizadores passados ​​para a open()chamada do sistema. Pelo contrário, <é O_RDONLYe >é O_WRONLY|O_CREAT|O_TRUNCe >> O_WRONLY|O_CREAT|O_APPEND.

Ter stdin gravável geralmente não é útil, pois os aplicativos geralmente não gravam no stdin. Os aplicativos geralmente não esperam ler e escrever em um descritor de arquivo que recebem na inicialização; eles geralmente leem do stdin (ou um descritor de arquivo que se abrem) e escrevem para stdout ou stderr (ou um descritor de arquivo que se abrem).

<> pode ter seus usos:

  • Você pode preferir cat <> filemais cat < filese você não deseja que o comando falhar se filenão existe, mas um vazio filecriado em seu lugar.
  • O aspecto não truncante <>torna útil substituir arquivos no local. Nesse caso, no entanto, você geralmente não o usa no descritor de arquivo 0:

    printf xxx 1<> file

    substitui os 3 primeiros bytes de filecom xxx.

  • Em alguns sistemas como o Linux, <>em um canal nomeado (FIFO), abre o canal nomeado sem bloquear (sem esperar por outro processo abrir a outra extremidade) e garante que a estrutura do canal permaneça ativa. Por exemplo, em:

    mkfifo pipe; sed 's/foo/bar/g' <> pipe

    sedlida com dados recebidos de qualquer número de outros processos gravados nele e nunca os vê eof.

Stéphane Chazelas
fonte
1
Observe que, na AT&T ksh93, o <>padrão é 1<>(stdout) em vez de 0<>(stdin). Este é um bug de conformidade com POSIX que relatei e será corrigido na próxima versão. github.com/att/ast/issues/75 Mas até as versões atuais do ksh93 ficarem fora de uso, você deverá incluir o número do descritor de arquivo para usar <>portably.
Martijn Dekker
@ MartijnDekker, eu sei, fui eu quem te contou sobre isso em primeiro lugar ;-). Observe que é apenas para ksh93t + (onde o comportamento mudou) e acima.
Stéphane Chazelas
Quais são (ou eram) os sistemas, ao contrário do Linux, onde mkfifo fifo; exec 3<>fifobloqueariam?
Tio Billy