Cómo reemplazar una línea completa en un archivo de texto por número de línea

152

Tengo una situación en la que quiero que un script bash reemplace una línea completa en un archivo. El número de línea es siempre el mismo, por lo que puede ser una variable codificada.

No estoy tratando de reemplazar alguna subcadena en esa línea, solo quiero reemplazar esa línea completamente con una nueva línea.

¿Hay algún método bash para hacer esto (o algo simple que pueda ser arrojado a un script .sh)?

usuario788171
fuente

Respuestas:

228

No es el mejor, pero esto debería funcionar:

sed -i 'Ns/.*/replacement-line/' file.txt

donde Ndebe ser reemplazado por su número de línea objetivo. Esto reemplaza la línea en el archivo original. Para guardar el texto modificado en un archivo diferente, suelte la -iopción:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt
chepner
fuente
1
¿Hay alguna manera de hacer que sed modifique el archivo en cuestión en lugar de volcarlo en la pantalla?
user788171
8
Para mí que dice: sed: -e expression #1, char 26: unknown option to ``s'y mi línea es: sed -i '7s/.*/<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpoint/</param-value>/' $TCE_SVN_HOME\trunk\tce\EWC\WebContent\WEB-INF\web.xml. ¿Alguna idea?
Danijel
44
Cada uno /en el texto de reemplazo se interpretará como la barra diagonal de cierre del scomando a menos que se haya escapado ( \/). Sin embargo, lo más fácil de hacer es elegir un personaje diferente sin usar como delimitador. Por ejemplo, sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk....
chepner
44
Esa explicación es un poco confusa y no parece funcionar. Si tienen problemas con sed y sus capacidades de configuración del delimitador, tal vez quieran ver esto: grymoire.com/Unix/Sed.html#uh-2 Dice que el primer carácter detrás de sdetermina el delimitador. Entonces, para cambiar su comando para usar, por ejemplo #, sería así:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der
77
Para expandirse en @ meonlol's, macOS también requiere un -eif -i; por lo tanto, el comando correcto se convierte en:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE
44

De hecho, usé este script para reemplazar una línea de código en el archivo cron en los servidores UNIX de nuestra compañía hace un tiempo. Lo ejecutamos como script de shell normal y no tuvimos problemas:

#Create temporary file with new line in place
cat /dir/file | sed -e "s/the_original_line/the_new_line/" > /dir/temp_file
#Copy the new file over the original file
mv /dir/temp_file /dir/file

Esto no va por número de línea, pero puede cambiar fácilmente a un sistema basado en un número de línea colocando el número de línea antes s/y colocando un comodín en lugar de the_original_line.

Kyle
fuente
17
Uso inútil del gato:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner
24
Inútil para el intérprete / compilador tal vez, pero no para un humano que lo lea. El código de @ Kyle se lee muy bien de izquierda a derecha; el idioma que usaste IMO no, debido al hecho de que el verbo está antes del sustantivo.
Clayton Stanley
1
¿Hay alguna manera de fusionar esas dos líneas, por ejemplo, por algo comosed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda el
22

Supongamos que desea reemplazar la línea 4 con el texto "diferente". Puedes usar AWK así:

awk '{ if (NR == 4) print "different"; else print $0}' input_file.txt > output_file.txt

AWK considera que la entrada son "registros" divididos en "campos". Por defecto, una línea es un registro. NRes la cantidad de registros vistos. $0representa el registro completo actual (mientras que $1es el primer campo del registro y así sucesivamente; de ​​forma predeterminada, los campos son palabras de la línea).

Entonces, si el número de línea actual es 4, imprima la cadena "diferente" pero de lo contrario imprima la línea sin cambios.

En AWK, el código del programa incluido en { }ejecuciones una vez en cada registro de entrada.

Debe citar el programa AWK en comillas simples para evitar que el shell intente interpretar cosas como la $0.

EDITAR: Un programa AWK más corto y elegante de @chepner en los comentarios a continuación:

awk 'NR==4 {$0="different"} { print }' input_file.txt

Solo para el registro (es decir, la línea) número 4, reemplace todo el registro con la cadena "diferente". Luego, para cada registro de entrada, imprima el registro.

¡Claramente mis habilidades de AWK están oxidadas! Gracias, @chepner.

EDITAR: y vea también una versión aún más corta de @Dennis Williamson:

awk 'NR==4 {$0="different"} 1' input_file.txt

Los comentarios explican cómo funciona esto: 1siempre evalúa verdadero, por lo que el bloque de código asociado siempre se ejecuta. Pero no hay un bloque de código asociado, lo que significa que AWK realiza su acción predeterminada de solo imprimir toda la línea. AWK está diseñado para permitir programas concisos como este.

steveha
fuente
3
Un poco más corto: awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner
2
@chepner: Un poco más corto:awk 'NR==4 {$0="different"}1' input_file.txt
Pausado hasta nuevo aviso.
@DennisWilliamson, ¡ni siquiera sé cómo funciona eso! ¿Qué hace ese rastro 1? (Nota: ¡Lo probé y de hecho funciona! Simplemente no entiendo cómo.)
steveha
1
¡Pensé que habría una manera de abreviar la impresión incondicional! @stevaha: el 1 es solo un valor verdadero, lo que significa un "patrón" que siempre coincide. Y la acción predeterminada para una coincidencia es imprimir la línea actual.
chepner
19

Dado este archivo de prueba (test.txt)

Lorem ipsum dolor sit amet,
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

el siguiente comando reemplazará la primera línea a "texto de nueva línea"

$ sed '1 c\
> newline text' test.txt

Resultado:

newline text
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

más información se puede encontrar aquí

http://www.thegeekstuff.com/2009/11/unix-sed-tutorial-append-insert-replace-and-count-file-lines/

Eric Robinson
fuente
2
Esta debería ser la respuesta correcta. La bandera c hace exactamente lo que se requiere (reemplaza una línea numerada con el texto dado).
adelphus
Sintaxis alternativa con sed que no hace eco del archivo en stdout: stackoverflow.com/a/13438118/4669135
Gabriel Devillers
Es la respuesta correcta, pero ¿por qué el salto de línea feo? sed '1 cnewline text' test.txtlo haría también
dev-null
7

en bash, reemplace N, M por los números de línea y xxx aaa por lo que desee

i=1
while read line;do
  if((i==N));then
    echo 'xxx'
  elif((i==M));then
    echo 'yyy'
  else
    echo "$line"
  fi
  ((i++))
done  < orig-file > new-file

EDITAR

De hecho, en esta solución hay algunos problemas, con los caracteres "\ 0" "\ t" y "\"

"\ t", se puede resolver poniendo IFS = antes de leer: "\", al final de la línea con -r

IFS= read -r line

pero para "\ 0", la variable se trunca, no existe una solución en bash puro: Asigne una cadena que contenga caracteres nulos (\ 0) a una variable en Bash Pero en el archivo de texto normal no hay caracteres nul \ 0

Perl sería una mejor opción

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file
Nahuel Fouilleul
fuente
¡Ni siquiera pensé en intentar una solución BASH pura!
steveha
4
# Replace the line of the given line number with the given replacement in the given file.
function replace-line-in-file() {
    local file="$1"
    local line_num="$2"
    local replacement="$3"

    # Escape backslash, forward slash and ampersand for use as a sed replacement.
    replacement_escaped=$( echo "$replacement" | sed -e 's/[\/&]/\\&/g' )

    sed -i "${line_num}s/.*/$replacement_escaped/" "$file"
}
Daniel Bigham
fuente
3

Excelente respuesta de Chepner. Me está funcionando en bash Shell.

 # To update/replace the new line string value with the exiting line of the file
 MyFile=/tmp/ps_checkdb.flag

 `sed -i "${index}s/.*/${newLine}/" $MyFile`

aquí
index- Línea no
newLine- nueva cadena de línea que queremos reemplazar.


De manera similar, el siguiente código se usa para leer una línea particular en el archivo. Esto no afectará el archivo real.

LineString=`sed "$index!d" $MyFile` 

aquí
!d: eliminará las líneas que no sean la línea no $index Por lo tanto, obtendremos el resultado como una cadena de línea de no $indexen el archivo.

Kanagavelu Sugumar
fuente
pero es mi culpa, ¿por qué solo se muestra el resultado ${newline}?
sikisis
1

En mac usé

sed -i '' -e 's/text-on-line-to-be-changed.*/text-to-replace-the=whole-line/' file-name
nakeer
fuente
-2

Incluso puede pasar parámetros al comando sed:

test.sh

#!/bin/bash
echo "-> start"
for i in $(seq 5); do
  # passing parameters to sed
  j=$(($i+3))
  sed -i "${j}s/.*/replaced by '$i'!/" output.dat
done
echo "-> finished"
exit 

salida original.dat:

a
b
c
d
e
f
g
h
i
j

Ejecutar ./test.sh proporciona el nuevo output.dat

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j
AndreH
fuente
1
¿Por qué necesita un bucle para este ejemplo, solo ofusca la solución? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob
No necesitas un bucle para esto
chandu vutukuri