Je souhaite remplacer la 5ème ligne de plusieurs fichiers texte (fichier1.txt, fichier2.txt, fichier3.txt, fichier4.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 est constitué de 6 fichiers .txt. Je souhaite appliquer la modification aux 4 fichiers texte susmentionnés uniquement.
Voici quelques approches. J'utilise extension d'accolade (file{1..4}.txt
), ce qui signifie file1.txt file2.txt file3.txt file4.txt
Perl
Perl -i -pe 's/.*/ Good Morning / if $.==5' file{1..4}.txt
-i
: permet à Perl
de modifier les fichiers en place, en modifiant le fichier d'origine.
Si -i
est suivi d'un suffixe d'extension de fichier, une sauvegarde est créée pour chaque fichier modifié. Ex: -i.bak
crée un file1.txt.bak
si file1.txt
est modifié pendant 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 à partir de la ligne de commande.
s/.*/ Good Morning /
: cela remplacera le texte de la ligne actuelle (.*
) par Good Morning.
$.
est une variable Perl spéciale qui contient le numéro de ligne actuel du fichier d'entrée. Ainsi, s/foo/bar/ if $.==5
, signifie remplacer foo
par bar
uniquement sur la 5ème ligne.
sed
sed -i '5s/.*/ Good Morning /' file{1..4}.txt
-i
: Comme pour Perl
, éditez le fichier à la place.Par défaut, sed
imprime chaque ligne du fichier d'entrée. Le 5s/pattern/replacement/
signifie un modèle de remplacement avec remplacement sur la 5ème ligne.
Awk
for f in file{1..4}.txt; do
awk 'NR==5{$0=" Good Morning "}1;' "$f" > foobar && mv foobar "$f";
done
awk
n'a pas d'équivalent à -i
option¹, ce qui signifie que nous devons créer un fichier temporaire (foobar
) qui est ensuite renommé pour écraser l'original. La boucle bash for f in file{1..4}.txt; do ... ; done
passe simplement dans chacun des file{1..4}.txt
, en enregistrant le nom du fichier actuel sous le nom $f
. Dans awk
, NR
est le numéro de la ligne en cours et $0
est le contenu de la ligne en cours. Ainsi, le script remplacera la ligne ($0
) par "Good Morning" uniquement sur la 5ème ligne. 1;
est awk
pour "print the line".
¹Les nouvelles versions font comme devnull l'a montré dans son réponse .
coreutils
for f in file{1..4}.txt; do
(head -4 "$f"; echo " Good Morning "; tail -n +6 "$f") > foobar &&
mv foobar "$f";
done
La boucle est expliquée dans la section précédente.
head -4
: affiche les 4 premières lignes
echo " Good Morning "
: print "Good Morning"
tail -n +6
: affiche tout depuis la 6ème ligne jusqu'à 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.
Vous pouvez utiliser sed
:
sed '5s/^/Good morning /' file
ajouterait Good morning
à la cinquième ligne d'un fichier.
Si vous souhaitez plutôt remplacer le contenu de la ligne 5, dites:
sed '5s/.*/Good morning/' file
Si vous souhaitez enregistrer les modifications dans le fichier, utilisez l'option -i
:
sed -i '5s/.*/Good morning/' file
sed
peut gérer plusieurs fichiers à la fois. Vous pouvez simplement ajouter d'autres noms de fichiers à la fin de la commande. Vous pouvez également utiliser les 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 expansions d'accolades ici .
Les versions 4.1.0 et supérieures de GNU awk sont accompagnées de ne extension qui permet l’édition sur place. Pour que vous puissiez dire:
gawk -i inplace 'NR==5{$0="Good morning"}7' file
remplacer la ligne n ° 5 dans le fichier par Good morning
!
Je n'ai pas vu la solution python alors le 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 en tant qu'argument, nous écrivons un fichier temporaire et énumérons chaque ligne du fichier d'origine. Si l'indice de la ligne est 5, nous écrivons une ligne différente. Le reste ne fait que remplacer l'ancien fichier par un 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 une compréhension de liste. 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é dans un autre fichier avec > output.txt
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
5
sélectionnez 5ème ligne
c
remplacer le texte
x
écrire si des modifications ont été apportées (elles l’ont fait) et quitter
Ce qui suit est un script bash qui termine le processus Perl suggéré dans cette réponse
/tmp/it
console:
enabled:false
Puis lancez 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
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