List Arquivos que não contêm uma string específica em uma linha específica

0

Estou tentando encontrar todos os arquivos com extensão .md

find . -type f -name "*.md"

Depois, quero filtrar os arquivos cuja segunda linha (número da linha = 2) não contém essa sequência exata começando e terminando com author: Mr. Xab Ycd

Como posso fazer a segunda parte? Fazer grep seria ineficiente, pois verifica o arquivo inteiro.

    
por Nikhil 15.09.2018 / 19:54

5 respostas

2
find . -type f -name '*.md' -exec \
  sh -c 'sed 1d\;q "$1" | grep -qvx "author: Mr. Xab Ycd"' sh {} \; -print

O comando acima inclui todos os requisitos sem ter que passar nomes de arquivos potencialmente confusos por meio de canais de shell.

A primeira parte é copiada como é (quase) da sua - procure arquivos com o nome *.md . Eu mudei as aspas duplas para "simples" aspas simples; não há diferença funcional no seu caso, mas se você quisesse procurar arquivos com o nome *.$md , aspas duplas tentariam expandir a variável $md .

Os nomes de arquivos correspondentes são então passados por outro teste via -exec . O parâmetro para exec é um script de shell pequeno cujo trabalho é determinar o sucesso ou a falha do nome de arquivo fornecido em $1 . O comando sed imprime apenas a linha dois; Existem diferentes maneiras de fazer isso, como:

  • sed -n '2{p;q;}' ou
  • sed '1d;q

O primeiro diz "não imprima linhas por padrão, mas quando você vir a linha dois, imprima e saia". O segundo diz "imprimir linhas por padrão, mas exclua a linha um, pare depois disso (na linha dois); o comando q imprimirá o buffer atual antes de sair.

Essa linha de texto (se houver) é passada ao grep, que verifica se a linha inteira corresponde (ou não) ao texto fornecido. Se não corresponder ( -v ), o comando inteiro será bem-sucedido e, portanto, find imprimirá o nome do arquivo.

    
por 15.09.2018 / 20:15
1
find . -type f -name '*.md' -exec awk '
    FNR == 2 && $0 == "author: Mr. Xab Ycd" { exit 1 }
    FNR >  2 { exit 0 }' {} ';' -print

Isso usaria awk para filtrar qualquer arquivo que tenha pelo menos duas linhas e tenha uma segunda linha que seja exatamente a que você mencionou. Isso é feito saindo explicitamente com um status de saída diferente de zero se a segunda linha ( FNR == 2 ) for exatamente igual à sequência. Também sai com um status de saída zero se atingir qualquer linha após a linha dois para não analisar mais do que o necessário.

O comando find continuará a imprimir o nome do caminho do arquivo com -print se awk sair com um status de saída zero (a string não foi encontrada na linha dois).

    
por 17.09.2018 / 16:49
1

com zsh :

by_Xab() {
  local line
  {
    IFS= read -r line &&
      IFS= read -r line &&
      [[ $line = "author: Mr. Xab Ycd" ]]
  } < ${1-$REPLY}
}
printf '%s\n' **/*.md(D.^+by_Xab)

Que lê no máximo 2 linhas para cada arquivo e não executa nenhum comando (é tudo incorporado), então seria muito mais eficiente do que find -exec abordagens que executam um comando ou mais por arquivo.

Com o GNU awk , você poderia fazer:

STRING='author: Mr. Xab Ycd' find . -name '*.md' -type f -exec gawk '
  BEGINFILE {found = 0}
  FNR == 2  {found = $0 == ENVIRON["STRING"]; nextfile}
  ENDFILE   {if (!found) print FILENAME}' {} +

O qual executaria uma invocação de find e a sintaxe -exec ... {} + como poucas invocações de gawk possível.

    
por 17.09.2018 / 18:34
0

Acredito que awk é a ferramenta certa para o trabalho, mas não sei Awk . No entanto, a resposta a seguir funciona usando Bash com a opção globstar habilitada ( shopt -s globstar ).

prompt% awk 'FNR==2 {print FILENAME, $0}' **/*.md | grep -v 'author: Mr. Xab Ycd' | cut -f1 -d ' '

awk exibe a segunda linha de todos os arquivos denominados *.md no diretório atual ou em subdiretórios e grep filtra nomes de arquivos que não incluem a cadeia especificada.

    
por 17.09.2018 / 15:52
0

Quando find fornece um arquivo de cada vez e o ônus da impressão é com find :

find . -type f -exec perl -lne '$. == 2 && exit +/^author: Mr\. Xab Ycd$/' {} \; -print

aqui find fornece arquivos em um grupo e a tarefa de impressão é manipulada por perl :

find . -type f -size 0 -print -o -exec perl -lne '
   print $ARGV if $. == 2 && !/^author: Mr\. Xab Ycd$/;
   close(ARGV),next if $. == 2;
   print($ARGV),close(ARGV) if eof;
' {} +

O fechamento - ing de ARGV é essencial para a OTW, o contador de linha, também conhecido como $. isn inicializado para o próximo arquivo.

Observe que a cláusula eof é necessária. OTW a segunda linha nunca seria alcançada para arquivos de comprimento > 1 e a verificação da segunda linha nunca aconteceria.

    
por 18.09.2018 / 05:24