Cómo detectar el final de línea con sed

14

Estoy buscando una forma de ejecutar solo el reemplazo cuando el último personaje es una nueva línea, usando sed.

Por ejemplo:

lettersAtEndOfLine

se reemplaza, pero esto no es:

lettersWithCharacterAfter&

Como sedno funciona bien con las nuevas líneas, no es tan simple como

$ sed -E "s/[a-zA-Z]*\n/replace/" file.txt

¿Cómo se puede lograr esto?

Matthew D. Scholefield
fuente

Respuestas:

20

Con el estándar sed, nunca verá una nueva línea en el texto leído de un archivo. Esto se debe a que sedlee línea por línea y, por lo tanto, no hay una nueva línea al final del texto de la línea actual en sedel espacio del patrón. En otras palabras, sedlee datos delimitados por nueva línea, y los delimitadores no son parte de lo que sedve un script.

Las expresiones regulares se pueden anclar al final de la línea usando $(o al principio, usando ^). Anclar una expresión al inicio / final de una línea la obliga a coincidir exactamente allí, y no en cualquier lugar de la línea.

Si desea reemplazar cualquier cosa que coincida con el patrón [A-Za-z]*al final de la línea con algo, entonces ancle el patrón de esta manera:

[A-Za-z]*$

... lo obligará a coincidir al final de la línea y en ningún otro lugar.

Sin embargo, dado que [A-Za-z]*$tampoco coincide con nada (por ejemplo, la cadena vacía presente al final de cada línea), debe forzar la coincidencia de algo , por ejemplo, especificando

[A-Za-z][A-Za-z]*$

Entonces, su línea de comando sed será

$ sed 's/[A-Za-z][A-Za-z]*$/replace/' file.txt

No utilicé el -Einterruptor aquí porque no es necesario. Con eso, podrías haber escrito

$ sed -E 's/[A-Za-z]+$/replace/' file.txt

Es cuestión de gustos.

Kusalananda
fuente
Aunque sabía cómo hacerlo, obtendrás un +1 solo por usar el término técnico para ello. :) Entonces esto se llama anclaje - es bueno saberlo. Hasta ahora, siempre tuve que parafrasearlo ... Otra nota sobre+ : PUEDES usarlo incluso sin usar expresiones regulares extendidas, solo recuerda escribirlo como \+. Así sed -e 's/[A-Za-z]\+$/replace/' file.txtfuncionará perfectamente incluso sin GNU sedinstalado. Y no se debe olvidar: no lo use -E, ya que GNU sedno lo admite .
syntaxerror
1
@syntaxerror: creo que puede eliminar la última oración o al menos desmarcarla como gnu seddefinitivamente lo admite-E .
don_crissti
@don_crissti Bueno, pensé que has estado en esta red el tiempo suficiente para saber que no hay forma de deshacer partes de un comentario (a menos que lo reescribas por completo). Así que permítanme corregir: GNU sedpuede admitir "silenciosamente" -E, pero no está documentado en la página de manual (ni en el manual de Texinfo (revisado ambos)). Por lo tanto, supuse que no es compatible (que era una suposición incorrecta, después de todo). De todos modos, tienes razón, porque al menos GNU sedno se quejará si usas esta opción.
syntaxerror
@don_crissti Me alegro de haberlo hecho! Así, al menos, se ha confirmado que sed va a tomar una opción en particular que no ha sido adecuadamente documentado aún. Esto siempre es útil; Si nadie está al tanto de la falta de documentación, nadie lo solucionará.
syntaxerror
@syntaxerror, consulte unix.stackexchange.com/a/310454/135943 . Por supuesto, si tiene que trabajar con sistemas antiguos como RHEL 5, entonces usará una versión de GNU sed que no es compatible -E.
Comodín el
3
sed "s/[a-zA-Z]*$/replace/" input.txt > result.txt

O bien, la forma compleja larga e innecesaria:

He descubierto que esto se puede hacer, todavía usando sed, con la ayuda de tr. Puede asignar otro carácter para representar el final de la línea. Se debe usar otro carácter temporal, en este caso "` ". Usemos "~" para representar el final de la línea:

tr '\n' '`' <input.txt >output.txt
sed -i "s/`/~`/" output.txt
tr '`' '\n' <output.txt >result.txt

Y luego, para realizar la búsqueda y el reemplazo reales, use "~" en lugar de "\ n":

sed -i -E "s/[a-zA-Z]*~/replace/" result.txt

Y luego limpia el carácter extra en las otras líneas:

sed -i "s/~//" result.txt

Obviamente, todo esto se puede canalizar y generar algo como:

tr '\n' '`' <input.txt | sed -e "s/`/~`/" | tr '`' '\n' | sed -E -e "s/[a-zA-Z]*~/replace/" | sed "s/~//" > result.txt
Matthew D. Scholefield
fuente
3
No estoy seguro de entender ... ¿Por qué no anclas al final de la línea $? por ejemplos/[a-zA-Z]*$/replace/
don_crissti
1
2 puntos: 1) Es mejor usarlo en \+lugar de hacerlo, *ya que este último permite cero letras al final de la cadena; 2) Puedes usar una clase de personaje [[:alpha:]]. Entonces:sed 's/[[:alpha:]]\+$/replace/' file
Glenn Jackman
@glennjackman ¿Cuál es la barra diagonal inversa antes del plus? ¿No coincidiría con el carácter de suma?
Matthew D. Scholefield
1
GNU sed sin la -ropción utiliza esta sintaxis de expresión regular .
Glenn Jackman
0

Por el fragmento de código (roto) que publicó, parece que también desea reemplazar la nueva línea. En ese caso, el anclaje de expresiones regulares por sí solo no puede ayudarte. La siguiente es una solución:

sed '/[[:alpha:]]\+$/{N;s/[[:alpha:]]\+\n/replace/}' your_file

Desglosado:

  • /[a-zA-Z]\+$/{} significa aplicar lo que viene dentro de los curvas a las líneas que coinciden con la expresión regular.
  • La expresión regular es la que utiliza el anclaje como se ve en su propia respuesta , modificada para tener en cuenta los comentarios de Glenn Jackman .
  • Dentro de los curlies, Nsignifica "agregar la siguiente línea al búfer activo" (lo que sedllama el "espacio del patrón")
  • Finalmente, la s///declaración es su sustitución requerida. Ahora funciona porque el espacio del patrón contiene dos líneas sucesivas y, por lo tanto, la nueva línea forma parte de ella.
Joseph R.
fuente
0

Para encontrar el final de la línea, solo use el signo $ :

Sin ancla de final de línea:

sed -n '/pattern/p' file 

Sin ancla de final de línea:

sed -n '/pattern$/p' file
usuario desconocido
fuente