Unix-Befehl zum Voranstellen von Text an eine Datei


124

Gibt es einen Unix-Befehl, um einer Textdatei einige Zeichenfolgendaten voranzustellen?

Etwas wie:

prepend "to be prepended" text.txt

Suchen Sie nach einem einzelnen Befehl oder ist ein kleiner Skript- / Alias-Befehl in Ordnung?
Levon

2
Wenn Sie alle Antworten kombinieren, ist dies wahrscheinlich der kompakteste Weg:<<(echo "to be prepended") < text.txt | sponge text.txt
Sridhar Sarnobat

Antworten:


110
sed -i.old '1s;^;to be prepended;' inFile
  • -ischreibt 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.

19
Wenn Sie mit dem Befehl eine Erklärung hinzufügen könnten, wäre dies hilfreich für andere, die mit der Funktionsweise von sed nicht so vertraut sind. OP möchte möglicherweise \nnach dem Voranstellen einfügen . je nach ihren Bedürfnissen. Ordentliche Lösung.
Levon

Ja, eine Erklärung wäre großartig. Können die inFileInclude-Verzeichnisse in ihren Pfad aufgenommen werden?
Zakdances

3
@Levon Ich wollte unbedingt eine einfügen \n. Hätte zuerst die Kommentare lesen sollen. Verdammt.
Chishaku

In meinem Fall möchte ich ein '1s;^;my-prepended-line\;\n;'
Semikolon

5
Beachten Sie, dass '\ n' nur für GNU sed funktioniert, nicht für BSD (wie OSX, FreeBSD usw.). Weitere Informationen finden
jwd

162
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt

2
In einer Zeile und in der Originaltextdatei! Dies ist die richtige einfache Antwort. Ich schlage vor, die zusätzliche neue Zeile \ n echo -e "vorangestellt zu werden \ n $ (cat text.txt)"> text.txt
VincentPerrin.com

32
Diese Lösung hat Probleme ... sie konvertiert Vorkommen von "\ n" in tatsächliche neue Zeilen ... problematisch, wenn Sie einer Codedatei mit Zeichenfolgen voranstellen, die "\ n" enthalten.
Paul Go

1
Ich hatte dies in meiner Datei git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';und dies bringt es durcheinander, wenn es das \tund konvertiert \n.
HPPy

8
Dies (1) lädt die gesamte Datei in den Speicher und (2) klebt die gesamte Datei in ein einzelnes Befehlszeilenargument. Gut für einfache Sachen, aber Vorsicht vor den Einschränkungen.
JWD

2
Als letztes habe ich es versucht, ich glaube, dies würde bei wirklich großen Dateien brechen, bei denen cat nicht den gesamten Inhalt von text.txt lesen kann, bevor es überschrieben wird. Wenn die Option -e für Echo ein Problem darstellt, können Sie eine neue Zeile in bash oder doecho "to be prepended"$'\n'"$(cat text.txt)"
jbo5112

41

Prozesssubstitution

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, spongeohne 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


Hier String (nur zsh)

Mit einem Here-String - <<<können Sie Folgendes tun:

<<< "to be prepended" < text.txt | sponge text.txt

3
Diese Antwort ist der Gewinner für mich. Einfach und nur mit eingebauten Geräten erreichbar. Gute Arbeit!
PiersyP

1
@PiersyP Ich stimme zu! Das hat sehr geholfen, danke Sridhar-Sarnobat.

Das Problem ist, dass es 1 Datei gibt, nicht 2 Dateien. Die erste Zeile der Antwort funktioniert also nicht wirklich. Die letzte Zeile funktioniert möglicherweise (erfordert jedoch einen Schwamm).
VasiliNovikov

1
Was macht Schwamm?
Hemanta

1
Ihre <<(echo "to be prepended") < text.txtund <<< "to be prepended" < text.txtKonstruktionen funktionieren nicht in Bash; sie benötigen zsh.
Anders Kaseorg

31

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

Strategie 1) ist meiner Meinung nach die intuitivste aller Antworten. Und eine kleine Abweichung : 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.
Sridhar Sarnobat

1
Die erste Methode
behält

19

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

2
Dadurch wird der Datei der Datei nicht text.txtwie gewünscht vorangestellt, sondern auf stdout angezeigt. Die Ausgabe könnte in eine andere Datei geleitet werden, wenn dies für das OP funktioniert
Levon

1
Dies würde definitiv das "vorangestellte" und dann den Inhalt von "text.txt" drucken. Aber ich möchte, dass der Text der Datei vorangestellt wird
Eins Zwei Drei

1
und was ist, wenn ich mehrzeiligen Text habe? Wie füge ich das Zeilenumbruchzeichen ein?
Eins Zwei Drei

Das wäre cool, aber Bash mag es nicht: $ echo RewriteBase / | cat - web / .htaccess> web / .htaccess cat: web / .htaccess: Eingabedatei ist Ausgabedatei
geek-merlin

14

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

Dies war die einzige Lösung, die für mich funktionierte. Es ist lustig genug, dass der Name meines Servers SpongeBob ist.
Ömer An

9

Wenn es akzeptabel ist, die Eingabedatei zu ersetzen :

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 -iWie 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 \nan das printfArgument anhängen ).

  • Im Gegensatz zur sedLösung funktioniert es, wenn die Eingabedatei leer ist ( 0Bytes).


Die sedLösung kann vereinfacht werden, wenn dem vorhandenen Inhalt eine oder mehrere ganze Zeilen vorangestellt werden sollen (vorausgesetzt, die Eingabedatei ist nicht leer):

sed's iFunktion 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.


Wenn die Eingabedatei an Ort und Stelle bearbeitet werden muss (wobei der Inode mit all seinen Attributen beibehalten wird):

Verwenden des ehrwürdigen edPOSIX-Dienstprogramms :

Hinweis:

  • edLiest immer zuerst die gesamte Eingabedatei in den Speicher.

So stellen Sie direkt in die erste Zeile voran (wie bei sedfunktioniert dies nicht, wenn die Eingabedatei vollständig leer ist ( 0Bytes)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -sedStatusmeldungen von unterdrückt .
  • Beachten Sie, wie die Befehle edals 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 Zeile
  • Die Funktion sfü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.
  • wschreibt in die Eingabedatei das Ergebnis zurück (für die Prüfung, ersetzen wmit ,pnur drucken das Ergebnis, ohne die Eingabedatei zu verändern).

So stellen Sie eine oder mehrere ganze Zeilen voran :

Wie bei fügt seddie iFunktion dem einzufügenden Text immer eine nachgestellte neue Zeile hinzu.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 imacht 0(den Anfang der Datei) zur aktuellen Zeile und startet den Einfügemodus ( i); Beachten Sie, dass die Zeilennummern ansonsten 1basieren.
  • Die folgenden Zeilen sind der Text, der vor der aktuellen Zeile eingefügt werden soll und mit .einer eigenen Zeile abgeschlossen wird.

7

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 ...


6
+1 für die Verwendung von $$ (der aktuellen PID) als Teil des temporären Dateinamens. Wenn Sie ein Skript für den allgemeinen Gebrauch erstellen, wird dadurch verhindert, dass ein anderer Benutzer die temporäre Datei möglicherweise überschreibt, indem dasselbe Skript gleichzeitig ausgeführt wird.
E Brown

3
Die Verwendung $$ist jedoch für Produktionscode nicht ausreichend (Google Symlink-Angriff). aber es ist sicherlich besser als ein statischer Dateiname.
Tripleee

1
benutze einfachmktemp
mewa

7

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 teeAusgabe weglassen möchten , fügen Sie sie hinzu > /dev/null.


Diese Antwort gefällt mir sehr gut. Es ist ein sehr sauberer und klarer Weg, dies zu tun. Die Verwendung von tee ist eine natürliche Lösung für das Problem, die Ausgabe in die Eingabedatei zu übertragen. Auch keine Hacks beim Umbenennen. Besser als die derzeit am höchsten bewertete Lösung, da keine neue Datei erstellt wird. Dies ist im Grunde der nächste Punkt, den Sie >> zum Voranstellen anstatt zum Anhängen erreichen.
DC2

Dies ist bei weitem die beste und sauberste Antwort.
Nerdoc

6
Gibt es eine Garantie, teedie nicht verstopft, text.txtbevor catman sie lesen kann? Ich denke nicht - was diese Lösung für die Gesundheit der Datei gefährlich machen würde.
Jonathan Leffler

3
@ JonathanLeffler wahrscheinlich nein. Ich habe versucht, diesen Code zu testen : 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 lenA1000000 (1 MB Datei) und lenB10000 (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 teein 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.
VasiliNovikov

3
Um es konzeptionell auszudrücken: Dieser Befehl führt zu Datenverlust, wenn die Eingabedatei größer ist als die Pipeline-Puffergröße Ihres Systems , die heutzutage normalerweise 64 KB beträgt.
mklement0

7

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\nfunktioniert 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


Liebe es. Super portabel und muss keine neue Datei generieren. Vielen Dank!
Pyb

1
Das einzige Problem dabei ist, wenn der Inhalt von file.txtgrößer ist, als in die Argumentliste aufgenommen werden kann printf.
Jonathan Leffler

6

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

Warum nicht sed -i '1ito be prepended' inFile- oder ist es nur für GNU sed erlaubt?
Benutzer unbekannt

@userunknown: Standardmäßig folgt auf sedden iBefehl ein Backslash und eine Newline, und jede Eingabezeile mit Ausnahme der letzten endet ebenfalls mit einem Backslash. GNU sederlaubt Abkürzungen in der Richtung, nach der Sie fragen, aber nur GNU sedtut dies. '
Jonathan Leffler

3

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.


2
Beste Antwort, ich habe diesen Einzeiler geschrieben, um meine Datei mit dieser Antwort zu drehen: für i in $ (<file2.txt); echo "$ (echo $ i; cat file.txt)"> file.txt; getan;
Aurangzeb

2

Eine andere ziemlich einfache Lösung ist:

    $ echo -e "string\n" $(cat file)

Dies hat bei mir funktioniert ... nur eine geringfügige Änderung, um mehrzeilige Zeilen aus der Eingabedatei beizubehalten, müssen Sie sie in Anführungszeichen setzen, also ungefähr so: echo -e "string \ n" "$ (cat ~ / file.txt) "> ~ / file.txt;
Craig Wayne

2
Die catParametererweiterung 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 \tund \nim Originaltext werden durch Tabulatoren und Zeilenumbrüche ersetzt, anstatt so zu bleiben, wie sie waren.
Charles Duffy

1

Wenn Sie vi / vim mögen, ist dies möglicherweise eher Ihr Stil.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file

0
# create a file with content..
echo foo > /tmp/foo
# prepend a line containing "jim" to the file
sed -i "1s/^/jim\n/" /tmp/foo
# verify the content of the file has the new line prepened to it
cat /tmp/foo

0

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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.