
Un complemento de vim que uso utiliza este script para pasar algunas entradas a linters que no admiten la lectura desde la entrada estándar.
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"
Al principio estaba un poco confundido acerca de por qué no habían usado algo así.
some input | some_program /dev/stdin
Pero después de intentarlo ghc
como linter, descubrí que se queja de que /dev/stdin dice algo acerca de que no es un archivo real (que no lo es).
Entonces me pregunto si puedo usar la canalización con nombre en lugar de un archivo temporal. La razón por la que no estoy del todo satisfecho con escribir en archivos temporales es el estado del SSD y si hay una mejor manera de hacerlo, ¿por qué no hacerlo, verdad?
Respuesta1
No, los programas que rechazan esos archivos generalmente los rechazan basándose en que el archivo no esbuscable(Necesitan acceder al contenido en desplazamientos arbitrarios, o varias veces después de rebobinar, etc.). O querrían abrir el archivo varias veces. Es posible que también quieran reescribir (parte de) el archivo o truncarlo.
Los que no tienen nombre pipe
(como con |
y /dev/stdin
) o los que tienen nombre no hacen ninguna diferencia en ninguno de esos casos.
En realidad, en Linux, /dev/stdin
cuando la entrada estándar es una tubería (con nombre o no) se comporta exactamente como una tubería con nombre, el programa no podría diferenciarla /dev/stdin
de una tubería con nombre real.
En otros sistemas, no es exactamente lo mismo, pero, en efecto, abrir /dev/stdin
una tubería con nombre le proporcionará un descriptor de archivo para una tubería, algo que no se puede buscar de ninguna manera.
Entonces, necesitarás crear el archivo temporal. Tenga en cuenta que algunos caparazones lo hacen más fácil. Con zsh
, es solo:
#! /bin/zsh -
"$@" =(cat)
En Linux y con shells que utilizan archivos temporales eliminados para estos documentos (como bash
y zsh
algunas implementaciones de ksh
), puede hacer:
#! /bin/bash -
"$@" /dev/fd/3 3<< EOF
$(cat)
EOF
Sin embargo, eso puede alterar el contenido del archivo si contiene caracteres NUL o termina en líneas vacías.
Tenga en cuenta que desde la versión 5, bash hace que el archivo temporal aquí doc sea de solo lectura, por lo que si la aplicación necesita realizar modificaciones en ese archivo, deberá restaurar los permisos de escritura con:
#! /bin/bash -
{
chmod u+w /dev/fd/3 && # only needed in bash 5+
"$@" /dev/fd/3
} 3<< EOF
$(cat)
EOF
Una nota sobre ese while read
bucle desde que preguntaste.
Primero read -r
, sin un nombre de variable no es sh
una sintaxis válida. La sh
sintaxis está especificada por POSIX (ISO 9945, también IEEE Std 1003.1) al igual que la C
sintaxis está especificada por ISO 9899.
Enesa especificación, notarás que read
requiere un argumento de nombre de variable. El comportamiento cuando lo omites esno especificadoy en la práctica varían con la sh
implementación del intérprete.
bash
es el sh
intérprete GNU, al igual gcc
que el compilador GNU C. Ambos bash
y gcc
tienen extensiones sobre lo que especifican esos estándares.
En el caso de read
, bash
lo trata read -r
como si lo fuera IFS= read -r REPLY
. En la especificación POSIX, lee la entrada estándar hasta que se alcanza IFS= read -r REPLY
un carácter o el final de la entrada y almacena los caracteres leídos en la variable y regresa con un\n
$REPLY
éxitoestado de salida si se leyó un carácter de nueva línea (una línea completa) ofallade lo contrario (como EOF antes de la nueva línea) y deja el comportamiento indefinido si los datos leídos contienen caracteres NUL o secuencias de bytes que no forman caracteres válidos.
En el caso de bash
, almacenará los bytes leídos incluso si no forman caracteres válidos y eliminará los caracteres NUL.
read -r
es como read -r REPLY
en ksh
or zsh
e informa un error en shells tipo POSIX basados en yash
or .ash
El comportamiento de echo
no está especificado a menos que sus argumentos no contengan caracteres de barra invertida y el primero no sea -n
.
Entonces, para resumir, a menos que conozca la sh
implementación (y la versión) particular con la que está tratando, no podrá saber qué
while read -r; do
echo "$REPLY" >> "$temp_file"
done
servirá. En el caso bash
específico, almacenará stdin en temp_file solo siempre que los datos no contengan caracteres NUL, terminen en un carácter de nueva línea y ninguna de las líneas coincida con la ^-[neE]+$
expresión regular extendida (y/o dependiendo del entorno o (cómo bash
se compiló como sh
OS/X, no contiene caracteres de barra invertida).
Es tambiénmuy ineficiente y no es la forma en que procesas el texto en shells.
Aquí quieres:
cat > "$temp_file"
cat
es uncomando estándar, que cuando no se le da ningún argumento simplemente descarga su entrada estándar en su salida estándarcomo es.