CLI AI

Buscar y reemplazar en todos los archivos de un proyecto

2026-05-03

El comando es grep -rl 'old' . | xargs sed -i 's/old/new/g'. La mitad importante es la primera: grep -rl lista los archivos que cambiarían, así puedes revisarlos antes de que sed los reescriba en el sitio.

1. ¿Qué archivos cambiarían?

clai
$ clai lista los archivos dentro de proj que contienen old_name→ grep -rl 'old_name' proj/proj/src/b.pyproj/src/a.py

-r recorre en profundidad, -l imprime nombres de archivo en lugar de las líneas que coinciden. Dos archivos: es un número creíble. Si dijera 4000, tu patrón es demasiado amplio y acabas de ahorrarte una tarde.

2. Reemplazar exactamente en esos archivos

clai
$ clai reemplaza old_name por new_name en todos los archivos dentro de proj que lo contengan→ grep -rl 'old_name' proj/ | xargs sed -i 's/old_name/new_name/g'$ cat proj/src/a.pynew_name = 1

Solo se abren los archivos que coinciden. Ejecutar sed -i sobre todo el árbol en su lugar reescribiría el mtime de cada archivo del repositorio, incluidos los miles que no cambiaron.

3. Los nombres con espacios no lo rompen

clai
$ clai el mismo reemplazo pero maneja con seguridad nombres de archivo con espacios→ grep -rlZ 'old_name' proj/ | xargs -0 -r sed -i 's/old_name/new_name/g'

-Z hace que grep separe los nombres con un byte nulo y -0 hace que xargs lo espere así. Sin el par, mi archivo.py llega como dos argumentos y sed no edita ninguno. -r evita que xargs ejecute sed sin argumentos cuando no hubo coincidencias.

4. Saltar los directorios que nunca quieres tocar

clai
$ clai reemplázalo pero salta node_modules, .git y vendor→ grep -rlZ --exclude-dir={.git,node_modules,vendor} 'old_name' . | xargs -0 -r sed -i 's/old_name/new_name/g'

Sin exclusiones, tarde o temprano reescribirás el código fuente de una dependencia, y el diff tendrá 40.000 líneas.

5. Solo un tipo de archivo

clai
$ clai reemplázalo solo en archivos python→ grep -rlZ --include='*.py' 'old_name' . | xargs -0 -r sed -i 's/old_name/new_name/g'

--include es el gemelo de lista blanca de --exclude. Cuanto más estrecho, más seguro.

6. Guardar una copia de seguridad — y seguir siendo portable

clai
$ clai haz el reemplazo pero guarda una copia de seguridad de cada archivo, que funcione también en mac→ grep -rlZ 'old_name' proj/ | xargs -0 -r sed -i.bak 's/old_name/new_name/g'

sed -i.bak escribe a.py.bak junto a cada archivo editado. Es también la única forma que funciona tanto en GNU como en BSD sed: macOS exige el sufijo, Linux lo acepta.

Trampas

  • Tu patrón es una regex, no una cadena. ., *, / y $ son especiales. Reemplazar config.yml también coincide con configXyml; escápalo o usa un delimitador que no esté en el texto: sed -i 's|old/path|new/path|g'.
  • Haz commit primero. sed -i no tiene deshacer. Un git status limpio antes del reemplazo convierte un error en git checkout ..
  • grep -l se detiene en la primera coincidencia por archivo — por eso es rápido, y no afecta al reemplazo, ya que sed sigue procesando cada línea.

Preguntas relacionadas

¿Cómo previsualizo el reemplazo en vez de aplicarlo? Quita -i y deja que imprima: grep -rlZ 'old' . | xargs -0 sed 's/old/new/g' | less. O grep -rn 'old' . para ver cada línea con su número.

¿Por qué no pasó nada? Normalmente xargs recibió una lista vacía, o tu patrón no coincide con lo que crees. Ejecuta solo la mitad con grep -rl.

¿Puedo hacerlo con find en su lugar? Sí: find . -type f -name '*.py' -exec sed -i 's/old/new/g' {} +. Reescribe cada archivo que coincida por nombre, contenga o no el patrón, por eso la versión con grep -rl es preferible.

Ver también

Describe el reemplazo, lee el comando que escribe CliAI y ejecútalo. Instálalo en una línea.