Obtendo dois campos da mesma linha juntos

4

Eu tenho uma situação em que tenho várias linhas e preciso de dois campos diferentes para cada uma delas. Para ser específico, eu tenho uma lista de referências em uma bibliografia e quero pegar o sobrenome e o ano.

Exemplo de entrada:

Aloise-Young, P.A. (1993). The development of self-presentation.  Self-promotion in 6- to 10-year-old children. Social Cognition, 11, 201-222.
Banerjee, R. (2002). Children's understanding of self-presentational behavior: Links with mental-state reasoning and the attribution of embarrassment. Merril-Palmer Quarterly, 48, 378-404.
Bennett, M., & Wellman, H. (1989). The role of second-order belief-understanding and social context in children's self-attribution of social emotions. Social Development, 9, 126-130.

Saída desejada:

Aloise-Young 1993
Banerjee 2002
Bennett 1989

Eu posso pegar os sobrenomes com cat file | cut -d, -f1

Eu posso obter os anos com cat file | grep -o "[[:digit:]]\{4\}"

Meu problema é que agora tenho duas saídas separadas e não sei como combiná-las da maneira que desejo. Alguma ideia? Eu suspeito que talvez awk possa fazer o que eu preciso.

    
por Nathan Wallace 02.05.2013 / 03:03

5 respostas

3

Eu coloquei o seu trecho em um arquivo chamado alois:

sed -r 's/^([^ ,]+)[^0-9]+([0-9]+).*$/ /' alois 
Aloise-Young 1993
Banerjee 2002
Bennett 1989

Explicação rápida: usamos a pesquisa e & substitua a função s / pattern / replacement /

^ ([^,] +) significa: do começo da linha, pegue qualquer coisa que não seja um espaço ou um, e lembre-se disso. (é por isso que os parênteses estão aí).

[^ 0-9] + procure por algo que não seja um valor numérico, mas ignore-o.

([0-9] +) pega e lembra de quaisquer dígitos consecutivos

. * $ combina todo o resto com o fim da linha.

\ 1 \ 2 substitui tudo correspondido (a linha inteira) pelos valores lembrados de cima.

    
por 02.05.2013 / 03:18
6

Quando o processamento de texto fica muito difícil para as ferramentas básicas, tente Awk .

awk -F , '{last_name = $1; sub(/\).*/, ""); sub(/.*\(/, ""); print last_name, $0}'

Aqui sed é quase a mesma coisa - é menos legível, mas o awk sofre com a falta de backreferences.

sed -n 's/^\([^,]*\),[^(]*(\([^()]*\)).*/ /p'

Para esta tarefa em particular, o Perl é um pouco mais fácil no geral. Você pode usar o operador de repetição não voraz *? para garantir que captura a primeira parte entre parênteses da linha.

perl -l -ne '/^([^,]*),.*?\(([^()]*)\)/ and print "$1 $2"'
    
por 02.05.2013 / 03:17
3

Em geral, você pode unir a saída de comandos com paste e a substituição de processo <(...) , portanto, no seu caso, isso funciona:

 paste -d ' ' <(cut -d, -f1 file) <(grep -o "[[:digit:]]\{4\}" file)

Saída:

Aloise-Young 1993
Banerjee 2002
Bennett 1989

Mas isso implica passar file duas vezes, o que é desnecessário, então você provavelmente deve usar uma ferramenta que pode pegar os dois itens de uma vez, por exemplo. sed , awk , etc.

    
por 02.05.2013 / 08:17
2

Você pode descobrir que precisa refinar seus requisitos. Por exemplo, nenhuma das soluções dadas até agora funciona como:

Smith, J., & 3Com(Inc.) research (1999), XYZ statistics (1960 - 1998)

Para relatar tudo até a primeira vírgula junto com a primeira ocorrência de uma sequência de quatro dígitos entre parênteses, você poderia fazer:

perl -ne 'print "$1 $2\n" if /^(.*?),.*?\((\d{4})\)/'
    
por 02.05.2013 / 13:51
0

Não é sexy, mas você pode aparar o texto da primeira vírgula para o primeiro paren aberto. Substitua tudo isso por um espaço. Em seguida, apare apenas o caractere do primeiro parêntese de fechamento até o final da linha.

1. convert this => , ..... ( to a space
2. convert this => )........ to nothing

O comando

$ cat file | sed 's/,.*(/ /' | sed 's/).*//'
Aloise-Young 1993
Banerjee 2002
Bennett 1989
    
por 02.05.2013 / 03:33