Estou tentando entender como exatamente o Bash trata a seguinte linha:
$(< "$FILE")
De acordo com a página do manual do Bash, isso é equivalente a:
$(cat "$FILE")
e posso seguir a linha de raciocínio dessa segunda linha. O Bash executa expansão variável $FILE
, entra na substituição de comando, passa o valor de $FILE
para cat
, cat envia o conteúdo de $FILE
para a saída padrão, a substituição do comando termina substituindo a linha inteira pela saída padrão resultante do comando interno, e o Bash tenta executá-lo como um comando simples.
No entanto, para a primeira linha que mencionei acima, entendo como: Bash realiza substituição de variável ativada $FILE
, o Bash é aberto $FILE
para leitura na entrada padrão, de alguma forma a entrada padrão é copiada para a saída padrão , a substituição do comando é concluída e o Bash tenta executar o padrão resultante resultado.
Alguém por favor pode me explicar como o conteúdo de $FILE
vai de stdin para stdout?
fonte
bash
interpretará isso comocat filename
", você quer dizer que esse comportamento é específico para comandar a substituição? Porque, se eu correr< filename
sozinho, o bash não dá certo. Ele não produzirá nada e retornará a um prompt.cat < filename
paracat filename
que eu me oponho e pode reverter.|
cria um canal entre dois subprocessos (ou, com algumas conchas, de um subprocesso para a entrada padrão do shell). O operador shell$(…)
cria um canal de um subprocesso para o próprio shell (não para sua entrada padrão). O operador shell<
não envolve um canal, apenas abre um arquivo e move o descritor de arquivo para a entrada padrão.< file
não é o mesmo quecat < file
(excetozsh
onde é como$READNULLCMD < file
).< file
é perfeitamente POSIX e apenas abrefile
para leitura e, em seguida, não faz nada (logofile
fica próximo). É$(< file)
ou`< file`
é um operador especial deksh
,zsh
ebash
(e o comportamento não é especificado no POSIX). Veja minha resposta para detalhes.$(cmd1) $(cmd2)
normalmente será o mesmo que$(cmd1; cmd2)
. Mas veja o caso ondecmd2
está< file
. Se dizemos$(cmd1; < file)
, o arquivo não é lido, mas, com$(cmd1) $(< file)
, é. Portanto, é incorreto dizer que$(< file)
é apenas um caso comum de$(command)
com um comando de< file
.$(< …)
é um caso especial de substituição de comando e não um uso normal de redirecionamento.$(<file)
(também trabalha com`<file`
) é um operador especial do shell Korn copiado porzsh
ebash
. Parece muito com substituição de comando, mas na verdade não é.Nos shells POSIX, um comando simples é:
Todas as peças são opcionais, você pode ter apenas redirecionamentos, apenas comandos, apenas atribuições ou combinações.
Se houver redirecionamentos, mas nenhum comando, os redirecionamentos serão executados (para que um
> file
abra e trunquefile
), mas nada acontece. assimAbre
file
para leitura, mas nada acontece, pois não há comando. Então ofile
é então fechado e é isso. Se$(< file)
fosse uma simples substituição de comando , ela se expandiria para nada.Na especificação POSIX , em
$(script)
, sescript
consiste apenas em redirecionamentos, produz resultados não especificados . Isso é para permitir esse comportamento especial do shell Korn.No ksh (testado aqui com
ksh93u+
), se o script consistir em um e apenas um comando simples (embora os comentários sejam permitidos antes e depois) que consistem apenas em redirecionamentos (sem comando, sem atribuição) e se o primeiro redirecionamento for um stdin (fd 0) única entrada (<
,<<
ou<<<
) de redireccionamento, de modo que:$(< file)
$(0< file)
$(<&3)
(também$(0>&3)
, na verdade, o mesmo operador)$(< file > foo 2> $(whatever))
mas não:
$(> foo < file)
$(0<> file)
$(< file; sleep 1)
$(< file; < file2)
então
<&3
), menos os caracteres de nova linha à direita.como se estivesse usando,
$(cat < file)
exceto quecat
$(<${file=foo.txt})
ou$(<file$((++n)))
)Em
zsh
, é o mesmo, exceto que esse comportamento especial só é acionado quando há apenas um redirecionamento de entrada de arquivo (<file
ou0< file
, não<&3
,<<<here
,< a < b
...)No entanto, exceto ao emular outras conchas, em:
é quando há apenas redirecionamentos de entrada sem comandos, fora da substituição de comando,
zsh
executa o$READNULLCMD
(um pager por padrão) e quando há redirecionamentos de entrada e saída, o$NULLCMD
(cat
por padrão), portanto, mesmo que$(<&3)
não seja reconhecido como especial operador, ele ainda funcionará como seksh
estivesse chamando um pager para fazê-lo (esse pager agindo como secat
seu stdout fosse um canal).No entanto, enquanto
ksh
's$(< a < b)
se expandiria para o conteúdoa
, emzsh
, ele se expande para o conteúdo doa
eb
(ou apenasb
se amultios
opção for desativada),$(< a > b)
iria copiara
parab
e expandir a nada, etc.bash
tem um operador semelhante, mas com algumas diferenças:comentários são permitidos antes, mas não depois:
funciona mas:
expande para nada.
como em
zsh
, apenas um arquivo stdin redirecionamento, embora não há nenhuma volta queda a um$READNULLCMD
, por isso$(<&3)
,$(< a < b)
não executar os redirecionamentos mas expandir para nada.bash
não seja invocadocat
, ele ainda bifurca um processo que alimenta o conteúdo do arquivo através de um canal, tornando-o muito menos otimizador do que em outros shells. É como um local$(cat < file)
ondecat
seria construídocat
.$(<${file=foo.txt})
mencionado acima, por exemplo, essa$file
atribuição é perdida posteriormente).Em
bash
,IFS= read -rd '' var < file
(também funcionazsh
) é uma maneira mais eficaz de ler o conteúdo de um arquivo de texto em uma variável. Ele também tem o benefício de preservar os caracteres de nova linha à direita. Veja também$mapfile[file]
emzsh
(nozsh/mapfile
módulo e apenas para arquivos regulares) que também funciona com arquivos binários.Observe que as variantes baseadas em pdksh
ksh
têm algumas variações em comparação com o ksh93. De interesse, emmksh
(uma dessas conchas derivadas de pdksh), emé otimizado, pois o conteúdo do documento here (sem os caracteres finais) é expandido sem que um arquivo ou canal temporário seja usado, como é o caso dos documentos here, o que o torna uma sintaxe eficaz de cotação de várias linhas.
Ser portátil para todas as versões de
ksh
,zsh
ebash
, o melhor é limitar-se a$(<file)
evitar comentários e ter em mente que as modificações nas variáveis feitas dentro podem ou não ser preservadas.fonte
$(<)
seja um operador nos nomes de arquivos? Está<
em$(<)
um operador de redirecionamento, ou não é um operador por si só, e deve fazer parte de todo o operador$(<)
?$(<file)
destina-se a expandir o conteúdo defile
maneira semelhante à que$(cat < file)
seria. Como isso é feito varia de shell para shell, que é descrito detalhadamente na resposta. Se desejar, você pode dizer que é um operador especial que é acionado quando o que parece uma substituição de comando (sintaticamente) contém o que parece um único redirecionamento stdin (sintaticamente), mas novamente com advertências e variações dependendo do shell, conforme listado aqui .n<&m
en>&m
faz a mesma coisa? Eu não sabia disso, mas acho que não é tão surpreendente.dup(m, n)
. Eu posso ver algumas evidências do ksh86 usando o stdio e outrasfdopen(fd, "r" or "w")
, então pode ter sido importante. Mas usar stdio em um shell faz pouco sentido, então não espero que você encontre nenhum shell moderno onde isso faça diferença. Uma diferença é que>&n
édup(n, 1)
(abreviação de1>&n
), enquanto<&n
édup(n, 0)
(abreviação de0<&n
).dup2()
;dup()
usa apenas um argumento e, comoopen()
, usa o descritor de arquivo disponível mais baixo. (Hoje eu aprendi que há umadup3()
função .)Como
bash
ele é feito internamente para você, expandiu o nome do arquivo e atribuiu o arquivo ao arquivo como saída padrão, como se você fosse fazer$(cat < filename)
. É um recurso bash, talvez você precise procurar nobash
código fonte para saber exatamente como ele funciona.Aqui está a função para lidar com esse recurso (do
bash
código-fonte, arquivobuiltins/evalstring.c
):Uma nota que
$(<filename)
não é exatamente equivalente a$(cat filename)
; o último falhará se o nome do arquivo começar com um traço-
.$(<filename)
foi originalmente deksh
e foi adicionado abash
partir deBash-2.02
.fonte
cat filename
falhará se o nome do arquivo começar com um traço porque cat aceita opções. Você pode contornar isso na maioria dos sistemas modernos comcat -- filename
.Pense na substituição de comando como executando um comando como de costume e descartando a saída no ponto em que você está executando o comando.
foo=$(echo "bar")
irá definir o valor da variável$foo
comobar
; a saída do comandoecho bar
.Substituição de Comando
fonte
$(< file)
, e ele não precisa de um tutorial sobre o caso geral. Se você está dizendo que esse$(< file)
é apenas um caso comum de$(command)
comando< file
, então você está dizendo a mesma coisa que Adam Katz está dizendo e ambos estão errados.