web-dev-qa-db-fra.com

Rechercher et remplacer à l'intérieur d'un fichier texte à partir d'une commande Bash

Quel est le moyen le plus simple de rechercher et de remplacer une chaîne d'entrée donnée, par exemple, abcname__, et de la remplacer par une autre chaîne, par exemple, XYZdans le fichier /tmp/file.txt?

J'écris une application et j'utilise IronPython pour exécuter des commandes via SSH - mais je ne connais pas très bien Unix et je ne sais pas quoi rechercher.

J'ai entendu dire que Bash, en plus d'être une interface de ligne de commande, peut être un langage de script très puissant. Donc, si cela est vrai, je suppose que vous pouvez effectuer des actions comme celles-ci.

Puis-je le faire avec bash, et quel est le script le plus simple (une ligne) pour atteindre mon objectif?

482
Ash

Le moyen le plus simple consiste à utiliser sed (ou Perl):

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

Qui invitera sed à effectuer une modification sur place en raison de l'option -i. Cela peut être appelé à partir de bash.

Si vous voulez vraiment utiliser juste bash, alors ce qui suit peut marcher:

while read a ; do echo ${a//abc/XYZ} ; done < /tmp/file.txt > /tmp/file.txt.t ; mv /tmp/file.txt{.t,}

Cela boucle sur chaque ligne, en effectuant une substitution et en écrivant dans un fichier temporaire (vous ne voulez pas masquer l'entrée). Le mouvement à la fin ne fait que déplacer temporairement le nom d'origine.

813
johnny

La manipulation de fichier n’est normalement pas effectuée par Bash, mais par des programmes appelés par Bash, par exemple:

> Perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

L’indicateur -i lui indique de procéder à un remplacement sur place.

Voir man perlrun pour plus de détails, y compris comment faire une sauvegarde du fichier original.

156
Alnitak

J'ai été surpris parce que je suis tombé sur cela ...

Il y a une commande "replace" qui est livrée avec le paquet "mysql-server", donc si vous l'avez installé, essayez-le:

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

Voir man replace pour plus d'informations à ce sujet ...

65
rayro

Ceci est un vieux post mais pour ceux qui veulent utiliser des variables comme @centurian, les guillemets simples signifient que rien ne sera développé.

Un moyen simple d’obtenir des variables est de faire la concaténation de chaînes puisque cela se fait par juxtaposition en bash, les opérations suivantes devraient fonctionner:

sed -i -e 's/'"$var1"'/'"$var2"'/g' /tmp/file.txt

40
zcourts

Bash, comme d’autres shells, n’est qu’un outil de coordination d’autres commandes. En règle générale, vous essayez d'utiliser des commandes UNIX standard, mais vous pouvez bien sûr utiliser Bash pour appeler n'importe quoi, y compris vos propres programmes compilés, d'autres scripts Shell, les scripts Python et Perl, etc.

Dans ce cas, il y a deux façons de le faire.

Si vous voulez lire un fichier et l'écrire dans un autre fichier, effectuez une recherche/remplacement au fur et à mesure, utilisez sed:

sed 's/abc/XYZ/g' <infile >outfile

Si vous souhaitez modifier le fichier à la place (comme si vous ouvriez le fichier dans un éditeur, puis le sauvegardiez), fournissez des instructions à l'éditeur de lignes 'ex'.

echo "%s/abc/XYZ/g
w
q
" | ex file

Ex est comme vi sans le mode plein écran. Vous pouvez lui donner les mêmes commandes que chez vi '': 'Invite.

38
slim

J'ai trouvé ce fil parmi d'autres et je conviens qu'il contient les réponses les plus complètes. J'ajoute donc le mien aussi:

1) sed et ed sont si utiles ... à la main !!! Regardez ce code de @Johnny:

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

2) lorsque ma restriction est de l’utiliser par un script Shell, aucune variable ne peut être utilisée à la place de abc ou XYZ! This semble être d'accord avec ce que je comprends au moins. Donc, je ne peux pas utiliser:

x='abc'
y='XYZ'
sed -i -e 's/$x/$y/g' /tmp/file.txt
#or,
sed -i -e "s/$x/$y/g" /tmp/file.txt

mais que pouvons-nous faire? Comme l'a dit @Johnny, utilisez le mot "tant que vous lisez ...", mais malheureusement, ce n'est pas la fin de l'histoire. Ce qui suit a bien fonctionné avec moi:

#edit user's virtual domain
result=
#if nullglob is set then, unset it temporarily
is_nullglob=$( shopt -s | egrep -i '*nullglob' )
if [[ is_nullglob ]]; then
   shopt -u nullglob
fi
while IFS= read -r line; do
   line="${line//'<servername>'/$server}"
   line="${line//'<serveralias>'/$alias}"
   line="${line//'<user>'/$user}"
   line="${line//'<group>'/$group}"
   result="$result""$line"'\n'
done < $tmp
echo -e $result > $tmp
#if nullglob was set then, re-enable it
if [[ is_nullglob ]]; then
   shopt -s nullglob
fi
#move user's virtual domain to Apache 2 domain directory
......

3) Comme on peut voir si nullglob est défini alors, il se comporte étrangement quand il y a une chaîne contenant un * comme dans

<VirtualHost *:80>
 ServerName www.example.com

qui devient

<VirtualHost ServerName www.example.com

il n'y a pas de cornière d'extrémité et Apache2 ne peut même pas charger

4) Ce type d'analyse devrait être plus lent que la recherche en une frappe et son remplacement, mais, comme vous l'avez déjà vu, il y a 4 variables pour 4 modèles de recherche différents élaborés à partir d'un seul cycle d'analyse!

La solution la plus appropriée à laquelle je puisse penser avec les hypothèses données du problème.

32
centurian

Vous pouvez utiliser sed

sed -i 's/abc/XYZ/gi' /tmp/file.txt

Utilisez -i pour ignore case si vous n'êtes pas sûr que le texte à rechercher est abc ou ABC ou AbC, etc.

Vous pouvez utiliser find et sed si vous ne connaissez pas votre nom de fichier:

 find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

Rechercher et remplacer dans tous les fichiers python:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;
19
MMParvin

Vous pouvez également utiliser la commande ed pour effectuer une recherche dans le fichier et la remplacer:

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

Voir plus sur site bash-hackers

11
chrio

Faites attention si vous remplacez les URL par le caractère "/".

Un exemple de la façon de le faire:

sed -i "s%http://domain.com%http://www.domain.com/folder/%g" "test.txt"

Extrait de: http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html

8
Linux4you

Si le fichier sur lequel vous travaillez n'est pas si volumineux et que le stocker temporairement dans une variable ne pose aucun problème, vous pouvez utiliser la substitution de chaîne Bash en une fois pour tout le fichier - vous n'avez pas besoin de la parcourir ligne par ligne:

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

Le contenu du fichier entier sera traité comme une longue chaîne, incluant les sauts de ligne.

XYZ peut être une variable, par exemple $replacement, et l'un des avantages de ne pas utiliser sed est que vous n'avez pas à craindre que la chaîne de recherche ou de remplacement puisse contenir le caractère délimiteur du modèle sed (généralement, mais pas nécessairement, /). Un inconvénient est de ne pas pouvoir utiliser les expressions régulières ou les opérations plus sophistiquées de sed.

8
johnraff

Essayez la commande Shell suivante:

find ./  -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'
5
J Ajay

Pour éditer du texte dans le fichier de manière non interactive, vous avez besoin d'un éditeur de texte sur place tel que vim.

Voici un exemple simple d'utilisation depuis la ligne de commande:

vim -esnc '%s/foo/bar/g|:wq' file.txt

Ceci est équivalent à @ réponse mince de ex éditeur qui est fondamentalement la même chose.

Voici quelques exemples pratiques exname__.

Remplacer le texte foopar bardans le fichier:

ex -s +%s/foo/bar/ge -cwq file.txt

Suppression des espaces finaux pour plusieurs fichiers:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

Dépannage (lorsque le terminal est bloqué):

  • Ajoutez -V1 param pour afficher les messages détaillés.
  • Forcer à quitter par: -cwq!.

Voir également:

4
kenorb

Vous pouvez également utiliser python dans le script bash. Je n'ai pas eu beaucoup de succès avec certaines des meilleures réponses ici, et j'ai trouvé que cela fonctionnait sans avoir besoin de boucles:

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()
2
micalith

Vous pouvez utiliser la commande rpl. Par exemple, vous voulez changer le nom de domaine dans tout le projet php.

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

Ce n’est pas clair, mais c’est très rapide et utile. :)

1
zalex