O que devo usar quando cortar não corta?

19

Eu tenho um arquivo cities como este:

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

Eu quero cortar os nomes das cidades, para que eu tenha:

San Diego
St Louis
Orlando

Isso é o melhor que consegui:

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

Mas isso ainda me deixa um espaço antes dos nomes. Existe um comando cut like que eu possa usar para aceitar delimitadores de vários caracteres para que eu possa cortar ] ?

    
por Kit Sunde 22.04.2011 / 15:39

4 respostas

15

Awk (verifique também Awk Info ) é lindo com esse tipo de pergunta. Experimente:

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

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

Quando a linha estiver dividida, você poderá fazer o que quiser com o resultado da divisão. Aqui, decidi imprimir o segundo campo apenas com print $2 . Note que é importante usar aspas simples em torno das instruções do awk, caso contrário, $ 2 será substituído pelo shell.

    
por 22.04.2011 / 20:23
12

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

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

Ou usando -r para simplificar a expressão regular, como sugerido por pepoluan :

sed -re 's/\[[0-9]+\] ([^,]+),.*//' cities
    
por 22.04.2011 / 15:46
11

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

cut -d ' ' -f2-

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

cut -d ',' -f1 cities | cut -d ' ' -f2-
    
por 22.04.2011 / 15:47
0

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

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

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

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

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

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

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

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

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

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

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

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

Desta forma, obviamente, assume que nenhum campo conterá qualquer um dos delimitadores.

    
por 23.04.2011 / 05:10