Búsqueda recursiva y reemplazo en archivos de texto en Mac y Linux

127

En el shell de Linux, el siguiente comando buscará y reemplazará recursivamente todas las instancias de 'this' con 'that' (no tengo un shell de Linux frente a mí, pero debería hacerlo).

find . -name "*.txt" -print | xargs sed -i 's/this/that/g'

¿Cómo será un comando similar en OSX?

Jack
fuente
Probablemente debería mudarse apple.stackexchange.comya que no es lo suficientemente genérico para Linux ni para todos los desarrolladores.
AlikElzin-kilaka

Respuestas:

246

OS X utiliza una combinación de herramientas BSD y GNU, por lo que es mejor consultar siempre la documentación (aunque tenía una que lessni siquiera se ajustaba a la página de manual de OS X):

https://web.archive.org/web/20170808213955/https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/sed.1.html

sed toma el argumento después -icomo la extensión para las copias de seguridad. Proporcione una cadena vacía ( -i '') para que no haya copias de seguridad.

Lo siguiente debería hacer:

LC_ALL=C find . -type f -name '*.txt' -exec sed -i '' s/this/that/ {} +

El -type fes solo una buena práctica; sed se quejará si le das un directorio más o menos. -execse prefiere sobre xargs; no necesitas molestarte -print0ni nada. Al {} +final significa que findagregará todos los resultados como argumentos a una instancia del comando llamado, en lugar de volver a ejecutarlo para cada resultado. (Una excepción es cuando se infringe el número máximo de argumentos de línea de comandos permitidos por el sistema operativo; en ese caso findse ejecutará más de una instancia).

TaylanUB
fuente
2
Mi "this" en esta sustitución contiene una barra diagonal (localhost / sitio): estoy sustituyendo partes de una URL en un archivo .html ... ¿cómo hago tal sustitución? Intenté poner comillas dobles, pero falla.
Timothy T.
66
Sed sintaxis permite utilizar casi cualquier carácter en lugar de la barra, por ejemplo, se puede utilizar el %carácter: sed "s%localhost/site%blah/blah%". Otra alternativa es la barra invertida-escapar del separador: sed "s/localhost\/site/blah\/blah/".
TaylanUB
Gracias déjame probar eso. Sin embargo, intenté usar {} para separar la barra diagonal y aún recibí un error ...
Timothy T.
16
¿Alguien más recibe un illegal byte sequenceerror? si es así, intente: LC_ALL=C find . -type f -name '*.txt' -exec sed -i '' s/this/that/ {} +funcionó para mí.
ColdTuna
10
Esto reemplazará solo una ocurrencia por archivo, use /gpara múltiples ocurrencias comoLC_ALL=C find . -type f -exec sed -i '' s/search/replace/g {} +
jamesjara
152

Para el mac, un enfoque más similar sería este:

find . -name '*.txt' -print0 | xargs -0 sed -i "" "s/form/forms/g"
Guybrush Threepwood
fuente
10
Desearía poder votar esto cada vez que vuelva a usarlo. Ya estaría en +15, fácil.
Droogans
Por alguna razón, no funciona para mí. No hace nada Estoy dentro de la carpeta form360 y estoy tratando de cambiar todas las instancias de cadena con el nombre easyform a form360, estoy ejecutando el siguiente comando:find . -name '*.php' -print0 | xargs -0 sed -i "" "s/easyform/form360/g"
Andres Ramos
2
Para mí, esta debería ser la respuesta correcta. Es el único que funcionó para mí.
nosequeldeebee
1
-print0 | xargs -0 no funciona en mi mac cuando el nombre del archivo contiene espacio.
user2807219
1
. sed:: en lugar de editar sólo funciona para los archivos normales
codeslapper
14

Como solución alternativa, estoy usando esta en Mac OSX 10.7.5

grep -ilr 'old-word' * | xargs -I@ sed -i '' 's/old-word/new-word/g' @

El crédito es para: la respuesta de Todd Cesere

Maciej Gurban
fuente
Este funciona bien! ¡Otros scripts agregan un final de línea adicional en algunos casos en OSX! ¡Muchas gracias!
Sebastien Filion
14

En Mac OSX 10.11.5 esto funciona bien:

grep -rli 'old-word' * | xargs -I@ sed -i '' 's/old-word/new-word/g' @
madx
fuente
xargs -I @ sed -i '' 's / old-word / new-word / g' @ funcionó para mí después de probar tantas otras sugerencias que no funcionan. ¡Gracias!
DrB
13

Ninguno de los anteriores funciona en OSX.

Haz lo siguiente:

perl -pi -w -e 's/SEARCH_FOR/REPLACE_WITH/g;' *.txt
eb80
fuente
1
¿Cómo escapar '/' si SEARCH_FOR y REPLACE_WITH son rutas?
rraallvv
Usa un delimitador diferente. Si está utilizando rutas, dos puntos o una tubería funcionarían. 's | SEARCH | REPLACE | g', por ejemplo. Nuestro uso de llaves, como en 's {SEARCH} {REPLACE}'.
dannysauer
1
Pregunta de dito, intentando no en Mac, pero parece generar un error. Por ejemplo, ¿mi ruta se interpreta como un archivo? -bash: localhost / nohost: No existe tal archivo o directorio
Timothy T.
esto no se repite a través de carpetas profundas. Solo un nivel.
Sergey Romanov
Para obtener más información sobre este comando leer este lifehacker.com/5810026/...
kuzdu
7

Una versión que funciona tanto en Linux como en Mac OS X (agregando el -einterruptor a sed):

export LC_CTYPE=C LANG=C
find . -name '*.txt' -print0 | xargs -0 sed -i -e 's/this/that/g'
vroc
fuente
Tuve que hacer la exportación de esta respuesta + la línea de la respuesta aceptada (no quería que se generaran archivos de respaldo)
Lance
2
para abordar el error 'secuencia de bytes ilegal', intente configurar LOCALE antes de ejecutar el comando: export LC_CTYPE=C && export LANG=C
disfrute el
NUNCA EJECUTE esto con '*' en lugar de '* .filetype' como lo hice si está usando Git. O puedes despedirte de todo tu trabajo inédito.
Sergey
1
La versión mac del comando sed requiere un '' después de -i, por lo que esta respuesta es incorrecta
G Huxley
2

Cada vez que escribo este comando, parece que siempre lo manguito u olvido una bandera. Creé un Gist en github basado en la respuesta de TaylanUB que hace un reemplazo global del directorio actual. Esto es específico de Mac OSX.

https://gist.github.com/nateflink/9056302

Es bueno porque ahora solo abro una terminal y luego copio:

curl -s https://gist.github.com/nateflink/9056302/raw/findreplaceosx.sh | bash -s "find-a-url.com" "replace-a-url.com"

Puede obtener algunos errores de secuencia de bytes extraños, así que aquí está el código completo:

#!/bin/bash
#By Nate Flink

#Invoke on the terminal like this
#curl -s https://gist.github.com/nateflink/9056302/raw/findreplaceosx.sh | bash -s "find-a-url.com" "replace-a-url.com"

if [ -z "$1" ] || [ -z "$2" ]; then
  echo "Usage: ./$0 [find string] [replace string]"
  exit 1
fi

FIND=$1
REPLACE=$2

#needed for byte sequence error in ascii to utf conversion on OSX
export LC_CTYPE=C;
export LANG=C;

#sed -i "" is needed by the osx version of sed (instead of sed -i)
find . -type f -exec sed -i "" "s|${FIND}|${REPLACE}|g" {} +
exit 0
Nate Flink
fuente
2

Este es mi viable. en mac OS X 10.10.4

grep -e 'this' -rl . | xargs sed -i '' 's/this/that/g'

Los anteriores usan find para cambiar los archivos que no contienen el texto de búsqueda (agregue una nueva línea al final del archivo), que es detallado.

NoteCode
fuente
2

Si está utilizando un terminal zsh, puede usar comodines mágicos:

sed -i "" "s/search/high-replace/g" *.txt

Mentor
fuente
1

Utilicé este formato, pero ... descubrí que tenía que ejecutarlo tres o más veces para que cambiara cada instancia, lo que me pareció extremadamente extraño. Ejecutarlo una vez cambiaría algunos en cada archivo pero no todos. Ejecutar exactamente la misma cadena dos o cuatro veces capturaría todas las instancias.

find . -type f -name '*.txt' -exec sed -i '' s/thistext/newtext/ {} +
Emma
fuente
Necesitabas ejecutar este comando varias veces b / c, tu sed regex necesita ga al final, de lo contrario solo reemplaza la primera aparición de thistexten una línea. Entonces tu expresión regular debería sers/thistext/newtext/g
Les Nightingill
0

https://bitbucket.org/masonicboom/serp es una utilidad go (es decir, multiplataforma), probada en OSX, que realiza búsquedas y reemplazos recursivos de texto en archivos dentro de un directorio dado, y confirma cada reemplazo. Es nuevo, por lo que podría tener errores.

El uso se ve así:

$ ls test
a  d  d2 z
$ cat test/z
hi
$ ./serp --root test --search hi --replace bye --pattern "*"                         
test/z: replace hi with bye? (y/[n]) y
$ cat test/z
bye
usuario4425237
fuente
0
find . -type f | xargs sed -i '' 's/string1/string2/g'

Consulte aquí para más información.

usuario2725109
fuente
-3

El comando en OSX debe ser exactamente el mismo que en Unix bajo la bonita interfaz de usuario.

clops
fuente
Lo pensé mucho, pero no. Pone un ./ delante de los archivos cuando se alimenta a sed, por lo que sed se queja de "código de comando no válido". Tal vez solo estoy cansado ;-)
Jack
@JacobusR, corta y pega desde tu terminal OSX; debes ingresar algo ligeramente diferente.
Glenn Jackman
3
Es posible que desee -print0agregar a las findbanderas y -0a las xargsbanderas.
jmtd
También puede usar en -exec sed -i s/this/that/g {} \+lugar de -printy xargs
Marian
@JacobusR: esto funciona, incluso si hay un nombre de ruta './' principal. Sin embargo, si hay un espacio en el nombre del archivo, diga "./jacobus \ R.txt", el uso de xargspodría ver el delimitado por espacios "./jacobus" como un archivo para pasar sed, y luego con "R.txt" , ninguno de los cuales existe.
Brett Hale
-4

podría decir $ PWD en lugar de "."

Samuel Devlin
fuente