O que devo usar quando o corte não o corta?

19

Eu tenho um arquivo citiescomo este:

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

Quero cortar os nomes das cidades, para que eu tenha:

San Diego
St Louis
Orlando

Este é o melhor que eu poderia ter:

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

Mas isso ainda me deixa com um espaço antes dos nomes. Existe um cutcomando semelhante que eu possa usar que aceite delimitadores de vários caracteres para que eu possa cortar ]?

Kit Sunde
fonte
1
tré útil para excluir caracteres que você não deseja.
LawrenceC
Se você tentar o código nas respostas das pessoas, verá três saídas diferentes. Isso sugere que sua pergunta não estava 100% clara. "Recortar" significa remover ou selecionar? Você quer o (inactive)status ou não? Forneça uma amostra de saída.
23611 Mikel
@ Mikel - Considerando que estou usando cutpara cortar as coisas e você pode ver a intenção do exemplo que falhei, tenho que ficar bem claro no contexto. Vou fornecer amostra embora para esclarecer ainda mais. :)
Kit Sunde 23/04
Não, não mesmo. Alterei uma frase da sua pergunta para "imprimir apenas os nomes das cidades", porque não era claro para você o uso da palavra "recortar". Minha alteração está correta?
23611 Mikel
1
@Kit Sunde: Com a saída de amostra, é certamente compreensível. O título é fofo. "recortar" me faz pensar no que acontece quando você pressiona Ctrl + X, e foi por isso que sugeri a alteração, mas é sua pergunta. A redução de votos seria tola quando é apenas um simples desacordo.
23611 Mikel

Respostas:

15

Awk (também verifique Awk Info ) é bonito com esse tipo de pergunta. Experimentar:

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

Isso define um separador de campo -Fcomo [],] *- o que significa uma ocorrência de um colchete de fechamento ou de uma vírgula, seguida por zero ou qualquer número de espaços. Claro que você pode mudar isso para atender a qualquer requisito. Leia sobre expressões regulares.

Depois que a linha é dividida, você pode fazer o que quiser com o resultado da divisão. Aqui, decidi imprimir o segundo campo apenas com print $2. Observe que é importante usar aspas simples nas instruções do awk, caso contrário, $ 2 serão substituídos pelo shell.

asoundmove
fonte
2
]não é um colchete angular. Os colchetes angulares são <>. []são "colchetes" ou apenas "colchetes".
Cjm
Eu acho que você precisa escapar desse colchete, a menos que eu realmente precise ler minhas expressões regulares.
Kit Sunde
@cjm - Talvez ele seja alemão: news.ycombinator.com/item?id=1181243 :)
Kit Sunde
1
@ cjm, desculpe, eu quis dizer colchete, digitou um pouco rápido demais. @Kit, eu não sou alemão. Você não deseja escapar do suporte de fechamento interno (não serviria para nada), mas deve ser o primeiro caractere no intervalo.
asoundmove
12

Você pode modificar o último cutno seu pipeline para isso:

cut -d ' ' -f2-

O texto acima significa que o separador de campos é um espaço em branco e queremos selecionar todos os campos a partir do segundo. A sequência completa se torna:

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

Para uma análise mais complexa, você deve usar o sed (1) :

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

Ou use -rpara simplificar a expressão regular, conforme sugerido por pepoluan :

sed -re 's/\[[0-9]+\] ([^,]+),.*/\1/' cities
Juliano
fonte
2
+1. você também pode usar -r para evitar escapar caracteres regex avançados, simplificando enormemente o padrão regex
pepoluan
0

Eu normalmente uso Perl quando as coisas ficam muito difíceis para sed e grep.

Existem várias maneiras de escrever no Perl. Por exemplo, você pode preferir que seja rápido, ou pode lidar com pequenos problemas inesperados na entrada (por exemplo, dois espaços onde um era esperado).

Uma maneira óbvia (assume que id é numérico, cidade é alfabética, status é alfabético):

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

Ou mais lento, mas mais permissivo (faz mais retrocessos):

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

Ou mais rápido (o campo para na primeira ocorrência do colchete de fechamento):

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

Na linha de comando, em vez de um script, você pode usar a -nopção, que basicamente adiciona o while (<>) { BLOCK }loop:

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

ou se quiser que o uso se pareça com o corte, você pode usar a -Fopção, que é semelhante à -Fopção do awk , por exemplo:

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

Dessa maneira, obviamente, assume que nenhum campo conterá nenhum dos delimitadores.

Mikel
fonte