sed: ¿eliminar la última aparición de una cadena (una coma) en un archivo?

sed: ¿eliminar la última aparición de una cadena (una coma) en un archivo?

Tengo un archivo csv muy grande. ¿Cómo eliminarías el último ,con sed (o similar)?

...
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0],
]

Salida deseada

...
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

El siguiente comando sed eliminará la última aparición por línea, pero quiero por archivo.

sed -e 's/,$//' foo.csv

Esto tampoco funciona

sed '$s/,//' foo.csv

Respuesta1

Usandoawk

Si la coma siempre está al final de la penúltima línea:

$ awk 'NR>2{print a;} {a=b; b=$0} END{sub(/,$/, "", a); print a;print b;}'  input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

Usando awkybash

$ awk -v "line=$(($(wc -l <input)-1))" 'NR==line{sub(/,$/, "")} 1'  input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

Usandosed

$ sed 'x;${s/,$//;p;x;};1d'  input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]

Para OSX y otras plataformas BSD, intente:

sed -e x -e '$ {s/,$//;p;x;}' -e 1d  input

Usandobash

while IFS=  read -r line
do
    [ "$a" ] && printf "%s\n" "$a"
    a=$b
    b=$line
done <input
printf "%s\n" "${a%,}"
printf "%s\n" "$b"

Respuesta2

Simplemente puede probar el siguiente comando de una sola línea de Perl.

perl -00pe 's/,(?!.*,)//s' file

Explicación:

  • ,Coincide con una coma.
  • (?!.*,)La búsqueda anticipada negativa afirma que no habría una coma después de esa coma coincidente. Entonces coincidiría con la última coma.
  • sY lo más importante es sel modificador DOTALL, que hace que el punto coincida incluso con los caracteres de nueva línea.

Respuesta3

lcomma() { sed '
    $x;$G;/\(.*\),/!H;//!{$!d
};  $!x;$s//\1/;s/^\n//'
}

Eso debería eliminar solo la última aparición de a ,en cualquier archivo de entrada, y aún imprimirá aquellos en los que a ,no aparece. Básicamente, almacena secuencias de líneas que no contienen una coma.

Cuando encuentra una coma, intercambia el búfer de línea actual con el búfer de retención y de esa manera imprime simultáneamente todas las líneas que ocurrieron desde la última coma.ylibera su buffer de retención.

Estaba revisando mi archivo de historial y encontré esto:

lmatch(){ set "USAGE:\
        lmatch /BRE [-(((s|-sub) BRE)|(r|-ref)) REPL [-(f|-flag) FLAG]*]*
"       "${1%"${1#?}"}" "$@"
        eval "${ZSH_VERSION:+emulate sh}"; eval '
        sed "   1x;     \\$3$2!{1!H;\$!d
                };      \\$3$2{x;1!p;\$!d;x
                };      \\$3$2!x;\\$3$2!b'"
        $(      unset h;i=3 p=:-:shfr e='\033[' m=$(($#+1)) f=OPTERR
                [ -t 2 ] && f=$e\2K$e'1;41;17m}\r${h-'$f$e\0m
                f='\${$m?"\"${h-'$f':\t\${$i$e\n}\$1\""}\\c' e=} _o=
                o(){    IFS=\ ;getopts  $p a "$1"       &&
                        [ -n "${a#[?:]}" ]              &&
                        o=${a#-}${OPTARG-${1#-?}}       ||
                        ! eval "o=$f;o=\${o%%*\{$m\}*}"
        };      a(){    case ${a#[!-]}$o in (?|-*) a=;;esac; o=
                        set $* "${3-$2$}{$((i+=!${#a}))${a:+#-?}}"\
                                ${3+$2 "{$((i+=1))$e"} $2
                        IFS=$;  _o=${_o%"${3+$_o} "*}$*\
        };      while   eval "o \"\${$((i+=(OPTIND=1)))}\""
                do      case            ${o#[!$a]}      in
                        (s*|ub)         a s 2 ''        ;;
                        (r*|ef)         a s 2           ;;
                        (f*|lag)        a               ;;
                        (h*|elp)        h= o; break     ;;
                esac;   done;   set -f; printf  "\t%b\n\t" $o $_o
)\"";}

En realidad es bastante bueno. Sí, usa eval, pero nunca le pasa nada más allá de una referencia numérica a sus argumentos. Crea sedscripts arbitrarios para manejar un último partido. Te mostrare:

printf "%d\" %d' %d\" %d'\n" $(seq 5 5 200) |                               
    tee /dev/fd/2 |                                                         
    lmatch  d^.0     \  #all re's delimit w/ d now                           
        -r '&&&&'    \  #-r or --ref like: '...s//$ref/...'      
        --sub \' sq  \  #-s or --sub like: '...s/$arg1/$arg2/...'
        --flag 4     \  #-f or --flag appended to last -r or -s
        -s\" \\dq    \  #short opts can be '-s $arg1 $arg2' or '-r$arg1'
        -fg             #tacked on so: '...s/"/dq/g...'                     

Eso imprime lo siguiente en stderr. Esta es una copia de lmatchla entrada de:

5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
105" 110' 115" 120'
125" 130' 135" 140'
145" 150' 155" 160'
165" 170' 175" 180'
185" 190' 195" 200'

El subnivel ed de la función evalitera todos sus argumentos una vez. A medida que los recorre, itera un contador de manera adecuada según el contexto de cada cambio y omite esa cantidad de argumentos para la siguiente iteración. A partir de entonces hace una de varias cosas por argumento:

  • Para cada opción, el analizador de opciones $aagrega $o. $ase asigna en función de cuyo valor $ise incrementa en el recuento de argumentos para cada argumento procesado. $ase le asigna uno de los dos valores siguientes:
    • a=$((i+=1))- esto se asigna si una opción corta no tiene su argumento adjunto o si la opción era larga.
    • a=$i#-?- esto se asigna si la opción es corta yhacetenga su argumento adjunto.
    • a=\${$a}${1:+$d\${$(($1))\}}- Independientemente de la asignación inicial, $ael valor de 's siempre está entre llaves y, en algunos -scasos, a veces $ise incrementa en uno más y además se agrega un campo delimitado.

El resultado es que evalnunca se pasa una cadena que contenga incógnitas. Se hace referencia a cada uno de los argumentos de la línea de comandos por su número de argumento numérico, incluso el delimitador que se extrae del primer carácter del primer argumento y es la única vez que debe usar cualquier carácter sin escape. Básicamente, la función es un generador de macros: nunca interpreta los valores de los argumentos de ninguna manera especial porque sedpuede(y lo hará, por supuesto)manejar eso fácilmente cuando analiza el script. En cambio, simplemente organiza sensatamente sus argumentos en un script viable.

Aquí hay algunos resultados de depuración de la función en funcionamiento:

... sed "   1x;\\$2$1!{1!H;\$!d
        };      \\$2$1{x;1!p;\$!d;x
        };      \\$2$1!x;\\$2$1!b
        s$1$1${4}$1
        s$1${6}$1${7}$1${9}
        s$1${10#-?}$1${11}$1${12#-?}
        "
++ sed '        1x;\d^.0d!{1!H;$!d
        };      \d^.0d{x;1!p;$!d;x
        };      \d^.0d!x;\d^.0d!b
        sdd&&&&d
        sd'\''dsqd4
        sd"d\dqdg
        '

Y, por lo tanto, lmatchse puede utilizar para aplicar fácilmente expresiones regulares a los datos posteriores a la última coincidencia en un archivo. El resultado del comando que ejecuté arriba es:

5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
101010105dq 110' 115dq 120'
125dq 130' 135dq 140sq
145dq 150' 155dq 160'
165dq 170' 175dq 180'
185dq 190' 195dq 200'

...que, dado el subconjunto de la entrada del archivo que sigue a la última vez /^.0/que coincide, aplica las siguientes sustituciones:

  • sdd&&&&d- se reemplaza $matchconsigo mismo 4 veces.
  • sd'dsqd4- la cuarta comilla simple que sigue al comienzo de la línea desde el último partido.
  • sd"d\dqd2- lo mismo, pero para comillas dobles y globalmente.

Y así, para demostrar cómo se podría utilizar lmatchpara eliminar la última coma de un archivo:

printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1

PRODUCCIÓN:

5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100

Respuesta4

verhttps://stackoverflow.com/questions/12390134/remove-comma-from-last-line

Esto funcionó para mí:

$cat input.txt
{"name": "secondary_ua","type":"STRING"},
{"name": "request_ip","type":"STRING"},
{"name": "cb","type":"STRING"},
$ sed '$s/,$//' < input.txt >output.txt
$cat output.txt
{"name": "secondary_ua","type":"STRING"},
{"name": "request_ip","type":"STRING"},
{"name": "cb","type":"STRING"}

Mi mejor manera es eliminar la última línea y, después de eliminar la coma, agregar el carácter ] nuevamente.

información relacionada