shell - programas - scripts linux ejercicios resueltos
¿Cómo leer un archivo en una variable en shell? (5)
Quiero leer un archivo y guardarlo en una variable, pero necesito mantener la variable y no solo imprimir el archivo. ¿Cómo puedo hacer esto? He escrito este guión pero no es exactamente lo que necesitaba:
#!/bin/sh
while read LINE
do
echo $LINE
done <$1
echo 11111-----------
echo $LINE
En mi script, puedo dar el nombre del archivo como parámetro, por lo tanto, si el archivo contiene "aaaa", por ejemplo, imprimirá esto:
aaaa
11111-----
¡Pero esto solo imprime el archivo en la pantalla, y quiero guardarlo en una variable! ¿Hay una forma fácil de hacer esto?
Como señala Ciro Santilli utilizando sustituciones de comandos, se eliminarán las nuevas líneas. Su solución para agregar caracteres finales es genial, pero después de usarla durante bastante tiempo, decidí que necesitaba una solución que no usara la sustitución de comandos.
Mi enfoque ahora usa read
junto con el printf
printv builtin -v
para leer el contenido de stdin directamente en una variable.
# Reads stdin into a variable, accounting for trailing newlines. Avoids needing a subshell or
# command substitution.
read_input() {
# Use unusual variable names to avoid colliding with a variable name
# the user might pass in (notably "contents")
: "${1:?Must provide a variable to read into}"
if [[ "$1" == ''_line'' || "$1" == ''_contents'' ]]; then
echo "Cannot store contents to $1, use a different name." >&2
return 1
fi
local _line _contents
while read -r _line; do
_contents="${_contents}${_line}"$''/n''
done
_contents="${_contents}${_line}" # capture any content after the last newline
printf -v "$1" ''%s'' "$_contents"
}
Esto admite entradas con o sin nuevas líneas finales.
Ejemplo de uso:
$ read_input file_contents < /tmp/file
# $file_contents now contains the contents of /tmp/file
En multiplataforma, el mínimo común denominador usará:
#!/bin/sh
value=`cat config.txt`
echo "$value"
En bash
o zsh
, para leer un archivo completo en una variable sin invocar cat
:
#!/bin/bash
value=$(<config.txt)
echo "$value"
Invocar cat
en bash
o zsh
para sorber un archivo se consideraría un uso inútil de cat .
Tenga en cuenta que no es necesario citar la sustitución de comandos para conservar las nuevas líneas.
Ver: Wiki de Bash Hacker - Sustitución de comandos - Especialidades .
Si quieres leer todo el archivo en una variable:
#!/bin/bash
value=`cat sources.xml`
echo $value
Si quieres leerlo línea por línea:
while read line; do
echo $line
done < file.txt
esto funciona para mí: v=$(cat <file_path>) echo $v
Dos escollos importantes
que fueron ignoradas por otras respuestas hasta el momento:
- Remolque eliminación de nueva línea de la expansión de comando
- Eliminación de caracteres NUL
Remolque eliminación de nueva línea de la expansión de comando
Este es un problema para el:
value="$(cat config.txt)"
Soluciones tipo, pero no para soluciones basadas en read
.
La expansión de comandos elimina las nuevas líneas finales:
S="$(printf "a/n")"
printf "$S" | od -tx1
Salidas:
0000000 61
0000001
Esto rompe el ingenuo método de leer archivos:
FILE="$(mktemp)"
printf "a/n/n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"
Solución POSIX: agregue un carácter adicional a la expansión del comando y elimínelo más tarde:
S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1
Salidas:
0000000 61 0a 0a
0000003
Casi la solución POSIX: codificación ASCII. Vea abajo.
Eliminación de caracteres NUL
No hay una forma sensata de Bash para almacenar caracteres NUL en variables .
Esto afecta tanto a las soluciones de expansión como de read
, y no conozco ninguna solución adecuada para ello.
Ejemplo:
printf "a/0b" | od -tx1
S="$(printf "a/0b")"
printf "$S" | od -tx1
Salidas:
0000000 61 00 62
0000003
0000000 61 62
0000002
Ha, nuestro NUL se ha ido!
Soluciones:
Codificación ASCII. Vea abajo.
usar la extensión bash
$""
literales:S=$"a/0b" printf "$S" | od -tx1
Solo funciona para literales, por lo que no es útil para leer desde archivos.
Solución para las trampas
Almacene una versión codificada en uuencode base64 del archivo en la variable, y descodifique antes de cada uso:
FILE="$(mktemp)"
printf "a/0/n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"
Salida:
0000000 61 00 0a
0000003
uuencode y udecode son POSIX 7 pero no están en Ubuntu 12.04 por defecto (paquete sharutils
) ... No veo una alternativa POSIX 7 para la extensión de sustitución del proceso bash <()
excepto la escritura en otro archivo ...
Por supuesto, esto es lento e inconveniente, así que supongo que la respuesta real es: no use Bash si el archivo de entrada puede contener caracteres NUL.