Remplacer la ligne entière contenant une chaîne à l'aide de Sed

319

J'ai un fichier texte qui a une ligne particulière quelque chose comme

sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext

Je dois remplacer toute la ligne ci-dessus par

This line is removed by the admin.

Le mot clé de recherche est TEXT_TO_BE_REPLACED

J'ai besoin d'écrire un script shell pour cela. Comment puis-je y parvenir en utilisant sed?

Rahul
la source
@Scharron J'ai essayé sed -rne 's / (TEXT_TO_BE_REPLACED) \ s + \ w + / \ 1 yyy / xxx' Mais cela remplacerait simplement yyy par xxx dans la chaîne s'il y a du yyy. Dans mon cas, le texte n'est pas fixe ... tout ce que j'ai est TEXT_TO_BE_REPLACED.
Rahul
ce n'est pas une expression sed valide. Êtes-vous sûr de le vouloir avec sed? Tout langage de programmation avec de bonnes expressions rationnelles peut faire un remplacement pour vous (recherchez une sous-fonction). Ce serait plus facile que de comprendre sed par vous
Scharron
@Scharron Eh bien, cela se fait avec un script shell, donc je suppose que sed est mon meilleur pari.
Rahul

Réponses:

483

Vous pouvez utiliser la commande change pour remplacer la ligne entière et l' -iindicateur pour effectuer les modifications sur place. Par exemple, en utilisant GNU sed:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
Todd A. Jacobs
la source
4
Notez que vous avez besoin d'un espace avant le c \. Je viens de modifier pour ajouter ceci.
Marcus Downing
27
@MarcusDowning GNU sed ne nécessite pas d'espace; cela fonctionne très bien comme initialement affiché. Si votre sed particulier a besoin d'espace, alors notez certainement quel sed est incompatible et ajoutez l'invocation nécessaire en tant que commentaire. Cependant, veuillez ne pas modifier le code de travail dans une réponse acceptée.
Todd A. Jacobs
7
Comment puis-je utiliser une variable au lieu du texte "This ..."? Si je le remplace par $ variable, il n'imprime pas son contenu mais le nom de la variable.
Steven
18
Il y a un problème avec le c\ suivi direct d'une variable: …c\$VAR…la barre oblique inversée échappera au dollar. Dans ce cas I (bash / sed sur Ubuntu 15.10) a dû écrire…c\\$VAR…
Jan
6
sur une utilisation de mac: sed -i '' '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo; (lorsque le premier paramètre est vide, il modifie le fichier, sinon crée une sauvegarde)
AndreDurao
152

Vous devez utiliser les caractères génériques ( .*) avant et après pour remplacer toute la ligne:

sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'
Thor
la source
Merci, le mien a fonctionné: sed 's /.* <expression>. * / <expression> SE_LABEL = ABC <expression> / g' MYR2.xml> test.txt
Nasri Najib
9
Cela fonctionne sur Mac OS X Yosemite à l'exception que j'utilise les drapeaux -i et -e comme suit:sed -i -e "s/.*search_string.*/Replacement_line/' file_being_searched.txt
Kent Bull
1
@KentJohnson Je pense que vous avez des citations incompatibles dans votre commande.
HashHazard
@MBarnett vous avez raison, je devrais y avoir deux guillemets.
Kent Bull
2
Juste pour l'info complète. Pour le rendre en place, on peut ajouter une -ioption
Temak
20

La réponse acceptée n'a pas fonctionné pour moi pour plusieurs raisons:

  • ma version de sed n'aime pas -iavec une extension de longueur nulle
  • la syntaxe de la c\commande est bizarre et je n'ai pas pu le faire fonctionner
  • Je ne savais pas que certains de mes problèmes venaient de barres obliques

Voici donc la solution que j'ai trouvée et qui devrait fonctionner dans la plupart des cas:

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/
}

Ainsi, l'exemple d'utilisation pour résoudre le problème posé:

change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile
skensell
la source
18

La réponse ci-dessus:

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

Fonctionne bien si la chaîne / ligne de remplacement n'est pas une variable.

Le problème, c'est que sur Redhat 5 \après, les céchappés $. Un double \\n'a pas fonctionné non plus (du moins sur Redhat 5).

Grâce au hit et à l'essai, j'ai découvert que l' \after cest redondant si votre chaîne / ligne de remplacement n'est qu'une seule ligne. Donc je n'ai pas utilisé \après le c, j'ai utilisé une variable comme une seule ligne de remplacement et c'était de la joie.

Le code ressemblerait à quelque chose comme:

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

Notez l'utilisation de guillemets doubles au lieu de guillemets simples.

user4256255
la source
vous pouvez toujours utiliser des guillemets simples comme celui-ci: sed -i '/ TEXT_TO_BE_REPLACED / c' "$ VARIABLE" '' / tmp / foo
Alistair McIntyre
4

Toutes les réponses fournies jusqu'à présent supposent que vous savez quelque chose sur le texte à remplacer, ce qui est logique, car c'est ce que le PO a demandé. Je fournis une réponse qui suppose que vous ne savez rien du texte à remplacer et qu'il peut y avoir une ligne distincte dans le fichier avec le même contenu ou un contenu similaire que vous ne souhaitez pas remplacer. De plus, je suppose que vous connaissez le numéro de ligne de la ligne à remplacer.

Les exemples suivants illustrent la suppression ou la modification de texte par des numéros de ligne spécifiques:

# 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/'
b_laoshi
la source
3

pour la manipulation des fichiers de configuration

j'ai trouvé cette solution inspirée par la réponse de skensell

configLine [searchPattern] [replaceLine] [filePath]

ce sera:

  • créer le fichier s'il n'existe pas
  • remplacer la ligne entière (toutes les lignes) où searchPattern correspond
  • ajoutez replaceLine à la fin du fichier si le modèle n'est pas trouvé

Fonction:

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 magie du statut de sortie folle vient de https://stackoverflow.com/a/12145797/1262663

Grain
la source
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
'

ça fonctionne bien

nkl
la source
1

Dans mon makefile, j'utilise ceci:

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

PS: N'OUBLIEZ PAS que le -i change en fait le texte du fichier ... donc si le motif que vous avez défini comme "Révision" changera, vous changerez également le motif à remplacer.

Exemple de sortie:

Abc-Project écrit par John Doe

Révision: 1190

Donc, si vous définissez le modèle "Révision: 1190", ce n'est évidemment pas la même chose que vous les avez définis comme "Révision:" seulement ...

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

Le fichier find_replace contient 2 colonnes, c1 avec motif à correspondre, c2 avec remplacement, la boucle sed remplace chaque ligne contenant un des motifs de la variable 1

sjordi
la source
Non, c'est faux à plusieurs titres. Exécutez sedune fois avec un fichier de script contenant tous les remplacements que vous souhaitez effectuer. L'exécution sed -irépétée du même fichier est un horrible motif.
tripleee
0

Il est similaire à celui ci-dessus ..

sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'
Annapureddy Hari
la source
3
Cela change FOO=TEXT_TO_BE_REPLACEDen FOO=This line ...donc ne répond pas aux spécifications.
Jens
Oui .. Notre exigence est de remplacer la ligne entière par «Cette ligne est supprimée par l'administrateur». si nous avons trouvé la clé pattren 'TEXT_TO_BE_REPLACED'. La commande ci-dessus est satisfaisante. Corrigez-moi si ma compréhension est fausse .. @Jens
Annapureddy Hari
@AnnapureddyHari cette réponse ne fonctionne pas si le texte avant ou après la chaîne de recherche contient autre chose que A-Za-z0-9. Il échoue s'il y a un signe égal, comme l'a souligné Jens. La portion "FOO =" restera; vous n'avez pas remplacé toute la ligne. Ce code est à courte vue sur ce qui pourrait être dans le fichier. Si vous voulez dire un caractère générique, vous devez mettre un caractère générique, comme le montre la réponse de Thor.
msouth
0

La commande ci-dessous fonctionne pour moi. Qui travaille avec des variables

sed -i "/\<$E\>/c $D" "$B"
Ritesh Borkar
la source
Mais ma nouvelle exigence est de sauter la ligne commentée (commence par #) lors du remplacement. Comme nous remplaçons la ligne complète, cela remplacera également les lignes commentées et vous vous retrouverez avec des propriétés en double. Si quelqu'un a une solution pour cela, faites-le moi savoir.
Ritesh Borkar,
-1

J'utilise très souvent regex pour extraire des données de fichiers que je viens d'utiliser pour remplacer la citation littérale \"par //rien :-)

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