Sostituisci l'intera riga contenente una stringa usando Sed


319

Ho un file di testo che ha una riga particolare simile

sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext

Ho bisogno di sostituire l'intera riga sopra con

This line is removed by the admin.

La parola chiave di ricerca è TEXT_TO_BE_REPLACED

Ho bisogno di scrivere uno script di shell per questo. Come posso ottenere questo usando sed?


@Scharron Ho provato a sed -rne 's / (TEXT_TO_BE_REPLACED) \ s + \ w + / \ 1 yyy / xxx' Ma questo sostituirebbe yyy con xxx nella stringa se è presente uno yyy. Nel mio caso il testo non è stato risolto ... tutto quello che ho è TEXT_TO_BE_REPLACED.
— Rahul,

questa non è un'espressione sed valida. Sei sicuro di volerlo con sed? Qualsiasi linguaggio di programmazione con buone regexps può sostituirti (cerca una sub funzione). Sarebbe più semplice che capire sed da soli
— Scharron,

@Scharron Bene, lo si deve fare con lo script della shell, quindi immagino che sed sia la mia scommessa migliore.
— Rahul,

Risposte:


483

È possibile utilizzare il comando change per sostituire l'intera riga e il -iflag per apportare le modifiche sul posto. Ad esempio, usando GNU sed:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo

4
Si noti che è necessario uno spazio prima di c \. Ho appena modificato per aggiungere questo.
— Marcus Downing,

27
@MarcusDowning GNU sed non richiede spazio; funziona bene come pubblicato originariamente. Se la tua particolare sed richiede lo spazio, allora nota quale sed non è compatibile e aggiungi l'invocazione necessaria come commento. Tuttavia, non modificare il codice di lavoro in una risposta accettata.
— Todd A. Jacobs,

7
Come posso usare una variabile al posto del testo "Questo ..."? Se lo sostituisco con $ variabile, non stampa il suo contenuto ma il nome della variabile.
— Steven,

18
C'è un problema con c\ quando seguito direttamente da una variabile: …c\$VAR…la barra rovesciata sfuggirà al dollaro. In questo caso ho (bash / sed su Ubuntu 15.10) dovuto scrivere…c\\$VAR…
— Jan

6
su un uso Mac: sed -i '' '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo; (quando il primo parametro è vuoto modifica in-file, altrimenti crea un backup)
— AndreDurao

152

È necessario utilizzare i caratteri jolly ( .*) prima e dopo per sostituire l'intera riga:

sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'

Grazie, ho fatto funzionare il mio: sed 's /.* <espressione>. * / <espressione> SE_LABEL = ABC <espressione> / g' MYR2.xml> test.txt
— Nasri Najib

9
Questo funziona su Mac OS X Yosemite con l'eccezione che sto usando i flag -i e -e come segue:sed -i -e "s/.*search_string.*/Replacement_line/' file_being_searched.txt
— Kent Bull,

1
@KentJohnson Penso che tu abbia virgolette non corrispondenti nel tuo comando.
— HashHazard,

@MBarnett hai ragione, dovrei avere due doppie virgolette lì.
— Kent Bull,

2
Solo per le informazioni complete. Per renderlo operativo, è possibile aggiungere -iun'opzione
— Temak,

20

La risposta accettata non ha funzionato per me per diversi motivi:

  • la mia versione di sed non piace -icon un'estensione di lunghezza zero
  • la sintassi del c\comando è strana e non sono riuscito a farlo funzionare
  • Non mi ero reso conto che alcuni dei miei problemi provenissero da barre senza caratteri di escape

Quindi ecco la soluzione che mi è venuta in mente e penso che dovrebbe funzionare nella maggior parte dei casi:

function escape_slashes {
    sed 's/\//\\\//g' 
}

function change_line {
    local OLD_LINE_PATTERN=$1; shift
    local NEW_LINE=$1; shift
    local FILE=$1

    local NEW=$(echo "${NEW_LINE}" | escape_slashes)
    sed -i .bak '/'"${OLD_LINE_PATTERN}"'/s/.*/'"${NEW}"'/' "${FILE}"
    mv "${FILE}.bak" /tmp/
}

Quindi l'utilizzo di esempio per risolvere il problema ha posto:

change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile

18

La risposta sopra:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo

Funziona bene se la stringa / riga sostitutiva non è una variabile.

Il problema è che su Redhat 5 il \dopo csfugge al $. Neanche un doppio \\ha funzionato (almeno su Redhat 5).

Attraverso hit e trial, ho scoperto che il \dopo cè ridondante se la stringa / linea di sostituzione è solo una singola linea. Quindi non ho usato \dopo il c, ho usato una variabile come singola linea di sostituzione ed è stata una gioia.

Il codice sarebbe simile a:

sed -i "/TEXT_TO_BE_REPLACED/c $REPLACEMENT_TEXT_STRING" /tmp/foo

Nota l'uso di virgolette doppie anziché singole.


puoi ancora usare virgolette singole come questa: sed -i '/ TEXT_TO_BE_REPLACED / c' "$ VARIABLE" '' / tmp / foo
— Alistair McIntyre

4

Tutte le risposte fornite finora presuppongono che tu sappia qualcosa sul testo da sostituire, il che ha senso, poiché è quello che l'OP ha chiesto. Sto fornendo una risposta che presuppone che tu non sappia nulla del testo da sostituire e che potrebbe esserci una riga separata nel file con lo stesso contenuto o simile che non desideri sostituire. Inoltre, presumo che tu conosca il numero di riga della riga da sostituire.

I seguenti esempi dimostrano la rimozione o la modifica del testo mediante numeri di riga specifici:

# replace line 17 with some replacement text and make changes in file (-i switch)
# the "-i" switch indicates that we want to change the file. Leave it out if you'd
#   just like to see the potential changes output to the terminal window.
# "17s" indicates that we're searching line 17
# ".*" indicates that we want to change the text of the entire line
# "REPLACEMENT-TEXT" is the new text to put on that line
# "PATH-TO-FILE" tells us what file to operate on
sed -i '17s/.*/REPLACEMENT-TEXT/' PATH-TO-FILE

# replace specific text on line 3
sed -i '3s/TEXT-TO-REPLACE/REPLACEMENT-TEXT/'

3

per manipolazione di file di configurazione

mi è venuta in mente questa soluzione ispirata alla risposta skensell

configLine [searchPattern] [replaceLine] [filePath]

lo farà:

  • crea il file se non esiste
  • sostituisce l'intera riga (tutte le righe) in cui searchPattern corrisponde
  • aggiungi replaceLine alla fine del file se il modello non è stato trovato

Funzione:

function configLine {
  local OLD_LINE_PATTERN=$1; shift
  local NEW_LINE=$1; shift
  local FILE=$1
  local NEW=$(echo "${NEW_LINE}" | sed 's/\//\\\//g')
  touch "${FILE}"
  sed -i '/'"${OLD_LINE_PATTERN}"'/{s/.*/'"${NEW}"'/;h};${x;/./{x;q100};x}' "${FILE}"
  if [[ $? -ne 100 ]] && [[ ${NEW_LINE} != '' ]]
  then
    echo "${NEW_LINE}" >> "${FILE}"
  fi
}

la folle magia dello stato di uscita proviene da https://stackoverflow.com/a/12145797/1262663


2
bash-4.1$ new_db_host="DB_HOSTNAME=good replaced with 122.334.567.90"
bash-4.1$ 
bash-4.1$ sed -i "/DB_HOST/c $new_db_host" test4sed
vim test4sed
'
'
'
DB_HOSTNAME=good replaced with 122.334.567.90
'

funziona benissimo


1

Nel mio makefile uso questo:

@sed -i '/.*Revision:.*/c\'"`svn info -R main.cpp | awk '/^Rev/'`"'' README.md

PS: NON dimenticare che il -i cambia effettivamente il testo nel file ... quindi se il pattern che hai definito come "Revision" cambierà, cambierai anche il pattern per sostituirlo.

Esempio di output:

Abc-Project scritto da John Doe

Revisione: 1190

Quindi, se imposti lo schema "Revisione: 1190" non è ovviamente lo stesso che li definivi come "Revisione:" solo ...


1
cat find_replace | while read pattern replacement ; do
sed -i "/${pattern}/c ${replacement}" file    
done 

Il file find_replace contiene 2 colonne, c1 con motivo da abbinare, c2 con sostituzione, il ciclo sed sostituisce ogni riga che unisce uno dei motivi della variabile 1


No, questo è sbagliato sotto diversi aspetti. Esegui seduna volta con un file di script contenente tutti i rimpiazzi che desideri eseguire. L'esecuzione sed -iripetuta sullo stesso file è un antipasto orribile.
— Tripleee,

0

È simile a quello sopra ..

sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'

3
Ciò cambia FOO=TEXT_TO_BE_REPLACEDin FOO=This line ...tal modo non soddisfa le specifiche.
— Jens,

Sì. Il nostro requisito è sostituire l'intera riga con "Questa riga viene rimossa dall'amministratore". se trovassimo la chiave pattren "TEXT_TO_BE_REPLACED". Il comando sopra è soddisfacente. Correggimi se la mia comprensione è sbagliata .. @Jens
— Annapureddy Hari

@AnnapureddyHari questa risposta non funziona se il testo prima o dopo la stringa di ricerca contiene qualcosa oltre A-Za-z0-9. Fallisce se c'è un segno di uguale, come sottolineato da Jens. La parte "FOO =" rimarrà; non hai sostituito l'intera riga. Questo codice ha una visione breve di ciò che potrebbe essere nel file. Se intendi jolly, dovresti mettere jolly, come mostra la risposta di Thor.
— msouth,

0

Di seguito il comando funziona per me. Che sta lavorando con le variabili

sed -i "/\<$E\>/c $D" "$B"

Ma il mio nuovo requisito è SKIP commentato (inizia con #) riga durante la sostituzione. Poiché stiamo sostituendo la riga completa, verranno sostituite anche le righe commentate e si otterranno proprietà duplicate. Se qualcuno ha una soluzione per questo, per favore fatemelo sapere.
— Ritesh Borkar,

-1

Uso molto spesso regex per estrarre dati da file che ho appena usato per sostituire la citazione letterale \"con //nulla :-)

cat file.csv | egrep '^\"([0-9]{1,3}\.[0-9]{1,3}\.)' | sed  s/\"//g  | cut -d, -f1 > list.txt
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.