Comment remplacer une chaîne sur la 5ème ligne de plusieurs fichiers texte?

22

Je souhaite remplacer la 5e ligne de plusieurs fichiers texte ( file1.txt, file2.txt, file3.txt, file4.txt ) par la chaîne "Good Morning" à l'aide d'une seule commande de terminal.

Tous les fichiers texte se trouvent sur mon ~/Desktop.

Remarque: Mon bureau se compose de 6 fichiers .txt. Je souhaite appliquer la modification aux 4 fichiers texte mentionnés ci-dessus uniquement.

Avinash Raj
la source

Réponses:

40

Voici quelques approches. J'utilise l' accolade expansion ( file{1..4}.txt) ce qui signifiefile1.txt file2.txt file3.txt file4.txt

  1. Perl

    perl -i -pe 's/.*/ Good Morning / if $.==5' file{1..4}.txt

    Explication:

    • -i: provoque la perlmodification des fichiers en place, en modifiant le fichier d'origine.

      Si -iest suivi d'un suffixe d'extension de fichier, une sauvegarde est créée pour chaque fichier modifié. Ex: -i.bakcrée un file1.txt.bakif file1.txtmodifié lors de l'exécution.

    • -p: signifie lire le fichier d'entrée ligne par ligne, appliquer le script et l'imprimer.

    • -e: permet de passer un script depuis la ligne de commande.

    • s/.*/ Good Morning /: Cela remplacera le texte de la ligne actuelle ( .*) par Bonjour.

    • $.est une variable Perl spéciale qui contient le numéro de ligne actuel du fichier d'entrée. Donc,, s/foo/bar/ if $.==5signifie remplacer foopar baruniquement sur la 5ème ligne.

  2. sed

    sed -i '5s/.*/ Good Morning /' file{1..4}.txt

    Explication:

    • -i: Comme pour perl, éditer le fichier sur place.

    Par défaut, sedimprime chaque ligne du fichier d'entrée. Le 5s/pattern/replacement/motif de substitution de moyen avec remplacement sur la 5ème ligne.

  3. Awk

    for f in file{1..4}.txt; do 
        awk 'NR==5{$0=" Good Morning "}1;' "$f" > foobar && mv foobar "$f"; 
    done

    Explication:

    awkn'a pas d'équivalent à l' -ioption¹, ce qui signifie que nous devons créer un fichier temporaire ( foobar) qui est ensuite renommé pour remplacer l'original. La boucle bash for f in file{1..4}.txt; do ... ; donepasse simplement par chacun file{1..4}.txt, enregistrant le nom de fichier actuel sous $f. Dans awk, NRest le numéro de ligne actuel et $0le contenu de la ligne actuelle. Ainsi, le script remplacera la ligne ( $0) par "Bonjour" uniquement sur la 5ème ligne. 1;est awkpour "imprimer la ligne".

    ¹ Les versions plus récentes font ce que devnull a montré dans sa réponse .

  4. coreutils

    for f in file{1..4}.txt; do 
        (head -4 "$f"; echo " Good Morning "; tail -n +6 "$f") > foobar && 
        mv foobar "$f"; 
    done 

    Explication:

    La boucle est expliquée dans la section précédente.

    • head -4: imprimer les 4 premières lignes

    • echo " Good Morning ": imprimer "Bonjour"

    • tail -n +6: imprime tout de la 6ème ligne à la fin du fichier

    Les parenthèses ( )autour de ces trois commandes vous permettent de capturer la sortie des trois (donc, les 4 premières lignes, puis "Bonjour", puis le reste des lignes) et de les rediriger vers un fichier.

terdon
la source
23

Vous pouvez utiliser sed:

sed '5s/^/Good morning /' file

s'ajouterait Good morningà la cinquième ligne d'un fichier.

Si vous souhaitez remplacer le contenu de la ligne 5 à la place, dites:

sed '5s/.*/Good morning/' file

Si vous souhaitez enregistrer les modifications du fichier sur place, utilisez l' -ioption:

sed -i '5s/.*/Good morning/' file

sedpeut gérer plusieurs fichiers à la fois. Vous pouvez simplement ajouter plus de noms de fichiers à la fin de la commande. Vous pouvez également utiliser des extensions bash pour faire correspondre des fichiers particuliers:

# manually specified
sed -i '5s/.*/Good morning/' file1.txt file2.txt file3.txt file4.txt

# wildcard: all files on the desktop
sed -i '5s/.*/Good morning/' ~/Desktop/*

# brace expansion: file1.txt, file2.txt, file3.txt, file4.txt
sed -i '5s/.*/Good morning/' file{1..4}.txt

Vous pouvez en savoir plus sur les extensions d'accolades ici .


Les versions GNU awk 4.1.0 et supérieures sont livrées avec une extension qui permet l'édition sur place. Vous pourriez donc dire:

gawk -i inplace 'NR==5{$0="Good morning"}7' file

pour remplacer la ligne # 5 du fichier par Good morning!

devnull
la source
2

Je n'ai pas vu de solution python alors voici:

import sys
import os
def open_and_replace(filename):

    with open(filename) as read_file:
        temp = open("/tmp/temp.txt","w")
        for index,line in enumerate(read_file,1):
            if  index == 5:
                temp.write("NEW STRING\n")
            else:
                temp.write(line.strip() + "\n")
        temp.close()
    os.rename("/tmp/temp.txt",filename)

for file_name in sys.argv[1:]:
    open_and_replace(file_name)

L'idée de base est que pour chaque fichier fourni sur la ligne de commande comme argument, nous écrivons un fichier temporaire et énumérons chaque ligne dans le fichier d'origine. Si l'index de la ligne est 5, nous écrivons une ligne différente. Le reste remplace simplement l'ancien fichier par le fichier temporaire Démo:

$> ls
file1.txt  file2.txt  file3.txt
$> cat file1.txt                                                               
line 1
line 2
line 3
line 4
GOOD MORNING
line 6
$> python ~/replace_5th_line.py file1.txt file2.txt  file3.txt                 
$> cat file1.txt                                                               
line 1
line 2
line 3
line 4
NEW STRING
line 6
$> cat file2.txt                                                               
line 1
line 2
line 3
line 4
NEW STRING
line 6

La même chose peut être obtenue avec la compréhension de la liste. Vous trouverez ci-dessous une ligne du même script:

cat /etc/passwd | python -c 'import sys; print "\n".join(["CUSTOM"  if index == 5 else line.strip() for index,line in enumerate(sys.stdin,1)])'

ou sans cat

python -c 'import sys; print "\n".join(["CUSTOM"  if index == 5 else line.strip() for index,line in enumerate(sys.stdin,1)])' < /etc/passwd

Il ne reste plus qu'à rediriger la sortie du contenu édité vers un autre fichier avec > output.txt

Sergiy Kolodyazhnyy
la source
1

Vous pouvez utiliser Vim en mode Ex:

for b in 1 2 3 4
do
  ex -sc '5c|Good Morning' -cx file"$b".txt
done
  1. 5 sélectionnez la 5ème ligne

  2. c remplacer le texte

  3. x écrire si des modifications ont été apportées (elles l'ont été) et quitter

Steven Penny
la source
0

Ce qui suit est un script bash qui termine le processus perl suggéré dans cette réponse

Exemple:

/ tmp / it

console:
  enabled:false

Exécutez ensuite ce qui suit:

$ search_and_replace_on_line_of_file.sh false true 2 /tmp/it

et le fichier contient maintenant

/ tmp / it

console:
  enabled:true

search_and_replace_on_line_of_file.sh

function show_help()
{
  IT=$(CAT <<EOF

  usage: SEARCH REPLACE LINE_NUM FILE

  e.g. 

  false true 52 /tmp/it  -> replaces false with true on line 52 of file /tmp/it

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$4" ]
then
  show_help
fi

SEARCH_FOR=$1
REPLACE_WITH=$2
LINE_NO=$3
FILE_NAME=$4
perl -i -pe "s/$SEARCH_FOR/$REPLACE_WITH/ if $.==$LINE_NO" $FILE_NAME 
Brad Parks
la source