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?
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 .
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
Zwei wichtige Fallstricke
die bisher von anderen Antworten ignoriert wurden:
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
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