
Um plugin vim que eu uso usa este script para passar algumas entradas para linters que não suportam leitura de stdin.
set -eu
# All of the following arguments are read as command to run.
file_extension="$1"
shift
temp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'ale_linter')
temp_file="$temp_dir/file$file_extension"
trap 'rm -r "$temp_dir"' EXIT
while read -r; do
echo "$REPLY" >> "$temp_file"
done
"$@" "$temp_file"
No começo fiquei um pouco confuso sobre por que eles não usaram algo assim
some input | some_program /dev/stdin
Mas depois de tentar ghc
como linter, descobri que ele está reclamando de /dev/stdin dizendo algo sobre que não é um arquivo real (o que não é)
Então, eu me pergunto se posso usar o pipe nomeado em vez do arquivo temporário. A razão pela qual não estou muito satisfeito em gravar arquivos temporários é a integridade do SSD e se existe uma maneira melhor de fazer isso, por que não fazê-lo, certo?
Responder1
Não, os programas que rejeitam esses arquivos geralmente os rejeitam alegando que o arquivo não épesquisável(eles precisam acessar o conteúdo em deslocamentos arbitrários ou várias vezes após retroceder, etc.). Ou eles gostariam de abrir o arquivo várias vezes. Eles também podem querer reescrever (parte) do arquivo ou truncá-lo.
Os sem nome pipe
(como com |
e /dev/stdin
) ou nomeados não fazem diferença em nenhum desses casos.
Na verdade, no Linux, /dev/stdin
quando stdin é um pipe (nomeado ou não) se comporta exatamente como um pipe nomeado, o programa não seria capaz de diferenciá-lo /dev/stdin
de um pipe nomeado real.
Em outros sistemas, não é exatamente o mesmo, mas, na verdade, abrir /dev/stdin
um pipe nomeado fornecerá um descritor de arquivo para um pipe, algo que não pode ser procurado de qualquer maneira.
Então, você precisará criar o arquivo temporário. Observe que alguns shells facilitam isso. Com zsh
, é apenas:
#! /bin/zsh -
"$@" =(cat)
No Linux e com shells que usam arquivos temporários excluídos para documentos aqui (como bash
e zsh
algumas implementações de ksh
), você pode fazer:
#! /bin/bash -
"$@" /dev/fd/3 3<< EOF
$(cat)
EOF
No entanto, isso pode alterar o conteúdo do arquivo se ele contiver caracteres NUL ou terminar em linhas vazias.
Observe que desde a versão 5, o bash torna o arquivo temporário here doc somente leitura, portanto, se o aplicativo precisar fazer modificações nesse arquivo, você deverá restaurar as permissões de gravação com:
#! /bin/bash -
{
chmod u+w /dev/fd/3 && # only needed in bash 5+
"$@" /dev/fd/3
} 3<< EOF
$(cat)
EOF
Uma observação sobre esse while read
loop desde que você perguntou.
Primeiro read -r
, sem um nome de variável, a sintaxe não é válida sh
. A sh
sintaxe é especificada pelo POSIX (ISO 9945, também IEEE Std 1003.1), assim como a C
sintaxe é especificada pela ISO 9899.
Emessa especificação, você notará que isso read
requer um argumento de nome de variável. O comportamento quando você o omite énão especificadoe na prática variam de acordo com a sh
implementação do intérprete.
bash
sh
é o interpretador GNU , assim como gcc
o compilador GNU C. Ambos bash
e gcc
têm extensões sobre o que esses padrões especificam.
No caso de read
, bash
trata read -r
como se fosse IFS= read -r REPLY
. Na especificação POSIX, IFS= read -r REPLY
lê stdin até que um \n
caractere ou o final da entrada seja alcançado e armazena os caracteres lidos na $REPLY
variável e retorna com umsucessostatus de saída se um caractere de nova linha foi lido (uma linha completa) oufalhacaso contrário (como EOF antes da nova linha) e deixa o comportamento indefinido se os dados lidos contiverem caracteres NUL ou sequências de bytes que não formem caracteres válidos.
No caso de bash
, armazenará os bytes lidos mesmo que não formem caracteres válidos e removerá os caracteres NUL.
read -r
é como read -r REPLY
em ksh
or zsh
e relata um erro em shells semelhantes a POSIX baseados yash
em or .ash
O comportamento de echo
não é especificado, a menos que seus argumentos não contenham caracteres de barra invertida e o primeiro não seja -n
.
Então, para resumir, a menos que você conheça a sh
implementação (e versão) específica com a qual está lidando, não poderá dizer o que está acontecendo.
while read -r; do
echo "$REPLY" >> "$temp_file"
done
vai fazer. No caso bash
específico, ele armazenará stdin no temp_file apenas enquanto os dados não contiverem caracteres NUL, terminarem em um caractere de nova linha e nenhuma das linhas corresponder à ^-[neE]+$
expressão regular estendida (e/ou dependendo do ambiente ou como bash
foi compilado como o sh
do OS/X, não contém caracteres de barra invertida).
É tambémmuito ineficiente e não é a maneira como você processa texto em shells.
Aqui você quer:
cat > "$temp_file"
cat
é umcomando padrão, que quando não recebe nenhum argumento apenas despeja seu stdin em seu stdoutcomo é.