#! /bin/bash
while read line; do
if [[ ${line:11:15} =~ John ]]; then
echo " ${line:11:15} ${line:43}"
fi
done <file
Eu tenho um arquivo assim:
Marketing Ranjit Singh FULLEagles Dean Johnson
Marketing Ken Whillans FULLEagles Karen Thompson
Sales Peter RobertsonPARTGolden TigersRich Gardener
President Sandeep Jain CONTWimps Ken Whillans
Operations John Thompson PARTHawks Cher
Operations Cher CONTVegans Karen Patel
Sales John Jacobs FULLHawks Davinder Singh
Finance Dean Johnson FULLVegans Sandeep Jain
EngineeringKaren Thompson PARTVegans John Thompson
IT Rich Gardener FULLGolden TigersPeter Robertson
IT Karen Patel FULLWimps Ranjit Singh
Eu quero usar um comando grep para procurar por "John" na segunda coluna, procurando na última coluna, mas para cada "John" na segunda coluna, eu quero a saída da última coluna.
O resultado final deve ser assim:
John Thompson Cher
John Jacobs Davinder Singh
Dean Johnson Sandeep Jain
#! /bin/bash
while read line; do
if [[ ${line:11:15} =~ John ]]; then
echo " ${line:11:15} ${line:43}"
fi
done <file
Você pode realizar a seleção em grep
combinando o número correto de caracteres do começo da linha:
grep -E '^.{11,22}John'
John
tem que começar e terminar dentro do intervalo das colunas 11–26.
Substituir algumas colunas por espaço em branco não está dentro das capacidades do grep. Com o GNU grep, você pode usar -o
para produzir apenas a parte correspondente, mas você não poderá combinar as duas colunas com espaço em branco adicional entre elas.
tab=$(printf '\t')
cut -c12-26 file | paste - file | grep "^[^$tab]*John" | cut -c1-16,60-
Como Gilles disse que você não pode fazer isso com grep
sozinho. grep
significa g/re/p
(um comando ed/ex/vi
), isto é, imprime as linhas que correspondem ao re.
Vamos combinar algumas dicas das soluções que acabamos de mostrar.
De Stephane + Gilles:
grep -E '^.{11,22}John' source.txt | cut -c12-26,44-
# or, if you want only "John " and not Johnson, add a space after John.
grep -E '^.{11,22}John ' source.txt | cut -c12-26,44-
Aqui, você precisa usar /bin/grep
e também /bin/cut
.
De Gilles + Hauke:
grep -E '^.{11,22}John' source.txt | while read line; do echo "${line:11:14} ${line:43}"; done
Aqui, você precisa usar /bin/grep
e também echo
.
No shell moderno, você encontra echo
como um comando embutido , de modo que requer menos.
A solução da Hauke é a menos dispendiosa em termos de programas instalados: requer apenas echo
(embutido em bash
), mas nem mesmo /bin/grep
, como desejado na questão.
Atualizar Vamos jogar um pouco.
No dia a dia nós rodamos snippet de código, criado na hora e usado talvez nunca mais;
nem sempre é conveniente gastar nosso tempo humano para otimizá-lo ou experimentar variações diferentes ...
mas semel in anno licet insanire :
Por outro lado, e com uma alta probabilidade, este foi um case-study question
,
talvez até mesmo um trabalho de casa não esteja completamente claro. Nesta óptica e IMHO, todas as diferentes abordagens e soluções são úteis.
Todos concordamos que, para um grande número de linhas, é melhor (mais rápido) usar um programa compilado então uma seqüência de comandos do shell. Mas o que é grande? Eu acho que números (e enredos) podem consertar idéias melhor do que apenas palavras: então vamos ver.
Aqui a receita : um conjunto de arquivos com um número crescente de linhas, N ,
estava preparado. Cada linha foi extraída aleatoriamente do original original publicado aqui.
Os valores de N usados são 11 22 55 110 220 550 1100 2200 5500 11000 110000 1100000 11000000
.
O snippet testado é:
Cut+Paste+Grep+Cut
tab=$(printf '\t') ; cut -c12-26 file | paste - file | grep "^[^$tab]*John" | cut -c1-16,60-
- Verde Only Grep
- Blue grep -E '^.{11,22}John' source.txt
. Simplesmente o grep
sem saída formatada conforme solicitado. Grep+Cut
- vermelho [ grep -E '^.{11,22}John' source.txt | cut -c12-26,44-
]. Grep+While
- Violeta [veja acima nesta resposta, o grep antes e o loop while]. While loop
- Yellow uma solução bash completa que não exige grep
Para cada tamanho dos arquivos e snippet , um número de repetições, NRep , foi feito
a partir de 400 (para os arquivos mais curtos) e diminuindo com N para 100,
10 e 1 para o último.
Foi registrado o valor de Tempo por linha , Tpl , também conhecido como tempo real , medido pelo em função
time
e média em NRep e N . Como os Tpl e N abrangem potências de 10, foi plotado
o logaritmo comum (para base 10, ou os poderes de 10). As linhas relatadas são curvas de Bézier que tocam cada ponto.
ParagrandesvaloresdeN,oTempoporLinhasetornaquaseconstante.Éochamadocomportamentoassintótico.Comoesperado,quantomenoronúmerocompiladodoprogramausado,maisrápidoseráoresultado.
Pelo contrário, com arquivos pequenos, o resultado é o oposto. As eficiências de diferentes códigos se cruzam em uma região que (para o nosso exemplo) está entre 40 e 140 linhas. Mesmo que seja verdade que, para arquivos pequenos, é pequeno mesmo o tempo humano usado, a mesma consideração não é mais válida quando é necessário trabalhar com um grande número de pequenos arquivos: o código bash puro (amarelo) que assintoticamente é 8,12 vezes mais lento que o verde, e até 157 vezes mais lento que o vermelho (para o arquivo de linhas 11M ele usa 334,56 s. em vez de 41,21s do verde e 2,16 do vermelho) Em vez disso, é respectivamente 2,16 e 1,58 vezes mais rápida para as 11 linhas, quando usa 1,20s para 400 repetições em vez de 1,89s do vermelho ou 2,59s do verde.
Conclusão: Quanto mais você souber, melhor você desafiar e verificar sempre que puder! : -)
Ps > Uma consideração semelhante pode ser feita no tempo do usuário e tempo do sistema , mas com uma região de cruzamento ligeiramente diferente.
bash 4.3.11 (1) -release em pasta (GNU coreutils) 8.21 em corte (GNU coreutils) 8.21
grep (GNU grep) 2.16
kernel 3.13.0-24-generico x86_64