Come sostituire un'intera riga in un file di testo per numero di riga


152

Ho una situazione in cui desidero che uno script bash sostituisca un'intera riga in un file. Il numero di riga è sempre lo stesso, quindi può essere una variabile hardcoded.

Non sto cercando di sostituire alcune sotto-stringhe in quella riga, voglio solo sostituire quella riga interamente con una nuova riga.

Ci sono dei metodi bash per farlo (o qualcosa di semplice che può essere gettato in uno script .sh).

Risposte:


228

Non il massimo, ma questo dovrebbe funzionare:

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

dove Ndovrebbe essere sostituito dal numero della linea target. Questo sostituisce la riga nel file originale. Per salvare il testo modificato in un altro file, rilasciare-i opzione:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt

1
C'è un modo per far sed modificare il file in questione invece di scaricare sullo schermo?
user788171,

8
Per me si dice: sed: -e expression #1, char 26: unknown option to ``s'e la mia linea è: 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. Qualche idea?
Danijel,

4
Ciascuno /nel testo sostitutivo verrebbe interpretato come la barra di chiusura del scomando a meno che non sia escape ( \/). La cosa più semplice da fare, tuttavia, è scegliere un delimitatore diverso, inutilizzato. Ad esempio sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk...,.
Chepner,

4
Questa spiegazione è un po 'confusa e non sembra funzionare. Se avete problemi con sed e le sue capacità di impostazione del delimitatore, potreste dare un'occhiata a questo: grymoire.com/Unix/Sed.html#uh-2 Dice che il primo carattere dietro sdetermina il delimitatore. Quindi, per cambiare il tuo comando per usare ad esempio #sarebbe così:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der

7
Per espandere su @ meonlol, macOS richiede anche un -eif -i; quindi, il comando corretto diventa:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE

44

In realtà ho usato questo script per sostituire una riga di codice nel file cron sui server UNIX della nostra società qualche tempo fa. Lo abbiamo eseguito come normale script di shell e non abbiamo avuto problemi:

#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

Questo non va per numero di riga, ma puoi facilmente passare a un sistema basato sul numero di riga mettendo il numero di riga prima del s/e posizionando un carattere jolly al posto di the_original_line.


17
Uso inutile del gatto:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner,

24
Inutile forse per l'interprete / compilatore, ma non per un umano che lo legge. Il codice di @ Kyle è ben leggibile da sinistra a destra; il linguaggio che hai usato IMO non lo fa, a causa del fatto che il verbo è prima del nome.
Clayton Stanley,

1
C'è un modo per unire quelle due linee, ad esempio con qualcosa del genere,sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda,

22

Supponiamo che tu voglia sostituire la riga 4 con il testo "diverso". Puoi usare AWK in questo modo:

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

AWK considera l'input come "record" diviso in "campi". Per impostazione predefinita, una riga è un record. NRè il numero di record visti. $0rappresenta il record completo corrente (mentre $1è il primo campo del record e così via; per impostazione predefinita, i campi sono parole della riga).

Pertanto, se il numero di riga corrente è 4, stampare la stringa "diverso", ma altrimenti stampare la riga invariata.

In AWK, il codice del programma racchiuso { }viene eseguito una volta su ciascun record di input.

Devi citare il programma AWK tra virgolette singole per evitare che la shell provi a interpretare cose come la $0.

EDIT: un programma AWK più breve ed elegante di @chepner nei commenti qui sotto:

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

Solo per il record (es. Riga) numero 4, sostituire l'intero record con la stringa "diverso". Quindi, per ogni record di input, stampare il record.

Chiaramente le mie abilità in AWK sono arrugginite! Grazie, @chepner.

EDIT: e vedi anche una versione ancora più breve di @Dennis Williamson:

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

Il modo in cui funziona è spiegato nei commenti: il valore è 1sempre true, quindi il blocco di codice associato viene sempre eseguito. Ma non esiste un blocco di codice associato, il che significa che AWK esegue la sua azione predefinita semplicemente stampando l'intera riga. AWK è progettato per consentire programmi concisi come questo.


3
Un po 'più breve: awk 'NR==4 {$0="different"} { print }' input_file.txt.
Chepner,

2
@chepner: Un po 'più breve:awk 'NR==4 {$0="different"}1' input_file.txt
In pausa fino a nuovo avviso.

@DennisWilliamson, non so nemmeno come funzioni! Cosa fa quel trailing 1? (Nota: l'ho provato e funziona davvero! Non capisco come.)
steveha,

1
Ho pensato che ci sarebbe stato un modo per abbreviare la stampa incondizionata! @stevaha: 1 è solo un valore vero, che significa uno "schema" che corrisponde sempre. E l'azione predefinita per una partita è stampare la riga corrente.
chepner,

19

Dato questo file di test (test.txt)

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

il seguente comando sostituirà la prima riga in "testo di nuova riga"

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

Risultato:

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

Ulteriori informazioni possono essere trovate qui

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


2
Questa dovrebbe essere la risposta corretta. Il flag c fa esattamente ciò che è richiesto (sostituisce una riga numerata con un determinato testo).
Adelphus,

Sintassi alternativa con sed che non fa eco al file su stdout: stackoverflow.com/a/13438118/4669135
Gabriel Devillers,

È la risposta giusta, ma perché la brutta interruzione di linea? sed '1 cnewline text' test.txtlo farebbe pure.
dev-null,

7

in bash, sostituisci N, M con i numeri di riga e xxx yyy con quello che vuoi

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

MODIFICARE

In effetti in questa soluzione ci sono alcuni problemi, con i caratteri "\ 0" "\ t" e "\"

"\ t", può essere risolto mettendo IFS = prima di leggere: "\", alla fine della riga con -r

IFS= read -r line

ma per "\ 0", la variabile viene troncata, non esiste una soluzione in puro bash: assegnare una stringa contenente un carattere null (\ 0) a una variabile in Bash Ma nel normale file di testo non è presente alcun carattere nullo \ 0

il perl sarebbe una scelta migliore

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file

Non ho nemmeno pensato di provare per una soluzione BASH pura!
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"
}

3

Ottima risposta di Chepner. Funziona per me in Shell 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`

qui
index- Linea n.
newLine- nuova stringa di linea che vogliamo sostituire.


Allo stesso modo il codice seguente viene utilizzato per leggere una riga particolare nel file. Ciò non influirà sul file effettivo.

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

qui
!d- eliminerà le righe diverse dalla linea no $index Quindi otterremo l'output come stringa di linea di no $indexnel file.


ma è il mio caso perché il risultato mostra solo ${newline}?
Sikisis,

1

Su Mac ho usato

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

-2

Puoi anche passare i parametri al comando sed:

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

L'esecuzione di ./test.sh fornisce il nuovo output.dat

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j

1
perché hai bisogno di un loop per questo esempio, offusca solo la soluzione? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob,

Non hai bisogno di un loop per questo
chandu vutukuri il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.