Buscar y reemplazar dentro de un archivo de texto de un comando Bash

¿Cuál es la forma más sencilla de buscar y reemplazar una cadena de entrada determinada, por ejemplo abc , y reemplazarla con otra cadena, por ejemplo, XYZ en el archivo /tmp/file.txt ?

Estoy escribiendo una aplicación y estoy usando IronPython para ejecutar comandos a través de SSH, pero no conozco bien Unix y no sé qué buscar.

He oído que Bash, además de ser una interfaz de línea de comando, puede ser un lenguaje de scripting muy poderoso. Entonces, si esto es cierto, supongo que puedes realizar acciones como estas.

¿Puedo hacerlo con bash, y cuál es el guión más simple (una línea) para lograr mi objective?

La forma más fácil es usar sed (o perl):

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

Lo cual invocará a sed para hacer una edición in situ debido a la opción -i . Esto se puede llamar desde bash.

Si realmente quieres usar solo bash, entonces lo siguiente puede funcionar:

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

Esto recorre cada línea, realiza una sustitución y escribe en un archivo temporal (no desea bloquear la entrada). El movimiento al final solo se mueve temporalmente al nombre original.

La manipulación de archivos normalmente no es realizada por Bash, sino por progtwigs invocados por Bash, por ejemplo:

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

La bandera -i le dice que haga un reemplazo en el lugar.

Consulte man perlrun para más detalles, incluido cómo realizar una copia de seguridad del archivo original.

Me sorprendió porque tropecé con esto …

Hay un comando “reemplazar” que se envía con el paquete “mysql-server” , por lo que si lo tiene instalado pruébelo:

 # 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" 

Ver hombre reemplazar por más en esto …

Bash, como otros shells, es solo una herramienta para coordinar otros comandos. Normalmente, intentaría usar comandos estándar de UNIX, pero puede usar Bash para invocar cualquier cosa, incluidos sus propios progtwigs comstackdos, otros scripts de shell, scripts de Python y Perl, etc.

En este caso, hay un par de maneras de hacerlo.

Si desea leer un archivo y escribirlo en otro archivo, haciendo búsqueda / reemplazo a medida que avanza, use sed:

 sed 's/abc/XYZ/g' outfile 

Si desea editar el archivo en su lugar (como si estuviera abriendo el archivo en un editor, edítelo y luego guárdelo) suministre instrucciones al editor de línea ‘ex’

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

Ex es como vi sin el modo de pantalla completa. Puede darle los mismos comandos que en el prompt ‘:’ de vi.

Encontré este hilo entre otros y acepto que contiene las respuestas más completas, así que también agregué el mío:

1) sed y ed son tan útiles … ¡a mano! Mira este código de @Johnny:

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

2) cuando mi restricción es usarlo con un script de shell, ¡entonces no se puede usar ninguna variable en lugar de abc o XYZ! Esto parece estar de acuerdo con lo que entiendo al menos. Entonces, no puedo usar:

 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 

¿pero que podemos hacer? Como, @Johnny dijo usar un ‘mientras lees …’ pero, lamentablemente, ese no es el final de la historia. Lo siguiente funcionó bien conmigo:

 #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//''/$server}" line="${line//''/$alias}" line="${line//''/$user}" line="${line//''/$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) Como se puede ver si se establece nullglob, se comporta de manera extraña cuando hay una cadena que contiene un * como en

  ServerName www.example.com 

que se convierte

  

no hay un corchete de ángulo final y ¡Apache2 ni siquiera puede cargar!

4) Este tipo de análisis debe ser más lento que la búsqueda y reemplazo de un solo golpe, pero, como ya has visto, hay 4 variables para 4 patrones de búsqueda diferentes que funcionan de un solo ciclo de análisis.

La solución más adecuada que se me ocurre con las suposiciones dadas del problema.

Esta es una publicación anterior pero para cualquiera que quiera usar variables como @centurian dijo que las comillas simples significan que nada se expandirá.

Una forma simple de obtener variables es hacer una concatenación de cadenas, ya que esto se hace por yuxtaposición en bash, lo siguiente debería funcionar:

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

También puede usar el comando ed para realizar búsquedas en el archivo y reemplazar:

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

Ver más en el sitio de bash-hackers

Puedes usar sed

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

Use i para ignorar el caso si no está seguro de que el texto para encontrar es abc o ABC o AbC, …

Puedes usar find y sed si no tienes tu nombre de archivo ahora:

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

Buscar y reemplazar en todos los archivos de Python:

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

Tenga cuidado si reemplaza las URL con el carácter “/”.

Un ejemplo de cómo hacerlo:

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

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

Si el archivo en el que está trabajando no es tan grande, y almacenarlo temporalmente en una variable no es problema, entonces puede usar la sustitución de Bash string en todo el archivo a la vez, no hay necesidad de revisarlo línea por línea:

 file_contents=$( /tmp/file.txt 

El contenido del archivo completo se tratará como una cadena larga, incluidos los saltos de línea.

XYZ puede ser una variable, por ejemplo, $replacement , y una de las ventajas de no usar sed aquí es que no debe preocuparse de que la cadena de búsqueda o reemplazo contenga el carácter delimitador sed (generalmente, pero no necesariamente, /). Una desventaja es no poder usar expresiones regulares o cualquiera de las operaciones más sofisticadas de sed.

Para editar texto en el archivo de forma no interactiva, necesita un editor de texto in situ como vim.

Aquí hay un ejemplo simple de cómo usarlo desde la línea de comando:

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

Esto es equivalente a la respuesta @slim del editor ex , que es básicamente lo mismo.

Aquí hay algunos ejemplos prácticos.

Reemplazando texto foo con bar en el archivo:

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

Eliminación de espacios en blanco finales para varios archivos:

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

Ver también:

  • ¿Cómo editar archivos de forma no interactiva (por ejemplo, en la tubería)? en Vi SE

buscar ./ ​​-type f -name “archivo * .txt” | xargs sed -i-e ‘s / abc / xyz / g’

Puede usar el comando rpl. Por ejemplo, desea cambiar el nombre de dominio en todo el proyecto de php.

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

Esto no está claro bash de causa, pero es muy rápido y útil. 🙂

También puedes usar python dentro del script bash. No tuve mucho éxito con algunas de las principales respuestas aquí, y encontré que esto funciona sin la necesidad de bucles:

 #!/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() 

Ahora que este hilo parece haberse convertido en reemplazo de cadenas (de bytes) con cualquier otro idioma que no sea bash, aquí hay una estúpida implementación de C:

  /** * Usage: * ./replace "foobar" "foobaz" < input_file > output_file * Note: input_file and output_file should be different */ #include  #include  #include  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; } 

Comstackr y ejecutar:

 $ cc replace.c -oreplace $ ./replace "foobar" "foobaz" < input_file > output_file