Comment remplacer une ligne entière dans un fichier texte par un numéro de ligne

152

J'ai une situation où je veux qu'un script bash remplace une ligne entière dans un fichier. Le numéro de ligne est toujours le même, ce qui peut être une variable codée en dur.

Je n'essaye pas de remplacer une sous-chaîne dans cette ligne, je veux juste remplacer cette ligne entièrement par une nouvelle ligne.

Existe-t-il des méthodes bash pour faire cela (ou quelque chose de simple qui peut être jeté dans un script .sh).

user788171
la source

Réponses:

228

Pas le meilleur, mais cela devrait fonctionner:

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

Ndoit être remplacé par votre numéro de ligne cible. Cela remplace la ligne dans le fichier d'origine. Pour enregistrer le texte modifié dans un autre fichier, supprimez l' -ioption:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt
chepner
la source
1
Existe-t-il un moyen de faire modifier le fichier en question par sed au lieu de le transférer à l'écran?
user788171
8
Pour moi , il dit: sed: -e expression #1, char 26: unknown option to ``s'et ma ligne est: 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. Une idée?
Danijel le
4
Chacun /dans le texte de remplacement serait interprété comme la barre oblique de fermeture de la scommande à moins que escape ( \/). La chose la plus simple à faire, cependant, est de choisir un caractère différent et inutilisé comme délimiteur. Par exemple sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk...,.
chepner le
4
Cette explication est un peu déroutante et ne semble pas fonctionner. Si vous avez des problèmes avec sed et ses capacités de réglage de délimiteur, vous voudrez peut-être jeter un œil à ceci: grymoire.com/Unix/Sed.html#uh-2 Il est dit que le premier caractère derrière le sdétermine le délimiteur. Donc pour changer votre commande pour utiliser par exemple le #ce serait comme ceci:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der
7
Pour développer @ meonlol, macOS nécessite également un -eif -i; ainsi, la commande correcte devient:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE
44

J'ai en fait utilisé ce script pour remplacer une ligne de code dans le fichier cron sur les serveurs UNIX de notre société il y a quelque temps. Nous l'avons exécuté comme un script shell normal et n'avons eu aucun problème:

#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

Cela ne se fait pas par numéro de ligne, mais vous pouvez facilement passer à un système basé sur le numéro de ligne en mettant le numéro de ligne avant le s/et en plaçant un caractère générique à la place de the_original_line.

Kyle
la source
17
Utilisation inutile de chat:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner
24
Inutile pour l'interpréteur / compilateur peut-être, mais pas pour un humain qui le lit. Le code de @ Kyle se lit bien de gauche à droite; l'idiome que vous avez utilisé IMO ne le fait pas, car le verbe est avant le nom.
Clayton Stanley
1
Existe-t-il un moyen de fusionner ces deux lignes, par exemple par quelque chose comme,sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda
22

Supposons que vous souhaitiez remplacer la ligne 4 par le texte "différent". Vous pouvez utiliser AWK comme ceci:

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

AWK considère l'entrée comme étant des "enregistrements" divisés en "champs". Par défaut, une ligne correspond à un enregistrement. NRest le nombre d'enregistrements vus. $0représente l'enregistrement complet actuel (while $1est le premier champ de l'enregistrement et ainsi de suite; par défaut, les champs sont des mots de la ligne).

Ainsi, si le numéro de ligne actuel est 4, imprimez la chaîne "différent" mais sinon imprimez la ligne inchangée.

Dans AWK, le code de programme inclus dans { }s'exécute une fois sur chaque enregistrement d'entrée.

Vous devez citer le programme AWK entre guillemets simples pour empêcher le shell d'essayer d'interpréter des choses comme le $0.

EDIT: Un programme AWK plus court et plus élégant de @chepner dans les commentaires ci-dessous:

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

Uniquement pour l'enregistrement (c'est-à-dire la ligne) numéro 4, remplacez l'ensemble de l'enregistrement par la chaîne «différent». Ensuite, pour chaque enregistrement d'entrée, imprimez l'enregistrement.

De toute évidence, mes compétences AWK sont rouillées! Merci, @chepner.

EDIT: et voir aussi une version encore plus courte de @Dennis Williamson:

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

Comment cela fonctionne est expliqué dans les commentaires: le 1évalue toujours vrai, donc le bloc de code associé s'exécute toujours. Mais il n'y a pas de bloc de code associé, ce qui signifie qu'AWK effectue son action par défaut consistant simplement à imprimer la ligne entière. AWK est conçu pour autoriser des programmes laconiques comme celui-ci.

Steveha
la source
3
Un peu plus court: awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner
2
@chepner: Un peu plus court:awk 'NR==4 {$0="different"}1' input_file.txt
pause jusqu'à nouvel ordre.
@DennisWilliamson, je ne sais même pas comment ça marche! Que fait cette traînée 1? (Remarque: je l'ai testé et cela fonctionne vraiment! Je ne comprends tout simplement pas comment.)
steveha
1
J'ai pensé qu'il y aurait un moyen d'abréger l'impression inconditionnelle! @stevaha: le 1 est juste une valeur vraie, c'est-à-dire un "modèle" qui correspond toujours. Et l'action par défaut pour une correspondance est d'imprimer la ligne courante.
chepner
19

Compte tenu de ce fichier de test (test.txt)

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

la commande suivante remplacera la première ligne par "texte de nouvelle ligne"

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

Résultat:

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

Plus d'informations peuvent être trouvées ici

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

Eric Robinson
la source
2
Cela devrait être la bonne réponse. L'indicateur c fait exactement ce qui est requis (remplace une ligne numérotée par un texte donné).
adelphus
Syntaxe alternative avec sed qui ne fait pas écho au fichier sur stdout: stackoverflow.com/a/13438118/4669135
Gabriel Devillers
C'est la bonne réponse, mais pourquoi ce vilain saut de ligne? sed '1 cnewline text' test.txtle ferait aussi.
dev-null
7

dans bash, remplacez N, M par les numéros de ligne et xxx yyy par ce que vous voulez

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

ÉDITER

En fait, dans cette solution, il y a quelques problèmes, avec les caractères "\ 0" "\ t" et "\"

"\ t", peut être résolu en mettant IFS = avant lecture: "\", en fin de ligne avec -r

IFS= read -r line

mais pour "\ 0", la variable est tronquée, il n'y a pas de solution en bash pur: Assignez une chaîne contenant un caractère nul (\ 0) à une variable en bash Mais dans un fichier texte normal, il n'y a pas de caractère nul \ 0

perl serait un meilleur choix

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file
Nahuel Fouilleul
la source
Je n'ai même pas pensé à essayer une solution pure BASH!
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
la source
3

Excellente réponse de Chepner. Cela fonctionne pour moi dans 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`

here
index- Line no
newLine- nouvelle chaîne de ligne que nous voulons remplacer.


De même, le code ci-dessous est utilisé pour lire une ligne particulière dans le fichier. Cela n'affectera pas le fichier réel.

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

ici
!d- supprimera les lignes autres que la ligne no. $index Nous obtiendrons donc la sortie sous forme de chaîne de ligne de no $indexdans le fichier.

Kanagavelu Sugumar
la source
mais c'est ma bash pourquoi le résultat montre seulement ${newline}?
sikisis
1

Sur Mac, j'ai utilisé

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

Vous pouvez même passer des paramètres à la commande 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'exécution de ./test.sh donne le nouveau output.dat

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j
AndreH
la source
1
pourquoi avez-vous besoin d'une boucle pour cet exemple, obscurcit simplement la solution? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob
Vous n'avez pas besoin d'une boucle pour cela
chandu vutukuri