Supondo que haja dois arquivos no Linux. FileA e FileB, ambos com alguma lista diferente de frutas. Eu aplico os comandos abaixo.
diff fileA fileB > file.diff
Em seguida, executo o comando abaixo
patch fileA 0< file.diff
O comando acima corrige (corrige os erros) o arquivo original ( fileA ) da entrada dada por file.diff e envia a saída para fileA (é isso que eu entendi, posso estar errado). Em outras palavras, fileA e fileB correspondem.
"0<" é conhecido como um símbolo de redirecionamento para entrada padrão (até onde eu entendo). Agora, já que a entrada padrão é um teclado, o comando patch não deveria ler do teclado e não do file.diff ? Como o comando acima funciona?
Esta resposta foi testada usando um shell Bash.
Os comandos abaixo são os mesmos. O
1
é o padrão e, portanto, pode ser omitido. Basicamente, a saída padrão dediff
é redirecionada da tela para o arquivofile.diff
.Os comandos abaixo são os mesmos. O
0
é o padrão e, portanto, pode ser omitido. Basicamente, a entrada padrão parapatch
é redirecionada do teclado para ser do arquivofile.diff
.Tentarei explicar da seguinte forma. Quando entro no comando
tty
, obtenho a seguinte saída.Isso significa que a janela Terminal recebeu o nome de arquivo
/dev/pts/0
. Tanto a entrada padrão quanto a saída padrão receberam os nomes de arquivo/dev/fd/0
e/dev/fd/1
, respectivamente.O comando abaixo testa se a entrada padrão (
/dev/fd/0
) e a janela Terminal (/dev/pts/0
) têm os mesmos valores de dispositivo e inode. Em outras palavras, teste para ver se eles são os mesmos. Neste caso, a saída étrue
.O comando abaixo testa se a entrada padrão (
/dev/fd/0
) e o arquivofile.diff
têm os mesmos valores de dispositivo e inode. Neste caso, a saída éfalse
.Entretanto, se a entrada padrão for redirecionada para vir do arquivo
file.diff
, como mostrado abaixo, então a saída serátrue
.Minha resposta até este ponto explicou o comportamento do redirecionamento ao usar um shell Bash. Esse comportamento deve ser consistente em todos os sistemas operacionais. Evitei detalhes de implementação, porque isso pode variar em diferentes sistemas operacionais. Você pode estar interessado em alguns detalhes de implementação, então apresento o seguinte para o Ubuntu Linux.
A saída abaixo mostra que a entrada padrão (
/dev/fd/0
) e a saída padrão (/dev/fd/1
) são links simbólicos para a janela do Terminal (/dev/pts/0
).Abaixo estão os resultados quando a entrada padrão é redirecionada para vir do arquivo
file.diff
. Agora, a entrada padrão (/dev/fd/0
) mudou para um link simbólico para o arquivofile.diff
.Não. A entrada padrão é conectada ao teclado (ou mais precisamente ao dispositivo 'tty' através do qual o SO fornece entrada de teclado). A qualquer momento, a conexão pode ser fechada e outra coisa aberta em seu lugar – o termo "entrada padrão" se refere ao "slot" de conexão específico e não a onde ele vai. (É por isso que é chamado de "entrada padrão" e não "entrada de teclado".)
Os números não são apenas sintaxe de shell; eles representam como os próprios programas trabalham com arquivos abertos. Dentro de cada processo, todo "arquivo aberto" é representado por um número (o descritor de arquivo , ou o handle como o Windows o chama) e todas as chamadas de leitura/gravação funcionam nesse número. Por convenção padrão, qualquer arquivo aberto que é atribuído ao descritor de arquivo
0
é a "entrada padrão".Se você iniciar o programa a partir de um terminal, o "tty" do terminal será pré-aberto como FD
0
– ou melhor, herdado do shell onde ele já estava aberto – e, portanto, será o stdin do programa. (O mesmo vale para1
stdout e2
stderr.) Então, o programa 'diff' precisará abrir alguns arquivos, então ele chamaráopen("fileA", ...)
e o arquivo A será aberto como FD3
e assim por diante.Mas assim como o programa pode fechar qualquer arquivo que ele mesmo abriu (por exemplo, ele pode fechar o arquivo A executando
close(3)
), ele também podeclose(0)
fechar seu stdin e abrir outra coisa em seu lugar; desde que o arquivo recém-aberto receba o descritor de arquivo,0
ele é, por definição, "entrada padrão".O shell pode fazer o mesmo logo antes de iniciar o programa. Usar
<file.diff
ou0<file.diff
significa que o shell fechará seu descritor de arquivo stdin original comclose(0)
e abrirá o arquivoopen("file.diff")
como o novo FD0
que então se torna o novo stdin, a ser herdado pelo programa 'diff'. (Isso acontece no processo filho que o shell cria para iniciar 'diff', sem afetar o processo principal do shell.) Agora, quando diff chama,read(0, …)
ele lê do arquivo.Notas laterais:
Geralmente
open()
usa o descritor de arquivo mais baixo disponível, por exemplo, se 0 acabou de ser fechado, então o próximo arquivo open()ed será 0 novamente. Se um controle mais preciso for necessário,dup2()
pode ser usado para escolher um FD específico.É possível redirecionar qualquer descritor de arquivo, por exemplo,
5>file.txt
dará ao programa um FD pré-aberto5
correspondente àquele arquivo, mas isso só é útil se o programa espera receber um; caso contrário, ele permanecerá aberto, mas não utilizado. (Alguns programas têm opções comogpg --status-fd=
which podem ser usadas para passar FDs adicionais.)O Windows tem conceitos semelhantes e seu cmd.exe até tem a mesma
2>
sintaxe, mas os identificadores de arquivo do Windows não são numerados a partir de 0 (eles são ponteiros de memória), então o cmd.exe implementa apenas 0/1/2 e os traduz para identificadores padrão no estilo Windows. (Enquanto isso, o PowerShell é um mundo estranho.)Semelhante ao exemplo que David Anderson deu,
ls -l /proc/self/fd
– somente no Linux – pode ser usado para inspecionar seus próprios descritores de arquivo, ouls -l /proc/<pid>/fd
para qualquer outro processo. Por exemplo:Aqui os descritores de arquivo 0 (stdin), 1 (stdout), 2 (stderr) estão atualmente conectados ao terminal (todos herdados do shell), enquanto 3 foi aberto pelo próprio 'ls' e é, claro, o diretório que está sendo listado. Se você redirecionar stdin usando
<
, você obtém:E o mesmo com alguns descritores de arquivo inúteis fornecidos ao programa:
No Linux e outros sistemas operacionais do tipo Unix
O que o shell faz quando quer executar outro programa é clonar uma cópia de si mesmo por uma chamada para
fork()
(isso pode parecer muito trabalho, mas através da mágica da cópia na escrita o esforço e o custo de recursos são muito reduzidos). Então essa cópia se substitui pelo programa que quer executar por uma chamada paraexec()
(a chamada real do SO éexecve()
)No caso de redirecionamento de entrada ou saída ser necessário, a cópia bifurcada sabe disso e esses arquivos ou fluxos são abertos entre o
fork()
e oexec()
, assim o shell original mantém sua conexão com os dispositivos de E/S originais, mas o ambiente no qual o novo programa é iniciado foi alterado.Isto é explicado em detalhes em LPG
O termo "redirecionamento" pode ser um pouco confuso, o que é redirecionado é a chamada de leitura do processo, porque a leitura de descritores de arquivo como STDIN é pull e não push. O processo puxa dados de "STDIN".
Geralmente você tem uma conexão que conecta o STDIN ao seu teclado. Tudo o que você digitar no teclado será mantido em um buffer e quando o processo ler de "STDIN", ele lerá desse buffer e obterá as teclas que você digitou.
Process --read-> STDIN --read-> Keyboard-Buffer
Quando você inicia o processo com
proc 0< file.diff
isso, as chamadas de leitura do processo serão redirecionadas para STDIN para file.diff. Agora há uma conexão de STDIN para file.diff em vez do teclado.Process --read-> STDIN --read-> file.diff
O processo ainda pode ler diretamente do buffer do teclado acessando algo como
/dev/pts/0
(dependendo do terminal), mas uma chamada para ler STDIN agora lerá de file.diff.