¿Grep para encontrar la línea correcta, sed para cambiar el contenido y luego volver a colocarla en el archivo original?

9

Estoy tratando de cambiar una sola palabra en una línea específica en un archivo, pero tengo algunos problemas para conectarlos todos juntos.

Básicamente, en una línea de mi archivo hay una palabra clave 'firmware_revision', y en esta línea (y solo en esta línea) quiero reemplazar la palabra 'prueba' con la palabra 'producción'.

Entonces puedo hacer esto:

grep 'firmware_revision' myfile.py | sed 's/test/production'

Esto seleccionará la línea que deseo y realizará la sustitución, pero no puedo encontrar la manera de colocar esta nueva línea en el archivo original para reemplazar la línea anterior. Obviamente no puedo simplemente redirigirlo de nuevo al archivo, entonces, ¿qué debo hacer?

Incluso si uso temporarios, al usar greppara obtener solo la línea que necesito, pierdo todos los demás datos en el archivo, por lo que ya no puedo redirigir todo a un archivo temporal y luego reemplazar el original con la temperatura.

Editar: alguien solicitó más información

Digamos que tengo un archivo lleno de líneas como esta

[
  ('key_name1', str, 'value1', 'Description'),
  ('key_name2', str, 'value2', 'Description'),
  ('key_name3', str, 'value3', 'Description'),
  ('firmware_revision', str, 'my-firmware-name-test', 'Firmware revision name')
]

ahora quiero escribir un script (idealmente de una línea) que encontrará la línea que contiene 'firmware_revision', y cambia todas las instancias de la palabra 'prueba' en esa línea a 'producción'. La palabra 'prueba' podría estar en otros lugares en ese archivo y no quiero que se cambien. Para que quede claro, quiero cambiar la línea anterior a

('firmware_revision', str, 'my-firmware-name-production', 'Firmware revision name')

¿Cómo hago esto?

John Allard
fuente
55
sedes muy potente, puede realizar ambas funciones ( grepy sustitución) pero necesitaremos más información sobre cómo se ve la línea para ayudarlo.
grochmal
Agregaré más información a la publicación original
John Allard
77
Prueba:sed -i.bak '/firmware_revision/ s/test/production/' myfile.py
John1024
1
Ah funcionó perfectamente! ¿Puedes explicar la sintaxis?
John Allard
@JohnAllard Muy bien. Agregué una respuesta con explicación.
John1024

Respuestas:

22

Tratar:

sed -i.bak '/firmware_revision/ s/test/production/' myfile.py

Aquí, /firmware_revision/actúa como una condición. Es cierto para las líneas que coinciden con la expresión regular firmware_revisiony falso para otras líneas. Si la condición es verdadera, se ejecuta el siguiente comando. En este caso, ese comando es un comando sustituto que reemplaza la primera aparición de testcon production.

En otras palabras, el comando s/test/production/se ejecuta solo en líneas que coinciden con la expresión regular firmware_revision. Todas las demás líneas pasan sin cambios.

Por defecto, sed envía su salida a la salida estándar. Sin embargo, usted quería cambiar el archivo en su lugar. Entonces, agregamos la -iopción. En particular, -i.bakhace que el archivo se cambie en su lugar con una copia de seguridad guardada con una .bakextensión.

Si ha decidido que el comando funciona para usted y desea vivir peligrosamente y no crear una copia de seguridad, entonces, con GNU sed (Linux), use:

sed -i '/firmware_revision/ s/test/production/' myfile.py

Por el contrario, en BSD (OSX), la -iopción debe tener un argumento. Si no desea mantener una copia de seguridad, proporcione un argumento vacío. Por lo tanto, use:

sed -i '' '/firmware_revision/ s/test/production/' myfile.py

Editar

En la edición de la pregunta, el OP pide que se sustituya cada aparición testen la línea production. En ese caso, agregamos la gopción al comando sustituto para un reemplazo global (para esa línea):

sed -i.bak '/firmware_revision/ s/test/production/g' myfile.py
John1024
fuente
55
Para completar, es posible que desee explicar la -i.bakparte también.
Stephen Harris
55
@StephenHarris Buen punto. Explicación de -iagregado.
John1024
Siento que probablemente podrías llegar a las estrellas, simplemente parado en el libro explicando todas las cosas que sedpueden hacer ...
KlaymenDK
@ John1024 Para las personas que no son BSD, ¿podría agregar las razones del primero ''después -i?
Hastur
3
OP quería cambiar todas las instancias de la palabra 'prueba' en esa línea a 'producción' . Es importante agregar / g al comando sed en este caso: de lo sed -i '/firmware_revision/ s/test/production/g' myfile.pycontrario, solo se cambia la primera instancia.
knub
4

En máquinas más antiguas con la vieja escuela sedque no admite la opción -i:

TF=$( mktemp -t "${0##*/}"_$$_XXXXXXXX ) && \
trap 'rm -f "$TF"' EXIT HUP INT QUIT TERM && \
sed '/firmware_revision/ s/test/production/' myfile.py >"$TF" && \
mv -f "$TF" myfile.py
Satō Katsura
fuente
1
En esas máquinas más antiguas, simplemente puede usar edy hacerlo en una línea:printf %s\\n g/firmware_revision/s/test/production/g w q | ed -s myfile.py
don_crissti
1
@don_crissti Muy cierto, pero ed(1)no ofrece ningún pretexto para mostrar el uso de mktemp(1).
Satō Katsura
Si está utilizando un archivo temporal, también puede mantener el inodo y las permanentes del archivo original, igual que lo edhace. En lugar de mv -f "$TF" myfile.plusar cat "$TF" > myfile.pl && rm -f "$TF". Por cierto, es una buena práctica usar nombres de variables en minúsculas (en $tflugar de $TF), se garantiza que no entren en conflicto con ningún vars incorporado en bash (probablemente también otros shells bourne).
cas
@cas Re:: cat "$TF" > myfile.plprueba esto: touch a b; chmod 0444 a; cat b >a(Por cierto, sed -itampoco funcionará en ese caso). Mejor deje que el usuario se ocupe de eso. Re:: rm -f "$TF"no es necesario, ver trap ... EXIT. Re: en $tflugar de $TF: tal vez; Es una cuestión de estilo.
Satō Katsura
Ese es el comportamiento normal y esperado de los permisos de archivos Unix. Mi punto fue que crear un nuevo archivo y mezclarlo sobre el 1. original dará como resultado un nuevo inodo para ese nombre de archivo (rompiendo los enlaces duros). 2. posiblemente cambie los permisos, si la corriente umask difiere de los permisos originales. En cuanto a los mayúsculas, no es solo una cuestión de estilo: muchas personas que han cometido el error de usar mayúsculas PATH o RANDOM o SHELL o lo que sea para sus propias variables lo han descubierto de la manera difícil. (y sí, a menudo uso mayúsculas también. Sé que está mal, estoy tratando de romper el hábito).
cas