it-swarm.com.de

Wie kann man eine Datei in eine Variable in der Shell einlesen?

Ich möchte eine Datei lesen und in einer Variablen speichern, aber ich muss die Variable behalten und nicht nur die Datei ausdrucken. Wie kann ich das tun? Ich habe dieses Skript geschrieben, aber es ist nicht das, was ich brauchte:

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

In meinem Skript kann ich den Dateinamen als Parameter angeben. Wenn die Datei beispielsweise "aaaa" enthält, würde sie Folgendes ausgeben:

aaaa
11111-----

Aber dies druckt die Datei einfach auf dem Bildschirm aus und ich möchte sie in einer Variablen speichern! Gibt es eine einfache Möglichkeit, dies zu tun?

369
kaka

In plattformübergreifenden, kleinsten gemeinsamen Nennern sh verwenden Sie:

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

In bash oder zsh können Sie eine ganze Datei in eine Variable einlesen, ohne cat aufzurufen:

#!/bin/bash
value=$(<config.txt)
echo "$value"

Das Aufrufen von cat in bash oder zsh zum Slurp einer Datei würde als nutzlose Verwendung von Cat betrachtet.

Beachten Sie, dass Sie die Befehlsersetzung nicht angeben müssen, um die Zeilenumbrüche zu erhalten.

Siehe: Bash Hackers Wiki - Befehlsersetzung - Spezialitäten .

817
Alan Gutierrez

Wenn Sie die gesamte Datei in eine Variable einlesen möchten:

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

Wenn Sie es Zeile für Zeile lesen möchten:

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

Zwei wichtige Fallstricke

die bisher von anderen Antworten ignoriert wurden:

  1. Nachlaufende Entfernung von Befehlen aus der Befehlserweiterung
  2. NUL-Zeichenentfernung

Nachlaufende Zeilenumbrüche aus der Befehlserweiterung entfernen

Dies ist ein Problem für die:

value="$(cat config.txt)"

typlösungen, jedoch nicht für read-basierte Lösungen.

Befehlserweiterung entfernt nachfolgende Zeilenumbrüche:

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

Ausgänge:

0000000 61
0000001

Dies bricht die naive Methode des Lesens aus Dateien:

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

POSIX-Problemumgehung: Fügen Sie der Befehlserweiterung ein zusätzliches Zeichen hinzu und entfernen Sie es später:

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

Ausgänge:

0000000 61 0a 0a
0000003

Fast POSIX-Problemumgehung: Kodierung ASCII. Siehe unten.

NUL-Zeichenentfernung

Es gibt keine vernünftige Bash-Methode zum Speichern von NUL-Zeichen in Variablen .

Dies betrifft sowohl Erweiterungs- als auch read-Lösungen, und ich kenne keine gute Lösung dafür.

Beispiel:

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

Ausgänge:

0000000 61 00 62
0000003

0000000 61 62
0000002

Ha, unsere NUL ist weg!

Problemumgehungen:

  • ASCII-Kodierung Siehe unten.

  • bash extension $"" literals verwenden:

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

    Funktioniert nur für Literale und eignet sich daher nicht zum Lesen von Dateien.

Workaround für die Fallstricke

Speichern Sie eine mit uuencode base64 codierte Version der Datei in der Variablen und dekodieren Sie sie vor jeder Verwendung:

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

Ausgabe:

0000000 61 00 0a
0000003

uuencode und udecode sind POSIX 7 aber nicht standardmäßig in Ubuntu 12.04 (sharutils-Paket) ... Ich sehe keine POSIX 7-Alternative für die Bash-Prozess-Erweiterung <(), es sei denn, in eine andere Datei wird geschrieben ...

Natürlich ist dies langsam und unpraktisch. Ich denke, die eigentliche Antwort lautet: Verwenden Sie Bash nicht, wenn die Eingabedatei NUL-Zeichen enthält.

das funktioniert für mich: v=$(cat <file_path>) echo $v

2
angelo.mastro

Wie in Ciro Santilli bemerkt, werden , die Befehlssubstitutionen verwenden, nachfolgende Zeilenumbrüche löschen. Ihre Problemumgehung durch Hinzufügen von nachgestellten Charakteren ist großartig, aber nachdem ich sie längere Zeit verwendet hatte, entschied ich, dass ich eine Lösung brauchte, die überhaupt keine Befehlsersetzung verwendete.

My approach verwendet jetzt read zusammen mit dem -v-Flag von printf builtin, um den Inhalt von stdin direkt in eine Variable zu lesen.

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

Dies unterstützt Eingaben mit oder ohne nachstehende Zeilenumbrüche.

Verwendungsbeispiel:

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