parametros - leer la primera linea de un archivo bash
¿Cómo leer desde un archivo o stdin en Bash? (13)
En Perl, el siguiente código se leerá desde el archivo especificado en la línea de comando args o desde stdin:
while (<>) {
print($_);
}
Esto es muy conveniente Solo quiero saber cuál es la forma más sencilla de leer desde un archivo o stdin en bash.
Aquí está la manera más simple:
#!/bin/sh
cat -
Uso:
$ echo test | sh my_script.sh
test
Para asignar stdin a la variable, puede usar: STDIN=$(cat -)
o simplemente STDIN=$(cat)
ya que el operador no es necesario (según @ mklement0 comment ).
Para analizar cada línea desde la entrada estándar , pruebe el siguiente script:
#!/bin/bash
while IFS= read -r line; do
printf ''%s/n'' "$line"
done
Para leer desde el archivo o stdin (si el argumento no está presente), puede extenderlo a:
#!/bin/bash
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
printf ''%s/n'' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
Notas:
-
read -r
- No trates una barra invertida de ninguna manera especial. Considere que cada barra invertida es parte de la línea de entrada.- Sin configurar
IFS
, por defecto las secuencias de espacio y tabulación al principio y al final de las líneas se ignoran (recortan).- Use
printf
lugar deecho
para evitar imprimir líneas vacías cuando la línea consiste en una sola-e
,-n
o-E
. Sin embargo, hay una solución mediante el uso deenv POSIXLY_CORRECT=1 echo "$line"
que ejecuta suecho
GNU externo que lo admite. Ver: ¿Cómo echo "-e"?
Ver: ¿Cómo leer stdin cuando no se pasan los argumentos? en SE
Con más precisión...
while IFS= read -r line ; do
printf "%s/n" "$line"
done < file
Creo que esta es la manera directa:
$ cat reader.sh
#!/bin/bash
while read line; do
echo "reading: ${line}"
done < /dev/stdin
-
$ cat writer.sh
#!/bin/bash
for i in {0..5}; do
echo "line ${i}"
done
-
$ ./writer.sh | ./reader.sh
reading: line 0
reading: line 1
reading: line 2
reading: line 3
reading: line 4
reading: line 5
El código ${1:-/dev/stdin}
solo comprenderá el primer argumento, entonces, ¿qué tal esto?
ARGS=''$*''
if [ -z "$*" ]; then
ARGS=''-''
fi
eval "cat -- $ARGS" | while read line
do
echo "$line"
done
El ciclo de Perl en la pregunta se lee de todos los argumentos de nombre de archivo en la línea de comando, o de la entrada estándar si no se especifican archivos. Las respuestas que veo parecen procesar un único archivo o entrada estándar si no hay ningún archivo especificado.
Aunque a menudo se ridiculiza con precisión como UUOC (Uso inútil del cat
), hay momentos en que el cat
es la mejor herramienta para el trabajo, y es discutible que este sea uno de ellos:
cat "$@" |
while read -r line
do
echo "$line"
done
El único inconveniente de esto es que crea una tubería que se ejecuta en un subconjunto, por lo que las asignaciones variables en el ciclo while no son accesibles fuera de la interconexión. El camino bash
es la Sustitución de procesos :
while read -r line
do
echo "$line"
done < <(cat "$@")
Esto deja el bucle while ejecutándose en el shell principal, por lo que las variables establecidas en el bucle son accesibles fuera del bucle.
El comportamiento de Perl, con el código dado en el PO, no puede tomar ninguno o varios argumentos, y si un argumento es un solo guión -
esto se entiende como stdin. Además, siempre es posible tener el nombre de archivo con $ARGV
. Ninguna de las respuestas dadas hasta ahora realmente imita el comportamiento de Perl en estos aspectos. Aquí hay una posibilidad pura de Bash. El truco es usar el exec
apropiadamente.
#!/bin/bash
(($#)) || set -- -
while (($#)); do
{ [[ $1 = - ]] || exec < "$1"; } &&
while read -r; do
printf ''%s/n'' "$REPLY"
done
shift
done
El nombre de archivo está disponible en $1
.
Si no se dan argumentos, establecemos artificialmente -
como el primer parámetro posicional. Luego hacemos un bucle en los parámetros. Si un parámetro no es -
, redirigimos la entrada estándar de nombre de archivo con exec
. Si esta redirección tiene éxito, hacemos un bucle con un ciclo while. Estoy usando la variable estándar REPLY
, y en este caso no necesita reiniciar IFS
. Si desea otro nombre, debe restablecer IFS
como tal (a menos que, por supuesto, no lo desee y sepa lo que está haciendo):
while IFS= read -r line; do
printf ''%s/n'' "$line"
done
La siguiente solución se lee de un archivo si se llama al script con un nombre de archivo como el primer parámetro $1
contrario, desde la entrada estándar.
while read line
do
echo "$line"
done < "${1:-/dev/stdin}"
La sustitución ${1:-...}
toma $1
si se define, de lo contrario se utiliza el nombre de archivo de la entrada estándar del proceso propio.
La solución de echo
agrega nuevas líneas cada vez que IFS
rompe la secuencia de entrada. La respuesta de @ fgm se puede modificar un poco:
cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"
Lo siguiente funciona con sh
estándar (probado con dash
en Debian) y es bastante legible, pero eso es una cuestión de gusto:
if [ -n "$1" ]; then
cat "$1"
else
cat
fi | commands_and_transformations
Detalles: si el primer parámetro no está vacío, cat
ese archivo, de lo contrario, ingrese la entrada estándar. Entonces, la salida de la sentencia if
completa es procesada por los commands_and_transformations
.
No encuentro ninguna de estas respuestas aceptable. En particular, la respuesta aceptada solo maneja el primer parámetro de línea de comando e ignora el resto. El programa Perl que está tratando de emular maneja todos los parámetros de línea de comando. Entonces la respuesta aceptada ni siquiera responde la pregunta. Otras respuestas usan extensiones de bash, agregan comandos ''cat'' innecesarios, solo funcionan para el caso simple de hacer eco de entrada a salida, o simplemente son innecesariamente complicados.
Sin embargo, tengo que darles un poco de crédito porque me dieron algunas ideas. Aquí está la respuesta completa:
#!/bin/sh
if [ $# = 0 ]
then
DEFAULT_INPUT_FILE=/dev/stdin
else
DEFAULT_INPUT_FILE=
fi
# Iterates over all parameters or /dev/stdin
for FILE in "$@" $DEFAULT_INPUT_FILE
do
while IFS= read -r LINE
do
# Do whatever you want with LINE here.
echo $LINE
done < "$FILE"
done
Por favor intente con el siguiente código:
while IFS= read -r line; do
echo "$line"
done < file
Qué tal si
for line in `cat`; do
something($line);
done
Quizás la solución más simple es redirigir stdin con un operador de redirección de fusión:
#!/bin/bash
less <&0
Stdin es el descriptor de archivo cero. Lo anterior envía la entrada canalizada a tu script bash en el stdin de less.
Obtenga más información sobre la redirección del descriptor de archivo .