export myvar
awk 'substr($0, 42) == ENVIRON["myvar"]' < "$filelist"
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.
Perl para o resgate!
perl -ne 'BEGIN { $search = shift }
print if /^.{41}\Q$search\E$/;
' -- "$myvar" "$filelist"
-n
lê o arquivo linha por linha. \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! 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).
Tags bash grep shell-script