Grep 2 requisitos diferentes em uma instrução de linha de comando

1

Como eu imprimiria as 3 últimas linhas que têm a string #include de tester.c e se menos de 3 linhas contiverem a string, imprima o arquivo inteiro .

Até agora eu tenho:

grep "#include" tester.c | tail -3

Mas não consigo entender como incluir a segunda metade do requisito. Esta é a lição de casa e a solução deve ser uma única linha sem ; , e é por isso que não consigo descobrir isso.

    
por Johnny 14.05.2015 / 17:59

5 respostas

1

tail -3 não imprime mais de 3 linhas; imprime 3 ou menos. Então, se 'grep' encontrar 3 ou menos linhas correspondentes, tail irá passar TODAS para a stdout. Se houver mais de três correspondências, tail as limitará a apenas três linhas.

Você resolveu seu problema antes mesmo de postá-lo aqui, pois seu comando faz exatamente o que você precisa ...

Isso é o que acontece quando você está trabalhando à noite:)

EDIT: Eu provavelmente interpretei mal o seu problema ..

Se você quiser imprimir o ARQUIVO INTEIRO quando os <3 fósforos forem encontrados - basta executar

f="testfile.c" && C=3 && [ $(grep -c "#include" ${f}) -lt ${C} ] && cat ${f} || grep "#include" ${f} |tail -${C}

No entanto, se você estiver trabalhando em um FS possivelmente lento (por exemplo, NFS) ou se estiver lidando com arquivos com MUITAS linhas ou linhas extremamente longas, isso pode ser muito lento para você, já que você precisa ler o arquivo duas vezes ..

f="testfile.c" && C=3 && CONTENT=$(cat "${f}") && MATCHES=$(echo "$CONTENT"|grep "#include"|tail -${C}) && [ $(echo "$MATCHES" | wc -l) -lt ${C} ] && echo "$CONTENT" || echo "$MATCHES"

Desta forma, você terá que abrir e ler o arquivo apenas uma vez, MAS você o colocará em buffer na RAM (que é muito mais rápido que os dispositivos de armazenamento). Se você estiver com pouca memória RAM ou fazendo o buffer do arquivo, isso é muito caro para você, use o primeiro comando [onde o arquivo é aberto duas vezes]; é mais lento, mas não trava o sistema operacional ou o aplicativo (embora o risco de ficar preso no estado 'D' seja maior ... e também não é bom para o sistema operacional.)

    
por 15.05.2015 / 09:41
0

Você pode fazer como Sildoreth diz sem o ";" assim:

test $(echo 'grep "#include" tester.c' | wc -l) -ge 3 && {echo 'grep "#include" tester.c' | head -n 3} || cat tester.c
    
por 14.05.2015 / 18:43
0

Esta é uma pergunta estranha, especialmente considerando que o comando que você coloca na postagem original faz exatamente o que você quer?

Por exemplo, no arquivo.c, existem 15 linhas que correspondem à expressão grep:

% grep -c "#include" file.c 15 % grep "#include" file.c | head -18 #(more than the number of matches) #include <sys/blah.h> #include <sys/blah2.h> #include <sys/blah3.h> #include <sys/blah4.h> #include <sys/blah5.h> #include <sys/blah6.h> #include <sys/blah7.h> #include <sys/blah8.h> #include <sys/blah9.h> #include <sys/blah10.h> #include <sys/blah11.h> #include <sys/blah12.h> #include <sys/blah13.h> #include <sys/blah14.h> #include <sys/blah15.h>

OR

% grep "#include" file.c | tail -18 #include <sys/blah.h> #include <sys/blah2.h> #include <sys/blah3.h> #include <sys/blah4.h> #include <sys/blah5.h> #include <sys/blah6.h> #include <sys/blah7.h> #include <sys/blah8.h> #include <sys/blah9.h> #include <sys/blah10.h> #include <sys/blah11.h> #include <sys/blah12.h> #include <sys/blah13.h> #include <sys/blah14.h> #include <sys/blah15.h>

Por favor, explique como isso NÃO está de acordo com os parâmetros da sua tarefa?

    
por 14.05.2015 / 18:46
0

Com uma passagem e com um comando e em um sistema GNU e memória suficiente para armazenar todo o conteúdo do arquivo, e você não se importa com a ordem das últimas 3 linhas de inclusão, você poderia fazer:

awk '{c=c$0RT}/#include/{l[n++%3]=$0RS}END{printf"%s",(n>=3?l[0]l[1]l[2]:c)}' file.c
    
por 15.05.2015 / 15:29
0

Tente isto:

lines=$(grep "#include" tester.c);
   test $(echo $lines | wc -l) -ge 3 && {echo $lines | head -n 3} || cat tester.c;
       unset lines

Nota: esta solução pode ser digitada como uma linha. Eu o separei em várias linhas apenas para facilitar a leitura.

EDITAR

Em resposta aos comentários, suponho que você poderia substituir os pontos-e-vírgulas por && para tornar a linha

lines=$(grep "#include" tester.c) &&
   test $(echo $lines | wc -l) -ge 3 && {echo $lines | head -n 3} || cat tester.c

Isso não é tecnicamente errado, mas deixa lines set. Também o && é desnecessário, mas está bem.

Esta solução modificada ainda tem a vantagem de você não ler o arquivo duas vezes, como em algumas das outras soluções.

Se você não puder usar test , poderá usar colchetes como este:

lines=$(grep "#include" tester.c) &&
   [ $(echo $lines | wc -l) -ge 3 ] && {echo $lines | head -n 3} || cat tester.c

Você pode até remover as chaves:

lines=$(grep "#include" tester.c) &&
   [ $(echo $lines | wc -l) -ge 3 ] && echo $lines | head -n 3 || cat tester.c

Uma dessas variações deve atender às suas necessidades.

    
por 14.05.2015 / 18:21

Tags