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

Quelle est la façon la plus simple de trouver et de remplacer une chaîne de caractères donnée, de dire abc , et de remplacer par une autre chaîne de caractères, de dire XYZ dans 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 chercher.

J'ai entendu dire que Bash, en plus d'être une interface en ligne de commande, peut être un langage de script très puissant. Donc, si c'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?

387
demandé sur Ash 2009-02-08 14:57:39

15 réponses

la manière la plus simple est d'utiliser sed (ou perl):

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

qui va invoquer sed pour faire une édition en place due à l'option -i . Cela peut être appelé de bash.

Si vous voulez vraiment utiliser juste bash, puis le suivant:

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

cette boucle au-dessus de chaque ligne, en faisant une substitution, et en écrivant à un dossier temporaire (ne veulent pas clobber l'entrée). Le mouvement à la fin se déplace juste temporaire au nom original.

672
répondu johnny 2012-12-26 16:27:54

la manipulation de fichiers n'est normalement pas faite par Bash, mais par des programmes invoqués par Bash, par exemple:

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

le drapeau -i lui indique de faire un remplacement en place.

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

148
répondu Alnitak 2013-12-05 12:01:48

j'ai été surpris parce que j'ai trébuché sur ceci...

il y a un "remplacer" commande quels navires avec le paquet "mysql-Serveur" , donc si vous l'avez installé essayer:

# 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 l'homme remplacer pour en savoir plus sur cette...

59
répondu rayro 2013-04-09 15:35:27

Bash, comme les autres shells, est juste un outil pour coordonner les autres commandes. En général, vous essayez D'utiliser les commandes UNIX standard, mais vous pouvez bien sûr utiliser Bash pour invoquer n'importe quoi, y compris vos propres programmes compilés, d'Autres scripts shell, python et scripts Perl, etc.

dans ce cas, il y a plusieurs façons de le faire.

Si vous voulez lire un fichier, et l'écrire dans un autre fichier, recherche/remplacer comme vous allez, utiliser sed:

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

si vous souhaitez éditer le fichier en place (comme si vous ouvrez le fichier dans un éditeur, l'éditez, puis le Sauvegardez), fournir des instructions à l'éditeur de ligne '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 qu'à l'invite ':' de vi.

37
répondu slim 2009-02-08 12:35:54

trouvé ce fil entre autres et je suis d'accord qu'il contient les réponses les plus complètes, donc j'ajoute le mien aussi:

1) sed et ed sont so useful...by main!!! Regardez ce code de @Johnny:

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

2) Quand ma restriction est de l'utiliser par un script shell alors, Aucune variable ne peut être utilisée à l'intérieur à la place de abc ou XYZ! Ce semble 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, @Johnny, a déclaré l'utilisation d'un tout lu..."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 mis 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 support d'angle de fin et Apache2 ne peut même pas charger!

4) Ce genre d'analyse devrait être plus lente que la recherche d'un seul coup et remplacer, mais, comme vous l'avez déjà vu, il ya 4 variables pour 4 différents modèles de recherche travailler à partir d'un seul cycle d'analyse!

la solution la plus appropriée je peux penser avec les hypothèses données du problème.

32
répondu centurian 2013-02-07 14:48:02

C'est un vieux post mais pour quiconque voulant utiliser des variables comme @centurian dit que les guillemets simples signifient que rien ne sera élargi.

une façon simple d'obtenir des variables est de faire la concaténation de chaîne de caractères puisque ceci est fait par juxtaposition dans bash ce qui suit devrait fonctionner:

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

30
répondu zcourts 2015-12-07 12:48:56

vous pouvez utiliser sed

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

utilisez I pour ignorer cas si vous n'êtes pas sûr que le texte à trouver est abc ou ABC ou AbC,...

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' {} \;

trouver et remplacer dans tous les fichiers python:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;
12
répondu MmParvin 2017-01-14 09:45:22

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

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

voir plus sur bash-hackers site

11
répondu olegtaranenko 2015-11-13 17:25:54

faites attention si vous remplacez les URLs par un 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

6
répondu Linux4you 2015-07-27 19:08:59

Si le fichier sur lequel vous travaillez n'est pas si grand, et temporairement stocker dans une variable n'est pas un problème, vous pouvez utiliser Bash substitution de chaîne sur l'ensemble du dossier à la fois - il n'y a pas besoin d'aller plus ligne par ligne:

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

l'ensemble du contenu du fichier sera traité comme une chaîne longue, y compris les linebreaks.

XYZ peut être une variable par exemple $replacement , et un avantage de ne pas utiliser sed ici est que vous n'avez pas besoin d'être concerné que la chaîne de recherche ou de remplacement peut contenir le caractère de délimiteur de motif (sed pattern delimiter) (habituellement, 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.

4
répondu johnraff 2017-04-15 02:09:16

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

voici un exemple simple comment l'utiliser à partir de la ligne de commande:

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

c'est l'équivalent de @slim answer de ex éditeur qui est essentiellement la même chose.

voici quelques ex exemples pratiques.

remplaçant le texte foo avec bar dans le fichier:

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

suppression des espaces vides pour plusieurs fichiers:

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

voir aussi:

3
répondu kenorb 2017-05-23 11:47:30

trouver ./ - type f-nom "file*.txt"/xargs sed-i-e 's/abc/xyz / g'

2
répondu J Ajay 2016-05-31 23:55:34

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

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

ce n'est pas clair bash de cause, mais c'est très rapide et utile. :)

1
répondu zalex 2015-02-12 08:34:49

vous pouvez aussi 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 fonctionne sans le 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()
1
répondu micalith 2016-05-03 18:44:19

maintenant que ce thread semble s'être transformé en remplaçant des chaînes (d'octets) par n'importe quel autre langage que bash, voici une implémentation stupide de C:

<!-- language: c -->

/**
 * Usage:
 *      ./replace "foobar" "foobaz" < input_file > output_file
 * Note: input_file and output_file should be different
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct string_t {
    const char * value;
    size_t length;
} string;

struct parser_t {
    string match_text, replace_text;

    char * match_buffer;
    unsigned int match_buffer_index;

    enum { STATE_INVALID, STATE_IN, STATE_OUT } state;
};

void parser_init(struct parser_t * parser, 
                 const char * match_text,
                 const char * replace_text)
{
    memset(parser, 0, sizeof(struct parser_t));

    parser->match_text.value = match_text;
    parser->match_text.length = strlen(match_text);
    parser->replace_text.value = replace_text;
    parser->replace_text.length = strlen(replace_text);
    parser->state = STATE_OUT;
    parser->match_buffer = malloc(parser->match_text.length);
}

void parser_free(struct parser_t * parser)
{
    free(parser->match_buffer);
}

void output_char(char current_char)
{
    fwrite(&current_char, sizeof(char), 1, stdout);
}

void buffer_match(struct parser_t * parser, char current_char)
{
    parser->match_buffer[parser->match_buffer_index++] = current_char;
}

void buffer_clear(struct parser_t * parser)
{
    parser->match_buffer_index = 0;
}

void buffer_flush(struct parser_t * parser)
{
    if (parser->match_buffer_index > 0) {
        fwrite(parser->match_buffer, sizeof(char), parser->match_buffer_index, stdout);
        buffer_clear(parser);
    }
}

int process_state_in(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    }

    if (parser->match_buffer_index == parser->match_text.length) {
        fwrite(parser->replace_text.value, sizeof(char), parser->replace_text.length, stdout);
        buffer_clear(parser);

        output_char(current_char);

        return STATE_OUT;
    }

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int process_state_out(struct parser_t * parser, char current_char)
{
    if (parser->match_text.value[parser->match_buffer_index] == current_char) {
        buffer_match(parser, current_char);

        return STATE_IN;
    } 

    if (parser->match_text.value[parser->match_buffer_index] != current_char) {
        buffer_flush(parser);
        output_char(current_char);

        return STATE_OUT;
    }

    return STATE_INVALID;
}

int main(int argc, char *argv[])
{
    char current_char;
    struct parser_t parser;

    if (argc != 3) {
        fprintf(stdout, "Usage:\n\t%s match_text replace_text < in_file > out_file\n\t# note in_file and out_file should be different.\n", argv[0]);
        return 0;
    }

    parser_init(&parser, argv[1], argv[2]);

    while (fread(&current_char, sizeof(char), 1, stdin) != 0) {
        switch (parser.state) {
            case STATE_IN:
            {
                parser.state = process_state_in(&parser, current_char);
            }
            break;
            case STATE_OUT:
            {
                parser.state = process_state_out(&parser, current_char);
            }
            break;
            default:
                fprintf(stderr, "Error: Invalid state.\n");
                return -1;
            break;
        }
    }

    parser_free(&parser);

    return 0;
}

compiler et exécuter:

$ cc replace.c -oreplace
$ ./replace "foobar" "foobaz" < input_file > output_file
-1
répondu neuro_sys 2018-04-11 21:45:23