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 ,
no ocurra. 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 y libera su búfer de retención.
Estaba buscando en 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í, lo usa eval
, pero nunca le pasa nada más allá de una referencia numérica a sus argumentos. Crea sed
scripts arbitrarios para manejar una última coincidencia. 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 para stderr. Esta es una copia de lmatch
la 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 eval
subshell de la función ed recorre todos sus argumentos una vez. A medida que avanza sobre ellos, itera un contador de manera apropiada según el contexto de cada cambio y omite esa cantidad de argumentos para la próxima iteración. A partir de entonces, hace una de las pocas cosas por argumento:
- Para cada opción la opción analizador agrega
$a
a $o
. $a
se asigna en función del valor del $i
cual se incrementa por conteo de arg para cada arg procesado. $a
se 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 y no tener su arg anexa a la misma.
a=\${$a}${1:+$d\${$(($1))\}}
- Independientemente de la asignación inicial, $a
el valor de siempre se incluye entre llaves y, en un -s
caso, a veces $i
se incrementa uno más y se agrega un campo delimitado adicionalmente.
El resultado es que eval
nunca 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 que no esté escapado. Básicamente, la función es un generador de macros: nunca interpreta los valores de los argumentos de ninguna manera especial porque sed
puede (y lo hará, por supuesto) manejarlo fácilmente cuando analiza el script. En cambio, simplemente organiza sus argumentos en un guión viable.
Aquí hay algunos resultados de depuración de la función en el trabajo:
... 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 así lmatch
se puede usar para aplicar fácilmente expresiones regulares a los datos después de 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
- reemplaza $match
con 4 veces sí.
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 puede usar lmatch
para eliminar la última coma en un archivo:
printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1
SALIDA:
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100