So ersetzen Sie eine ganze Zeile in einer Textdatei durch die Zeilennummer

152

Ich habe eine Situation, in der ein Bash-Skript eine ganze Zeile in einer Datei ersetzen soll. Die Zeilennummer ist immer dieselbe, so dass es sich um eine fest codierte Variable handeln kann.

Ich versuche nicht, eine Unterzeichenfolge in dieser Zeile zu ersetzen, sondern möchte diese Zeile nur vollständig durch eine neue Zeile ersetzen.

Gibt es dafür Bash-Methoden (oder etwas Einfaches, das in ein .sh-Skript geworfen werden kann)?

user788171
quelle

Antworten:

228

Nicht das Beste, aber das sollte funktionieren:

sed -i 'Ns/.*/replacement-line/' file.txt

wo Nsollte durch Ihre Zielzeilennummer ersetzt werden. Dies ersetzt die Zeile in der Originaldatei. Löschen Sie die -iOption, um den geänderten Text in einer anderen Datei zu speichern :

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt
chepner
quelle
1
Gibt es eine Möglichkeit, sed dazu zu bringen, die betreffende Datei zu ändern, anstatt sie auf dem Bildschirm abzulegen?
user788171
8
Für mich heißt es: sed: -e expression #1, char 26: unknown option to ``s'und meine Linie ist : sed -i '7s/.*/<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpoint/</param-value>/' $TCE_SVN_HOME\trunk\tce\EWC\WebContent\WEB-INF\web.xml. Irgendeine Idee?
Danijel
4
Jeder /im Ersetzungstext wird als abschließender Schrägstrich des sBefehls interpretiert, sofern nicht maskiert ( \/). Am einfachsten ist es jedoch, ein anderes, nicht verwendetes Zeichen als Trennzeichen auszuwählen. Zum Beispiel sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk....
Chepner
4
Diese Erklärung ist etwas verwirrend und scheint nicht zu funktionieren. Wenn Sie Probleme mit sed und seinen Funktionen zum Einstellen von Trennzeichen haben, sollten Sie sich Folgendes ansehen: grymoire.com/Unix/Sed.html#uh-2 Das erste Zeichen hinter dem sbestimmt das Trennzeichen. #sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
Um
7
Um @ meonlol zu erweitern, benötigt macOS auch ein -eif -i. Somit wird der richtige Befehl:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE
44

Ich habe dieses Skript tatsächlich verwendet, um vor einiger Zeit eine Codezeile in der Cron-Datei auf den UNIX-Servern unseres Unternehmens zu ersetzen. Wir haben es als normales Shell-Skript ausgeführt und hatten keine Probleme:

#Create temporary file with new line in place
cat /dir/file | sed -e "s/the_original_line/the_new_line/" > /dir/temp_file
#Copy the new file over the original file
mv /dir/temp_file /dir/file

Dies erfolgt nicht nach Zeilennummer, aber Sie können einfach zu einem auf Zeilennummern basierenden System wechseln, indem Sie die Zeilennummer vor das setzen s/und einen Platzhalter anstelle von setzen the_original_line.

Kyle
quelle
17
Nutzloser Gebrauch von Katze:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner
24
Vielleicht nutzlos für den Interpreter / Compiler, aber nicht für einen Menschen, der es liest. @ Kyles Code liest sich gut von links nach rechts; Die Redewendung, die Sie IMO verwendet haben, funktioniert nicht, da das Verb vor dem Substantiv steht.
Clayton Stanley
1
Gibt es eine Möglichkeit, diese beiden Zeilen zusammenzuführen, zum Beispiel durch etwas wie:sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda
22

Angenommen, Sie möchten Zeile 4 durch den Text "different" ersetzen. Sie können AWK wie folgt verwenden:

awk '{ if (NR == 4) print "different"; else print $0}' input_file.txt > output_file.txt

AWK betrachtet die Eingabe als "Datensätze", die in "Felder" unterteilt sind. Standardmäßig ist eine Zeile ein Datensatz. NRist die Anzahl der gesehenen Datensätze. $0stellt den aktuellen vollständigen Datensatz dar (während dies $1das erste Feld aus dem Datensatz usw. ist; standardmäßig sind die Felder Wörter aus der Zeile).

Wenn die aktuelle Zeilennummer 4 ist, drucken Sie die Zeichenfolge "anders", andernfalls drucken Sie die Zeile unverändert.

In AWK wird der in eingeschlossene Programmcode { }einmal in jedem Eingabedatensatz ausgeführt.

Sie müssen das AWK-Programm in einfache Anführungszeichen setzen, damit die Shell nicht versucht, Dinge wie das zu interpretieren $0.

EDIT: Ein kürzeres und eleganteres AWK-Programm von @chepner in den Kommentaren unten:

awk 'NR==4 {$0="different"} { print }' input_file.txt

Ersetzen Sie nur für Datensatz (dh Zeile) Nummer 4 den gesamten Datensatz durch die Zeichenfolge "different". Drucken Sie dann für jeden Eingabedatensatz den Datensatz.

Offensichtlich sind meine AWK-Fähigkeiten verrostet! Vielen Dank, @chepner.

EDIT: und siehe auch eine noch kürzere Version von @Dennis Williamson:

awk 'NR==4 {$0="different"} 1' input_file.txt

Wie dies funktioniert, wird in den Kommentaren erklärt: Der 1Wert wird immer als wahr ausgewertet, sodass der zugehörige Codeblock immer ausgeführt wird. Es ist jedoch kein Codeblock zugeordnet, was bedeutet, dass AWK standardmäßig nur die gesamte Zeile druckt. AWK wurde entwickelt, um knappe Programme wie dieses zu ermöglichen.

steveha
quelle
3
Etwas kürzer : awk 'NR==4 {$0="different"} { print }' input_file.txt.
Chepner
2
@chepner: Etwas kürzer:awk 'NR==4 {$0="different"}1' input_file.txt
Bis auf weiteres angehalten.
@ TennisWilliamson, ich weiß nicht mal wie das funktioniert! Was macht das Schleppen 1? (Hinweis: Ich habe es getestet und es funktioniert tatsächlich! Ich verstehe nur nicht wie.)
Steveha
1
Ich dachte, es gäbe eine Möglichkeit, den bedingungslosen Druck abzukürzen! @stevaha: Die 1 ist nur ein wahrer Wert, was bedeutet, dass ein "Muster" immer übereinstimmt. Die Standardaktion für eine Übereinstimmung besteht darin, die aktuelle Zeile zu drucken.
Chepner
19

Angesichts dieser Testdatei (test.txt)

Lorem ipsum dolor sit amet,
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

Der folgende Befehl ersetzt die erste Zeile durch "Zeilenumbruch".

$ sed '1 c\
> newline text' test.txt

Ergebnis:

newline text
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

Weitere Informationen finden Sie hier

http://www.thegeekstuff.com/2009/11/unix-sed-tutorial-append-insert-replace-and-count-file-lines/

Eric Robinson
quelle
2
Dies sollte die richtige Antwort sein. Das Flag c macht genau das, was erforderlich ist (ersetzt eine nummerierte Zeile durch den angegebenen Text).
Adelphus
Alternative Syntax mit sed, die die Datei auf stdout nicht wiedergibt
Gabriel Devillers
Es ist die richtige Antwort, aber warum die hässliche Linie brechen? sed '1 cnewline text' test.txtwürde es auch tun.
dev-null
7

Ersetzen Sie in bash N, M durch die Zeilennummern und xxx yyy durch das, was Sie möchten

i=1
while read line;do
  if((i==N));then
    echo 'xxx'
  elif((i==M));then
    echo 'yyy'
  else
    echo "$line"
  fi
  ((i++))
done  < orig-file > new-file

BEARBEITEN

Tatsächlich gibt es bei dieser Lösung einige Probleme mit den Zeichen "\ 0" "\ t" und "\".

"\ t" kann gelöst werden, indem IFS = vor dem Lesen gesetzt wird: "\" am Ende der Zeile mit -r

IFS= read -r line

Für "\ 0" wird die Variable jedoch abgeschnitten. In Pure Bash gibt es keine Lösung: Weisen Sie einer Variablen in Bash eine Zeichenfolge mit einem Nullzeichen (\ 0) zu. In einer normalen Textdatei gibt es jedoch kein Nullzeichen \ 0

Perl wäre eine bessere Wahl

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file
Nahuel Fouilleul
quelle
Ich habe nicht einmal daran gedacht, nach einer reinen BASH-Lösung zu suchen!
Steveha
4
# Replace the line of the given line number with the given replacement in the given file.
function replace-line-in-file() {
    local file="$1"
    local line_num="$2"
    local replacement="$3"

    # Escape backslash, forward slash and ampersand for use as a sed replacement.
    replacement_escaped=$( echo "$replacement" | sed -e 's/[\/&]/\\&/g' )

    sed -i "${line_num}s/.*/$replacement_escaped/" "$file"
}
Daniel Bigham
quelle
3

Hervorragende Antwort von Chepner. Es funktioniert für mich in Bash Shell.

 # To update/replace the new line string value with the exiting line of the file
 MyFile=/tmp/ps_checkdb.flag

 `sed -i "${index}s/.*/${newLine}/" $MyFile`

hier
index- Zeile Nr.
newLine- neue Zeilenfolge, die wir ersetzen möchten.


In ähnlicher Weise wird der folgende Code verwendet, um eine bestimmte Zeile in der Datei zu lesen. Dies hat keine Auswirkungen auf die eigentliche Datei.

LineString=`sed "$index!d" $MyFile` 

hier
!d- löscht die anderen Zeilen als Zeile no. $index Wir erhalten also die Ausgabe als Zeilenzeichenfolge no $indexin der Datei.

Kanagavelu Sugumar
quelle
aber es ist mein Schlag, warum das Ergebnis nur zeigt ${newline}?
Sikisis
1

Auf dem Mac habe ich verwendet

sed -i '' -e 's/text-on-line-to-be-changed.*/text-to-replace-the=whole-line/' file-name
Nakeer
quelle
-2

Sie können sogar Parameter an den Befehl sed übergeben:

test.sh

#!/bin/bash
echo "-> start"
for i in $(seq 5); do
  # passing parameters to sed
  j=$(($i+3))
  sed -i "${j}s/.*/replaced by '$i'!/" output.dat
done
echo "-> finished"
exit 

orignial output.dat:

a
b
c
d
e
f
g
h
i
j

Die Ausführung ./test.sh gibt die neuen OUTPUT.DAT

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j
AndreH
quelle
1
Warum brauchen Sie für dieses Beispiel eine Schleife, die die Lösung nur verschleiert? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob
Sie brauchen dafür keine Schleife
chandu vutukuri