Cómo leer un archivo en una variable en shell?


Quiero leer un archivo y guardarlo en variable, pero necesito mantener la variable y no solo imprimir el archivo. ¿Cómo puedo hacer esto? He escrito este guion 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 un parámetro, por lo que, si el archivo contiene "aaaa", por ejemplo, imprimiría esto:

aaaa
11111-----

Pero esto solo imprime el archivo en la pantalla, ¡y quiero guardarlo en una variable! Hay una manera fácil de hacer esto?

 334
Author: Martin Tournoij, 2011-09-15

5 answers

En multiplataforma, el mínimo común denominador sh se utiliza:

#!/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 zshpara sorber un archivo sería considerado un Uso inútil de Cat.

Tenga en cuenta que no es necesario citar la sustitución de órdenes para preservar las líneas nuevas.

Ver: Bash Hacker's Wiki - Command substitution - Specialities.

 744
Author: Alan Gutierrez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-15 19:19:55

Si desea leer el archivo completo 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
 69
Author: brain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-15 07:34:19

Dos escollos importantes

Que fueron ignoradas por otras respuestas hasta ahora:

  1. Eliminación de la nueva línea final de la expansión de comandos
  2. Eliminación de caracteres NUL

Eliminación de la nueva línea final de la expansión de comandos

Este es un problema para:

value="$(cat config.txt)"

Soluciones de tipo, pero no para soluciones basadas en read.

La expansión de comandos elimina el final nuevas líneas:

S="$(printf "a\n")"
printf "$S" | od -tx1

Salidas:

0000000 61
0000001

Esto rompe el método ingenuo de lectura de archivos:

FILE="$(mktemp)"
printf "a\n\n" > "$FILE"
S="$(<"$FILE")"
printf "$S" | od -tx1
rm "$FILE"

Solución alternativa POSIX: agregue un carácter adicional a la expansión de comandos y elimínelo más tarde:

S="$(cat $FILE; printf a)"
S="${S%a}"
printf "$S" | od -tx1

Salidas:

0000000 61 0a 0a
0000003

Solución casi POSIX: codificación ASCII. Véase más adelante.

Eliminación de caracteres NUL

No hay una forma de Bash sana para almacenar caracteres NUL en variables.

Esto afecta tanto la expansión como read soluciones, y no conozco ninguna buena solución 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. Véase más adelante.

  • Use 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

Almacenar una versión codificada uuencode base64 de el archivo en la variable, y decodificar 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 en Ubuntu 12.04 por defecto (paquetesharutils)... No veo una alternativa POSIX 7 para la extensión de sustitución del proceso bash <() excepto escribir 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.

 58
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:37

Como Ciro Santilli señala el uso de sustituciones de comandos soltará nuevas líneas finales. Su solución alternativa es agregar caracteres finales, pero después de usarlo durante bastante tiempo decidí que necesitaba una solución que no usara la sustitución de comandos en absoluto.

Mi enfoque ahora usa read junto con los componentes printf -v flag 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 soporta entradas con o sin trailing nuevas líneas.

Ejemplo de uso:

$ read_input file_contents < /tmp/file
# $file_contents now contains the contents of /tmp/file
 1
Author: dimo414,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-26 00:36:08

Esto funciona para mí: v=$(cat <file_path>) echo $v

 0
Author: angelo.mastro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-30 22:46:54