Gibt es einen Unix-Befehl, um einer Textdatei einige Zeichenfolgendaten voranzustellen?
Etwas wie:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
Gibt es einen Unix-Befehl, um einer Textdatei einige Zeichenfolgendaten voranzustellen?
Etwas wie:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
Antworten:
sed -i.old '1s;^;to be prepended;' inFile
-i
schreibt die Änderung an Ort und Stelle und erstellt ein Backup, wenn eine Erweiterung angegeben wird. (In diesem Fall .old
)1s;^;to be prepended;
Ersetzt den Anfang der ersten Zeile durch die angegebene Ersetzungszeichenfolge und verwendet sie ;
als Befehlsbegrenzer.\n
nach dem Voranstellen einfügen . je nach ihren Bedürfnissen. Ordentliche Lösung.
inFile
Include-Verzeichnisse in ihren Pfad aufgenommen werden?
\n
. Hätte zuerst die Kommentare lesen sollen. Verdammt.
'1s;^;my-prepended-line\;\n;'
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';
und dies bringt es durcheinander, wenn es das \t
und konvertiert \n
.
echo "to be prepended"$'\n'"$(cat text.txt)"
Ich bin überrascht, dass niemand dies erwähnt hat.
cat <(echo "before") text.txt > newfile.txt
Das ist wohl natürlicher als die akzeptierte Antwort (etwas zu drucken und in einen Substitutionsbefehl zu leiten, ist lexikographisch kontraintuitiv).
... und entführen, was Ryan oben gesagt hat, sponge
ohne dass Sie eine temporäre Datei benötigen:
sudo apt-get install moreutils
<<(echo "to be prepended") < text.txt | sponge text.txt
EDIT: Sieht so aus, als würde dies in Bourne Shell nicht funktionieren /bin/sh
Mit einem Here-String - <<<
können Sie Folgendes tun:
<<< "to be prepended" < text.txt | sponge text.txt
<<(echo "to be prepended") < text.txt
und <<< "to be prepended" < text.txt
Konstruktionen funktionieren nicht in Bash; sie benötigen zsh.
Dies ist eine Möglichkeit:
(echo "to be prepended"; cat text.txt) > newfile.txt
Sie werden wahrscheinlich nicht leicht um eine Zwischendatei herumkommen.
Alternativen (können umständlich sein, wenn die Schale entweicht):
sed -i '0,/^/s//to be prepended/' text.txt
cat <(echo "to be prepended") text.txt > newfile.txt
. Wenn ich darüber nachdenke, bin ich mir nicht sicher, ob meine verwandt ist, also poste ich eine separate Antwort.
Dies wird funktionieren, um die Ausgabe zu bilden. Das - bedeutet Standardeingang, der über die Pipe vom Echo bereitgestellt wird.
echo -e "to be prepended \n another line" | cat - text.txt
Zum Umschreiben der Datei ist eine temporäre Datei erforderlich, die nicht in die Eingabedatei zurückgeleitet werden kann.
echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt
text.txt
wie gewünscht vorangestellt, sondern auf stdout angezeigt. Die Ausgabe könnte in eine andere Datei geleitet werden, wenn dies für das OP funktioniert
Bevorzugen Sie Adams Antwort
Wir können die Verwendung von Schwamm vereinfachen . Jetzt müssen wir keine temporäre Datei erstellen und umbenennen durch
echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt
Hinweis:
Dies kann unerwartete Nebenwirkungen haben, insbesondere das Ersetzen eines Symlinks durch eine reguläre Datei, das Erhalten unterschiedlicher Berechtigungen für die Datei und das Ändern des Erstellungsdatums (des Geburtsdatums) der Datei.
sed -i
Wie in der Antwort von Prince John Wesley wird versucht, zumindest die ursprünglichen Berechtigungen wiederherzustellen, es gelten jedoch auch die anderen Einschränkungen.
Hier ist eine einfache Alternative, bei der eine temporäre Datei verwendet wird (es wird vermieden, dass die gesamte Eingabedatei wie bei der Shime-Lösung in den Speicher eingelesen wird):
{ printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt
Die Verwendung eines Gruppenbefehls ( { ...; ...; }
) ist etwas effizienter als die Verwendung einer Unterschale ( (...; ...)
), wie in der Lösung von 0xC0000022L .
Die Vorteile sind:
Es ist einfach zu steuern, ob der neue Text direkt der ersten Zeile vorangestellt oder als neue Zeile (n) eingefügt werden soll (einfach \n
an das printf
Argument anhängen ).
Im Gegensatz zur sed
Lösung funktioniert es, wenn die Eingabedatei leer ist ( 0
Bytes).
Die sed
Lösung kann vereinfacht werden, wenn dem vorhandenen Inhalt eine oder mehrere ganze Zeilen vorangestellt werden sollen (vorausgesetzt, die Eingabedatei ist nicht leer):
sed
's i
Funktion fügt ganze Zeilen ein:
Mit GNU sed
:
# Prepends 'to be prepended' *followed by a newline*, i.e. inserts a new line.
# To prepend multiple lines, use '\n' as part of the text.
# -i.old creates a backup of the input file with extension '.old'
sed -i.old '1 i\to be prepended' inFile
Eine tragbare Variante, die auch mit macOS / BSD funktioniert sed
:
# Prepends 'to be prepended' *followed by a newline*
# To prepend multiple lines, escape the ends of intermediate
# lines with '\'
sed -i.old -e '1 i\
to be prepended' inFile
Beachten Sie, dass der wörtliche Zeilenumbruch nach dem \
erforderlich ist.
Verwenden des ehrwürdigen ed
POSIX-Dienstprogramms :
Hinweis:
ed
Liest immer zuerst die gesamte Eingabedatei in den Speicher.So stellen Sie direkt in die erste Zeile voran (wie bei sed
funktioniert dies nicht, wenn die Eingabedatei vollständig leer ist ( 0
Bytes)):
ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
-s
ed
Statusmeldungen von unterdrückt .ed
als mehrzeiliges here-document ( <<EOF\n...\nEOF
) bereitgestellt werden , dh über stdin ; Standardmäßig wird in solchen Dokumenten eine Zeichenfolgenerweiterung durchgeführt (Shell-Variablen werden interpoliert). zitieren Sie den Eröffnungsbegrenzer, um dies zu unterdrücken (z <<'EOF'
. B. ).1
macht die 1. Zeile zur aktuellen Zeiles
führt eine Regex-basierte Zeichenfolgenersetzung in der aktuellen Zeile durch, wie in sed
; Sie können wörtliche Zeilenumbrüche in den Ersetzungstext aufnehmen, diese müssen jedoch \
umgangen werden.w
schreibt in die Eingabedatei das Ergebnis zurück (für die Prüfung, ersetzen w
mit ,p
nur drucken das Ergebnis, ohne die Eingabedatei zu verändern).So stellen Sie eine oder mehrere ganze Zeilen voran :
Wie bei fügt sed
die i
Funktion dem einzufügenden Text immer eine nachgestellte neue Zeile hinzu.
ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
0 i
macht 0
(den Anfang der Datei) zur aktuellen Zeile und startet den Einfügemodus ( i
); Beachten Sie, dass die Zeilennummern ansonsten 1
basieren..
einer eigenen Zeile abgeschlossen wird.Wahrscheinlich ist nichts eingebaut, aber Sie könnten ganz einfach Ihre eigenen schreiben:
#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"
Zumindest so etwas ...
$$
ist jedoch für Produktionscode nicht ausreichend (Google Symlink-Angriff). aber es ist sicherlich besser als ein statischer Dateiname.
mktemp
Unter bestimmten Umständen ist vorangestellter Text möglicherweise nur bei stdin verfügbar. Dann soll diese Kombination funktionieren.
echo "to be prepended" | cat - text.txt | tee text.txt
Wenn Sie die tee
Ausgabe weglassen möchten , fügen Sie sie hinzu > /dev/null
.
tee
die nicht verstopft, text.txt
bevor cat
man sie lesen kann? Ich denke nicht - was diese Lösung für die Gesundheit der Datei gefährlich machen würde.
lenA=1000000; yes a | head -c $lenA > a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt > /dev/null
. Wenn lenA
1000000 (1 MB Datei) und lenB
10000 (10 KB Text vorangestellt) ist, wird die Datei "a.txt" mit 20 KB "b" Buchstaben überschrieben. Das ist total kaputt. Wenn Sie jetzt 1 MB a.txt und 1 MB Text zum Voranstellen verwenden und tee
in eine Schleife gehen , die eine 7 GB + -Datei generiert, musste ich den Befehl stoppen. Es ist also offensichtlich, dass das Ergebnis für große Größen nicht vorhersehbar ist. Ich habe keine Informationen, ob es bei kleinen Größen funktionieren soll.
Lösung:
printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt
Beachten Sie, dass dies für alle Arten von Eingaben sicher ist, da keine Erweiterungen vorhanden sind. Wenn Sie beispielsweise voranstellen möchten, !@#$%^&*()ugly text\n\t\n
funktioniert dies nur:
printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt
Der letzte Teil, der berücksichtigt werden muss, ist das Entfernen von Leerzeichen am Ende der Datei während der Befehlsersetzung "$(cat file.txt)"
. Alle Workarounds hierfür sind relativ komplex. Wenn Sie Zeilenumbrüche am Ende von file.txt beibehalten möchten, lesen Sie Folgendes: https://stackoverflow.com/a/22607352/1091436
file.txt
größer ist, als in die Argumentliste aufgenommen werden kann printf
.
Ein anderer Weg mit sed
:
sed -i.old '1 {i to be prepended
}' inFile
Wenn die vorangestellte Zeile mehrzeilig ist:
sed -i.old '1 {i\
to be prepended\
multiline
}' inFile
sed -i '1ito be prepended' inFile
- oder ist es nur für GNU sed erlaubt?
sed
den i
Befehl ein Backslash und eine Newline, und jede Eingabezeile mit Ausnahme der letzten endet ebenfalls mit einem Backslash. GNU sed
erlaubt Abkürzungen in der Richtung, nach der Sie fragen, aber nur GNU sed
tut dies. '
Wie in Bash (in Ubuntu) getestet, wenn mit einer Testdatei über begonnen wird;
echo "Original Line" > test_file.txt
Sie können ausführen;
echo "$(echo "New Line"; cat test_file.txt)" > test_file.txt
oder wenn die Version von bash für $ () zu alt ist, können Sie Backticks verwenden;
echo "`echo "New Line"; cat test_file.txt`" > test_file.txt
und den folgenden Inhalt von "test_file.txt" erhalten;
New Line
Original Line
Keine Zwischendatei, nur Bash / Echo.
Eine andere ziemlich einfache Lösung ist:
$ echo -e "string\n" $(cat file)
cat
Parametererweiterung nicht in doppelte Anführungszeichen zu setzen, ist ein ziemlich guter Grund für eine Abwertung . Dieser Code teilt den Inhalt der Datei in Wörter auf und übergibt jedes dieser Wörter als separates Argument an echo
. Sie verlieren die ursprünglichen Argumentgrenzen, Sie verlieren Zeilenumbrüche / Tabulatoren / etc. Und Zeichenfolgen wie \t
und \n
im Originaltext werden durch Tabulatoren und Zeilenumbrüche ersetzt, anstatt so zu bleiben, wie sie waren.
Wenn Sie vi / vim mögen, ist dies möglicherweise eher Ihr Stil.
printf '0i\n%s\n.\nwq\n' prepend-text | ed file
Ich würde empfehlen, eine Funktion zu definieren und diese dann zu importieren und bei Bedarf zu verwenden.
prepend_to_file() {
file=$1
text=$2
if ! [[ -f $file ]] then
touch $file
fi
echo "$text" | cat - $file > $file.new
mv -f $file.new $file
}
Dann benutze es so:
prepend_to_file test.txt "This is first"
prepend_to_file test.txt "This is second"
Ihr Dateiinhalt lautet dann:
This is second
This is first
Ich werde diesen Ansatz für die Implementierung eines Änderungsprotokoll-Updaters verwenden.