¿Qué debo usar cuando el corte no lo corta?

19

Tengo un archivo citiescomo este:

[1598] San Diego, US (inactive)
[4517] St Louis, US (inactive)
[6346] Orlando, US (inactive)

Quiero recortar los nombres de las ciudades, para tener:

San Diego
St Louis
Orlando

Esto es lo mejor que se me ocurrió:

cut -d ',' -f1 cities | cut -d ']' -f2

Pero eso todavía me deja un espacio antes de los nombres. ¿Hay un cutcomando similar que pueda usar que acepte delimitadores de varios caracteres para poder cortar ]?

Kit Sunde
fuente
1
tres útil para eliminar caracteres que no quieres.
LawrenceC
Si prueba el código en las respuestas de las personas, verá tres salidas diferentes. Esto sugiere que su pregunta no estaba 100% clara. ¿"Cortar" significa eliminar o seleccionar? ¿Quieres el (inactive)estado o no? Proporcione una salida de muestra.
Mikel
@Mikel - Teniendo en cuenta que estoy usando cutpara cortar cosas y puedes ver la intención del ejemplo fallido que tengo, debería ser bastante claro en el contexto. Proporcionaré una muestra para aclararla aún más. :)
Kit Sunde
No en realidad no. Cambié una oración en su pregunta para "imprimir solo los nombres de las ciudades", porque era su uso de la palabra "cortar" lo que no estaba claro para mí. ¿Es correcto mi cambio?
Mikel
1
@ Kit Sunde: con el resultado de la muestra, ciertamente es comprensible. El titulo es lindo. "cortar" me hace pensar en lo que sucede cuando presionas Ctrl + X, por eso sugerí el cambio, pero es tu pregunta. El voto negativo sería una tontería cuando es solo un simple desacuerdo.
Mikel

Respuestas:

15

Awk (también verifique Awk Info ) es hermoso con ese tipo de preguntas. Tratar:

awk -F'[],] *' '{print $2}' cities

Esto define un separador de campo -Fcomo [],] *, lo que significa una aparición de un corchete de cierre o una coma, seguido de cero o cualquier número de espacios. Por supuesto, puede cambiar eso para adaptarse a cualquier requisito. Lea sobre expresiones regulares.

Una vez que la línea se divide, puede hacer lo que quiera con el resultado dividido. Aquí, decidí imprimir el segundo campo solo con print $2. Tenga en cuenta que es importante utilizar comillas simples alrededor de las instrucciones awk; de lo contrario, $ 2 se sustituirá por el shell.

asoundmove
fuente
2
]No es un soporte angular. Los corchetes angulares son <>. []son "corchetes" o simplemente "corchetes".
cjm
Creo que debes escapar de ese corchete de cierre, a menos que realmente necesite leer mis expresiones regulares.
Kit Sunde
@cjm - Quizás sea alemán: news.ycombinator.com/item?id=1181243 :)
Kit Sunde
1
@ cjm, lo siento, quise decir corchetes, escribí un poco demasiado rápido. @ Kit, no soy alemán. No desea escapar del corchete de cierre interno (no serviría de nada), pero debe ser el primer personaje del rango.
asoundmove
12

Puede modificar el último cuten su tubería a esto:

cut -d ' ' -f2-

Lo anterior significa que el separador de campo es un espacio en blanco, y queremos seleccionar todos los campos a partir del segundo. La secuencia completa se convierte en:

cut -d ',' -f1 cities | cut -d ' ' -f2-
Barun
fuente
12

Para un análisis más complejo, debe usar sed (1) :

sed -e 's/\[[0-9]\+\] \([^,]\+\),.*/\1/' cities

O usando -rpara simplificar la expresión regular, como lo sugiere pepoluan :

sed -re 's/\[[0-9]+\] ([^,]+),.*/\1/' cities
Juliano
fuente
2
+1. también se puede utilizar -r para evitar que se escape caracteres de expresiones regulares avanzadas, lo que simplifica en gran medida el patrón de expresión
pepoluan
0

Normalmente uso Perl cuando las cosas se ponen demasiado difíciles para sed y grep.

Hay varias formas de escribirlo en Perl. Por ejemplo, es posible que prefiera que sea rápido o que prefiera manejar problemas leves e inesperados en la entrada (por ejemplo, dos espacios donde se esperaba uno).

Una forma obvia (supone que la identificación es numérica, la ciudad es alfabética, el estado es alfabético):

while (<>) {
    if (/^\[\d+\] (\w+(?: \w+)*), \w+ \(\w*\)$/) {
        my $city = $1;
        print "$city\n";
    }
}

O más lento pero más permisivo (hace más retroceso):

while (<>) {
    if (/^.*\]\s+(.*),.*$/) {
        my $city = $1;
        print "$city\n";
    }
}

O más rápido (el campo se detiene en la primera aparición del corchete de cierre):

while (<>) {
    if (/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/) {
        my $city = $1;
        print "$city\n";
    }
}

Desde la línea de comandos en lugar de un script, puede usar la -nopción, que básicamente agrega el while (<>) { BLOCK }bucle:

perl -ne '/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/ and print $1, "\n";' cities

o si desea que el uso se parezca al corte, puede usar la -Fopción, que es similar a la -Fopción de awk , por ejemplo:

perl -a -n -F'/[],]\s+/' -e 'print $F[1], "\n"' cities

De esta manera, obviamente, se supone que ningún campo contendrá ninguno de los delimitadores.

Mikel
fuente