Usando grep com um padrão que contém uma variável a ser expandida

3

Eu tenho um arquivo no formulário

2e95d7582c53583fa8afb54e0fe7a2597c92cbba 1461065389 52880 temp/hello/file.txt
46c897a7aa8a641f46080b3431860bd0cd4a8f05 1461066221 207 temp/Another file.txt
83c8ce6b163ec1c615617fa0dbde9e928bc3daf4 1461056193 86112 Pictures/a photo.jpg
...

Ou seja, cada linha tem um número hexadecimal longo de 40 caracteres, seguido por espaço, um inteiro, espaço, um inteiro, espaço e um caminho de arquivo que pode conter espaços. Cada caminho é único.

Em um script bash eu tenho variáveis na mesma forma que uma linha no arquivo, somente sem a primeira string hexadecimal e espaço (41 primeiros caracteres), por exemplo:

myvar="1461066221 207 temp/Another file.txt"

Meu objetivo é encontrar a linha no arquivo que corresponde a myvar exatamente quando os primeiros 41 caracteres forem ignorados. Se essa correspondência for encontrada, eu gostaria que a variável line fosse definida para a linha completa no arquivo. Para o exemplo acima, line seria definido como

46c897a7aa8a641f46080b3431860bd0cd4a8f05 1461066221 207 temp/Another file.txt

Se não houver essa correspondência, line deve ser definido para a string vazia ou deixado não definido.

Minha solução é essa ( filelist é o nome do arquivo):

line=$(grep --color=never -E "^.{41}$myvar$" $filelist)

Um problema é que quando $myvar é expandido, ele pode conter símbolos especiais como . , + , até ^ ou $ , etc, que têm um significado especial para grep . Eu quero que grep realize uma correspondência exata de myvar em todos, exceto nos primeiros 41 caracteres de uma linha.

    
por DustByte 19.04.2016 / 17:09

3 respostas

4
export myvar
awk 'substr($0, 42) == ENVIRON["myvar"]' < "$filelist"
    
por 19.04.2016 / 18:09
6

Perl para o resgate!

perl -ne 'BEGIN { $search = shift }
          print if /^.{41}\Q$search\E$/;
         ' -- "$myvar" "$filelist"
  • -n lê o arquivo linha por linha.
  • O bloco BEGIN recupera o $ myvar do primeiro argumento para a variável $ search do Perl.
  • \Q...\E cita a parte interna (consulte quotemeta ). Isso manipula todos os caracteres especiais que a variável pode conter. Não esqueça de aspas duplas nas variáveis do shell!
por 19.04.2016 / 17:19
3

Esta é uma maneira diferente de ir; primeiro define 'linha' como vazia, e define se ela acha que 'myvar' corresponde aos 3 campos à direita.

line=
while IFS=' ' read -r hex int1 int2 rest
do
  if [[ "$myvar" = "$int1 $int2 $rest" ]]
  then
    line="$hex $int1 $int2 $rest"
  fi
done < filelist

Aqui está outra forma de usar o bash mapfile builtin e o segundo array associativo temporário:

mapfile -t < filelist # sets MAPFILE array
declare -A temparray
shopt -s extglob
for val in "${MAPFILE[@]}"
do 
  short="${val##*([^ ]) }"
  temparray[$short]="$val"
done
line=${m[$myvar]}
unset -v MAPFILE temparray val short

Isso traz o arquivo para um MAPFILE de matriz indexada, a qual nós então passamos por loop. O loop define a variável "curta" como a parte "sufixo" da linha - ela exclui os valores iniciais que correspondem ao padrão "zero ou mais não-espaços seguidos por um espaço" e, em seguida, define o valor da matriz associativa como "$ short "para ser a linha inteira (" $ val "). Fazer esse tipo de correspondência com o * requer que shopt -s extglob esteja em vigor. Em seguida, definimos a linha como o valor resultante (ou se esse valor não existir na matriz, o bash retornará o valor vazio / nulo).

    
por 19.04.2016 / 17:45