texto sustituir script reemplazar para linea insertar especiales espacios eliminar comando caracteres blanco sed awk tcsh openbsd

sustituir - sed: reemplazar espacios entre comillas con guiones bajos



sed linux (5)

Para una solución sed only (que no necesariamente defiendo), intente:

echo ''a b "c d e" f g "h i"'' |/ sed '':a;s/^/(/([^"]*"[^"]*"[^"]*/)*[^"]*"[^"]*/) //1_/;ta'' a b "c_d_e" f g "h_i"

Traducción:

  • Comience al comienzo de la línea.
  • Busque el patrón junk"junk" , repetido cero o más veces, donde la junk no tiene una cita, seguido de junk"junk space .
  • Reemplace el espacio final con _ .
  • Si tiene éxito, regrese al principio.

Tengo entrada (por ejemplo, desde ifconfig run0 scan en OpenBSD) que tiene algunos campos separados por espacios, pero algunos de los campos contienen espacios (afortunadamente, los campos que contienen espacios están siempre entre comillas).

Necesito distinguir entre los espacios dentro de las comillas y los espacios separadores. La idea es reemplazar espacios entre comillas con guiones bajos.

Data de muestra:

%cat /tmp/ifconfig_scan | fgrep nwid | cut -f3 nwid Websense chan 6 bssid 00:22:7f:xx:xx:xx 59dB 54M short_preamble,short_slottime nwid ZyXEL chan 8 bssid cc:5d:4e:xx:xx:xx 5dB 54M privacy,short_slottime nwid "myTouch 4G Hotspot" chan 11 bssid d8:b3:77:xx:xx:xx 49dB 54M privacy,short_slottime

Lo cual no termina procesado de la manera que quiero, ya que aún no he reemplazado los espacios entre las comillas con los guiones bajos:

%cat /tmp/ifconfig_scan | fgrep nwid | cut -f3 |/ cut -s -d '' '' -f 2,4,6,7,8 | sort -n -k4 "myTouch Hotspot" 11 bssid d8:b3:77:xx:xx:xx ZyXEL 8 cc:5d:4e:xx:xx:xx 5dB 54M Websense 6 00:22:7f:xx:xx:xx 59dB 54M


NO ES UNA RESPUESTA, simplemente publicando un código equivalente a awk para el código perl de @ steve en caso de que alguien esté interesado (y para ayudarme a recordar esto en el futuro):

@steve publicado:

perl -pe ''s:"[^/"]*":($x=$&)=~s/ /_/g;$x:ge''

y al leer la explicación de @ steve, el awk más breve equivalente a ese código perl (NO la solución awk preferida - vea la respuesta de @ Kent para eso) sería el awk de GNU:

gawk ''{ head = "" while ( match($0,"/"[^/"]*/"") ) { head = head substr($0,1,RSTART-1) gensub(/ /,"_","g",substr($0,RSTART,RLENGTH)) $0 = substr($0,RSTART+RLENGTH) } print head $0 }''

lo cual llegamos a partir de una solución POSIX awk con más variables:

awk ''{ head = "" tail = $0 while ( match(tail,"/"[^/"]*/"") ) { x = substr(tail,RSTART,RLENGTH) gsub(/ /,"_",x) head = head substr(tail,1,RSTART-1) x tail = substr(tail,RSTART+RLENGTH) } print head tail }''

y guardando una línea con GNU awk''s gensub ():

gawk ''{ head = "" tail = $0 while ( match(tail,"/"[^/"]*/"") ) { x = gensub(/ /,"_","g",substr(tail,RSTART,RLENGTH)) head = head substr(tail,1,RSTART-1) x tail = substr(tail,RSTART+RLENGTH) } print head tail }''

y luego deshacerse de la variable x:

gawk ''{ head = "" tail = $0 while ( match(tail,"/"[^/"]*/"") ) { head = head substr(tail,1,RSTART-1) gensub(/ /,"_","g",substr(tail,RSTART,RLENGTH)) tail = substr(tail,RSTART+RLENGTH) } print head tail }''

y luego deshacerte de la variable "cola" si no necesitas $ 0, NF, etc., que quedaron después del ciclo:

gawk ''{ head = "" while ( match($0,"/"[^/"]*/"") ) { head = head substr($0,1,RSTART-1) gensub(/ /,"_","g",substr($0,RSTART,RLENGTH)) $0 = substr($0,RSTART+RLENGTH) } print head $0 }''


prueba esto:

awk -F''"'' ''{for(i=2;i<=NF;i++)if(i%2==0)gsub(" ","_",$i);}1'' OFS="/"" file

Funciona para partes de varias citas en una línea:

echo ''"first part" foo "2nd part" bar "the 3rd part comes" baz''| awk -F''"'' ''{for(i=2;i<=NF;i++)if(i%2==0)gsub(" ","_",$i);}1'' OFS="/"" "first_part" foo "2nd_part" bar "the_3rd_part_comes" baz

EDITAR formulario alternativo:

awk ''BEGIN{FS=OFS="/""} {for(i=2;i<NF;i+=2)gsub(" ","_",$i)} 1'' file


Estarías mejor con perl . El código es mucho más legible y mantenible:

perl -pe ''s:"[^"]*":($x=$&)=~s/ /_/g;$x:ge''

Con su aporte, los resultados son:

a b "c_d_e" f g "h_i"

Explicación:

-p # enable printing -e # the following expression... s # begin a substitution : # the first substitution delimiter "[^"]*" # match a double quote followed by anything not a double quote any # number of times followed by a double quote : # the second substitution delimiter ($x=$&)=~s/ /_/g; # copy the pattern match ($&) into a variable ($x), then # substitute a space for an underscore globally on $x. The # variable $x is needed because capture groups and # patterns are read only variables. $x # return $x as the replacement. : # the last delimiter g # perform the nested substitution globally e # make sure that the replacement is handled as an expression

Algunas pruebas:

for i in {1..500000}; do echo ''a b "c d e" f g "h i" j k l "m n o "p q r" s t" u v "w x" y z'' >> test; done time perl -pe ''s:"[^"]*":($x=$&)=~s/ /_/g;$x:ge'' test >/dev/null real 0m8.301s user 0m8.273s sys 0m0.020s time awk ''BEGIN{FS=OFS="/""} {for(i=2;i<NF;i+=2)gsub(" ","_",$i)} 1'' test >/dev/null real 0m4.967s user 0m4.924s sys 0m0.036s time awk ''!(NR%2){gsub(FS,"_")}1'' RS=/" ORS=/" test >/dev/null real 0m4.336s user 0m4.244s sys 0m0.056s time sed '':a;s/^/(/([^"]*"[^"]*"[^"]*/)*[^"]*"[^"]*/) //1_/;ta'' test >/dev/null real 2m26.101s user 2m25.925s sys 0m0.100s


Otro awk para intentar:

awk ''!(NR%2){gsub(FS,"_")}1'' RS=/" ORS=/"

Eliminando las comillas

awk ''!(NR%2){gsub(FS,"_")}1'' RS=/" ORS=

Algunas pruebas adicionales con un archivo de prueba de tamaño triple además de las pruebas anteriores realizadas por @steve. Tuve que transformar la declaración de sed un poco para que los sed no-GNU pudieran procesarla también. bwk awk ( bwk ) gawk3 , gawk4 y mawk :

$ for i in {1..1500000}; do echo ''a b "c d e" f g "h i" j k l "m n o "p q r" s t" u v "w x" y z'' ; done > test $ time perl -pe ''s:"[^"]*":($x=$&)=~s/ /_/g;$x:ge'' test >/dev/null real 0m27.802s user 0m27.588s sys 0m0.177s $ time awk ''BEGIN{FS=OFS="/""} {for(i=2;i<NF;i+=2)gsub(" ","_",$i)} 1'' test >/dev/null real 0m6.565s user 0m6.500s sys 0m0.059s $ time gawk3 ''BEGIN{FS=OFS="/""} {for(i=2;i<NF;i+=2)gsub(" ","_",$i)} 1'' test >/dev/null real 0m21.486s user 0m18.326s sys 0m2.658s $ time gawk4 ''BEGIN{FS=OFS="/""} {for(i=2;i<NF;i+=2)gsub(" ","_",$i)} 1'' test >/dev/null real 0m14.270s user 0m14.173s sys 0m0.083s $ time mawk ''BEGIN{FS=OFS="/""} {for(i=2;i<NF;i+=2)gsub(" ","_",$i)} 1'' test >/dev/null real 0m4.251s user 0m4.193s sys 0m0.053s $ time awk ''!(NR%2){gsub(FS,"_")}1'' RS=/" ORS=/" test >/dev/null real 0m13.229s user 0m13.141s sys 0m0.075s $ time gawk3 ''!(NR%2){gsub(FS,"_")}1'' RS=/" ORS=/" test >/dev/null real 0m33.965s user 0m26.822s sys 0m7.108s $ time gawk4 ''!(NR%2){gsub(FS,"_")}1'' RS=/" ORS=/" test >/dev/null real 0m15.437s user 0m15.328s sys 0m0.087s $ time mawk ''!(NR%2){gsub(FS,"_")}1'' RS=/" ORS=/" test >/dev/null real 0m4.002s user 0m3.948s sys 0m0.051s $ time sed -e :a -e ''s/^/(/([^"]*"[^"]*"[^"]*/)*[^"]*"[^"]*/) //1_/;ta'' test > /dev/null real 5m14.008s user 5m13.082s sys 0m0.580s $ time gsed -e :a -e ''s/^/(/([^"]*"[^"]*"[^"]*/)*[^"]*"[^"]*/) //1_/;ta'' test > /dev/null real 4m11.026s user 4m10.318s sys 0m0.463s

mawk rindió los resultados más rápidos ...