¿Cómo agregar Línea a la Línea anterior?


9

Tengo un archivo de registro que debe analizarse y analizarse. El archivo contiene algo similar como a continuación:

Expediente:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

jwdjamc ksadkek} ssfjddkc * kdlsdl
sddsfd jfkdfk 
20141101 server contain dump

Según el escenario anterior, tengo que verificar si la línea de inicio no contiene la fecha o el número que debo agregar a la línea anterior.

Archivo de salida:

20141101 server contain dump
20141101 server contain nothing {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdl sddsfd jfkdfk 
20141101 server contain dump

Respuestas:


11

Una versión en perl, usando lookaheads negativos:

$ perl -0pe 's/\n(?!([0-9]{8}|$))//g' test.txt
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk
20141101 server contain dump

-0permite que la expresión regular coincida en todo el archivo y \n(?!([0-9]{8}|$))tiene un aspecto negativo hacia adelante, lo que significa una nueva línea no seguida por 8 dígitos o el final de la línea (que, con -0, será el final del archivo).


@terdon, actualizado para guardar la última línea nueva.
muru

¡Buena esa! Te votaré pero me temo que ya lo había hecho :)
terdon

No, -0si es para registros delimitados por NUL. Úselo -0777para sorber todo el archivo en la memoria (que no necesita aquí).
Stéphane Chazelas

@ StéphaneChazelas Entonces, ¿cuál es la mejor manera de hacer que Perl coincida con la nueva línea, además de leer todo el archivo?
muru

Vea las otras respuestas que procesan el archivo línea por línea.
Stéphane Chazelas

5

Puede ser un poco fácil con sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • primera parte :1;N;$!b1recolecta todas las líneas en el archivo dividido por \n1 línea larga

  • símbolo de nueva línea de la tira de la segunda parte si seguía un símbolo sin dígitos con posibles espacios entre ellos.

Para evitar la limitación de memoria (especialmente para archivos grandes) puede usar:

sed -e '1{h;d}' -e '1!{/^[0-9]/!{H;d};/^[0-9]/x;$G}' -e 's/\n\+\( *[^0-9]\)/\1/g'

O olvidar un sedguión difícil y recordar que el año comienza desde2

tr '\n2' ' \n' | sed -e '1!s/^/2/' -e 1{/^$/d} -e $a

Niza, +1. ¿Podría agregar una explicación de cómo funciona, por favor?
terdon

1
Aw Agradable. Siempre lo hago tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'yo mismo.
mirabilos

Lo sentimos, pero tengo que votar negativamente por usar cosas que no son POSIX BASIC REGULAR EXPRESSION S en sed (1) , que es un GNUism.
mirabilos

1
@ Costas, esa es la página de manual de GNU grep. Las especificaciones POSIX BRE están ahí . BRE equivalente de ERE +es \{1,\}. [\n]tampoco es portátil. \n\{1,\}Sería POSIX.
Stéphane Chazelas

1
Además, no puede tener otro comando después de una etiqueta. : 1;xes definir la 1;xetiqueta en seds POSIX. Por lo que necesita: sed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'. También tenga en cuenta que muchas sedimplementaciones tienen un pequeño límite en el tamaño de su espacio de patrones (POSIX solo garantiza 10 x LINE_MAX IIRC).
Stéphane Chazelas

5

Una forma sería:

 $ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file
 20141101 server contain dump
 20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
 20141101 server contain dump

Sin embargo, eso también elimina la nueva línea final. Para agregarlo nuevamente, use:

$ { perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file; echo; } > new

Explicación

El -leliminará las nuevas líneas finales (y también agregará una a cada printllamada, por eso lo uso printfen su lugar. Luego, si la línea actual comienza con números ( /^\d+/) y el número de línea actual es mayor que uno ( $.>1, esto es necesario para evitar agregar un extra línea vacía al principio), agregue una \nal principio de la línea. printfImprime cada línea.


Alternativamente, puede cambiar todos los \ncaracteres a \0, luego cambiar aquellos \0que están justo antes de una cadena de números a \nnuevamente:

$ tr '\n' '\0' < file | perl -pe 's/\0\d+ |$/\n$&/g' | tr -d '\0'
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
20141101 server contain dump

Para que coincida solo con cadenas de 8 números, use esto en su lugar:

$ tr '\n' '\0' < file | perl -pe 's/\0\d{8} |$/\n$&/g' | tr -d '\0'

El primer argumento printfes el formato . Usoprintf "%s", $_
Stéphane Chazelas

@ StéphaneChazelas ¿por qué? Quiero decir, sé que es más limpio y tal vez más fácil de entender, pero ¿hay algún peligro del que protegerlo?
terdon

Sí, es incorrecto y potencialmente peligroso si la entrada puede contener% caracteres. Pruebe con una entrada con, %10000000000spor ejemplo.
Stéphane Chazelas

En C, esa es una fuente muy conocida de muy mala práctica y vulnerabilidad. Con perl, echo %.10000000000f | perl -ne printfpone mi máquina de rodillas.
Stéphane Chazelas

@ StéphaneChazelas wow, sí. Mío también. Bastante justo entonces, respuesta editada y gracias.
terdon

3

Intenta hacer esto usando :

#!/usr/bin/awk -f

{
    # if the current line begins with 8 digits followed by
    # 'nothing' OR the current line doesn't start with 8 digits
    if (/^[0-9]{8}.*nothing/ || !/^[0-9]{8}/) {
        # print current line without newline
        printf "%s", $0
        # feeding a 'state' variable
        weird=1
    }
    else {
        # if last line was treated in the 'if' statement
        if (weird==1) {
            printf "\n%s", $0
            weird=0
        }
        else {
            print # print the current line
        }
    }
}
END{
    print # add a newline when there's no more line to treat
}

Para usarlo:

chmod +x script.awk
./script.awk file.txt

2

Otra forma más simple (que mi otra respuesta) usando el algoritmo de y terdon :

awk 'NR>1 && /^[0-9]{8}/{printf "%s","\n"$0;next}{printf "%s",$0}END{print}' file

ITYM END{print ""}. Alternativa:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Stéphane Chazelas


0

El programa en bash:

while read LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo -ne "\n${LINE} "
    else
        echo -n "${LINE} "
    fi
done < file.txt

en forma de una línea:

while read L; do if [[ $L =~ ^[0-9]{8} ]]; then echo -ne "\n${L} "; else echo -n "${L} "; fi done < file.txt

Solución con barras invertidas conservando ( read -r) y espacios iniciales (justo IFS=después while):

while IFS= read -r LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo
        echo -nE "\n${LINE} "
    else
        echo -nE "${LINE} "
    fi
done < file.txt

formulario de una línea:

while IFS= read -r L; do if [[ $L =~ ^[0-9]{8} ]]; then echo; echo -nE "${L} "; else echo -nE "${L} "; fi done < file.text

Esto se romperá si la línea contiene, por ejemplo, una barra invertida y un n. También elimina espacios en blanco. Pero puede mkshhacer esto:while IFS= read -r L; do [[ $L = [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]* ]] && print; print -nr -- "$L"; done; print
mirabilos

Por supuesto, no es un algoritmo para todo, sino una solución para los requisitos proporcionados por la tarea. Por supuesto, la solución final será más compleja y menos legible de un vistazo, como suele suceder en la vida real :)
Torre el

Estoy de acuerdo, pero he aprendido la manera difícil de no asumir demasiado sobre el OP ☺ especialmente si reemplazan el texto real por texto ficticio.
mirabilos

0
[shyam@localhost ~]$ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' appendDateText.txt

que funcionará

i/p:
##06/12/2016 20:30 Test Test Test
##TestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test test
##i123312331233123312331233123312331233123312331233Test
## 06/12/2016 20:30 abc

o/p:
##06/12/2016 20:30 Test Test TestTestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test ##testi123312331233123312331233123312331233123312331233Test
06/12/2016 20:30 abc vi appendDateText.txt 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.