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?
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.
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.
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...
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.
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.
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
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' {} \;
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
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
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.
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:
trouver ./ - type f-nom "file*.txt"/xargs sed-i-e 's/abc/xyz / g'
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. :)
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()
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(¤t_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(¤t_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