Come leggere un file in una variabile nella shell?

Voglio leggere un file e salvarlo in variabile, ma ho bisogno di mantenere la variabile e non solo stampare il file. Come posso fare questo? Ho scritto questo script ma non è proprio quello di cui avevo bisogno:

#!/bin/sh while read LINE do echo $LINE done <$1 echo 11111----------- echo $LINE 

Nel mio script, posso dare il nome del file come parametro, quindi, se il file contiene “aaaa”, ad esempio, verrà stampato questo:

 aaaa 11111----- 

Ma questo stampa il file sullo schermo e voglio salvarlo in una variabile! C’è un modo semplice per farlo?

In multipiattaforma, il minimo comune denominatore, usi:

 #!/bin/sh value=`cat config.txt` echo "$value" 

In bash o in zsh , per leggere un intero file in una variabile senza invocare il cat :

 #!/bin/bash value=$( 

Invocare il cat in bash o in zsh per estrarre un file sarebbe considerato un uso inutile di Cat .

Si noti che non è necessario citare la sostituzione di comando per preservare le newline.

Vedi: Wiki di Bash Hacker - Sostituzione del comando - Specialità .

Se vuoi leggere l’intero file in una variabile:

 #!/bin/bash value=`cat sources.xml` echo $value 

Se vuoi leggerlo riga per riga:

 while read line; do echo $line done < file.txt 

Due importanti insidie

che sono stati ignorati da altre risposte finora:

  1. Eliminazione finale di nuova riga dall’espansione del comando
  2. Rimozione del personaggio NUL

Eliminazione finale di nuova riga dall’espansione del comando

Questo è un problema per:

 value="$(cat config.txt)" 

digitare le soluzioni, ma non per le soluzioni basate sulla read .

L’espansione dei comandi rimuove le nuove righe finali:

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

Uscite:

 0000000 61 0000001 

Questo rompe il metodo ingenuo di leggere dai file:

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

Soluzione POSIX: aggiungi un extra char all'espansione del comando e rimuovilo in un secondo momento:

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

Uscite:

 0000000 61 0a 0a 0000003 

Soluzione quasi POSIX: codifica ASCII. Vedi sotto.

Rimozione del personaggio NUL

Non esiste un modo Bash corretto per memorizzare i caratteri NUL nelle variabili .

Ciò riguarda sia l'espansione che le soluzioni di read e non conosco alcuna soluzione valida per questo.

Esempio:

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

Uscite:

 0000000 61 00 62 0000003 0000000 61 62 0000002 

Ah, il nostro NUL è sparito!

soluzioni alternative:

  • Codifica ASCII. Vedi sotto.

  • usa l'estensione bash $"" letterali:

     S=$"a\0b" printf "$S" | od -tx1 

    Funziona solo per i letterali, quindi non è utile per leggere i file.

Soluzione per le insidie

Memorizza una versione codificata in base al codice uuencode del file nella variabile e decodifica prima di ogni utilizzo:

 FILE="$(mktemp)" printf "a\0\n" > "$FILE" S="$(uuencode -m "$FILE" /dev/stdout)" uudecode -o /dev/stdout < (printf "$S") | od -tx1 rm "$FILE" 

Produzione:

 0000000 61 00 0a 0000003 

uuencode e udecode sono POSIX 7 ma non in Ubuntu 12.04 per impostazione predefinita (pacchetto sharutils ) ... Non vedo un'alternativa POSIX 7 per l'estensione di sostituzione del processo bash < () tranne la scrittura in un altro file ...

Naturalmente, questo è lento e scomodo, quindi suppongo che la vera risposta sia: non usare Bash se il file di input può contenere caratteri NUL.

Come nota Ciro Santilli usando le sostituzioni di comando, i newline finali saranno eliminati. La loro soluzione alternativa per aggiungere caratteri finali è ottima, ma dopo averla utilizzata per un po ‘di tempo ho deciso che avevo bisogno di una soluzione che non usasse affatto la sostituzione dei comandi.

Il mio approccio ora viene read insieme al flag -v printf incorporato per leggere il contenuto di stdin direttamente in una variabile.

 # 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" } 

Questo supporta gli input con o senza i newline finali.

Esempio di utilizzo:

 $ read_input file_contents < /tmp/file # $file_contents now contains the contents of /tmp/file 

questo funziona per me: v=$(cat ) echo $v