O código a seguir deve demonstrar e ajudar a testar Pattern Matching
expressões ineficientes em Parameter Substitution
uma variável de string separada por nova linha vs. matriz.
O objetivo é atingir pelo menos um desempenho equivalente ao grep
, ao filtrar apenas git status -s
resultados que envolvam index
alterações (total ou parcialmente staged
).
Então, basicamente, cada entrada de alteração que começa com um sinalizador de status curto do Git, como [MTARDC]
(sinalizando uma alteração em estágio/índice), incluindo sinalizadores duplos (sinalizando alterações parcialmente em estágio no índice e worktree
), exceto untracked
ou unstaged-only
alterações (começando com [ ?]).
Observe que R
os sinalizadores de alteração (renomear) podem ser seguidos por vários dígitos, veja também exemplos nos dados de teste abaixo (possivelmente até mesmo para índice e árvore de trabalho, ou seja, R104R104). Veja: formato curto do status do Git
Os dados de teste também contêm nomes de arquivos com caracteres especiais potencialmente problemáticos, como sequências de escape, espaço ou "e" comercial: [\ $*"]
.
Observe também que a substituição baseada em Pattern Matching requer uma negação de padrões, em comparação a um RegEx
for grep com os mesmos resultados. Para imprimir os resultados, simplesmente comente as &>/dev/null
partes.
#! /bin/bash
# Set extended pattern matching active
shopt -s extglob
clear
unset -v tmpVar tmpArr
# Populate tmpVar and tmpArr for testing
for i in {1..3}; do
tmpVar+=' A addedW1'[$i]$'\n'
tmpVar+='A addedI1'[$i]$'\n'
tmpVar+='AM addedA1'[$i]$'\n'
tmpVar+=' C copiedW1'[$i]$'\n'
tmpVar+='C copiedI1'[$i]$'\n'
tmpVar+='CR copied A1'[$i]$'\n'
tmpVar+=' D removedW1'[$i]$'\n'
tmpVar+='D removedI1'[$i]$'\n'
tmpVar+='DM removedA1'[$i]$'\n'
tmpVar+=' M modifiedW1'[$i]$'\n'
tmpVar+='M modifiedW1'[$i]$'\n'
tmpVar+='MR modifiedA1'[$i]$'\n'
tmpVar+=' R101 renamedW1'[$i]$'\n'
tmpVar+='R102 renamedI2'[$i]$'\n'
tmpVar+='R103D renamedA1'[$i]$'\n'
tmpVar+=' T typeChangedW1'[$i]$'\n'
tmpVar+='T typeChangedI1'[$i]$'\n'
tmpVar+='TM typeChangedA1'[$i]$'\n'
tmpVar+='?? exec2.bin'[$i]$'\n'
tmpVar+='?? file1.txt'[$i]$'\n'
tmpVar+='?? test.launch2'[$i]$'\n'
tmpVar+='A file00 0.bin'[$i]$'\n'
tmpVar+='A file11*1.bin'[$i]$'\n'
tmpVar+='A file22\03457zwei.bin'[$i]$'\n'
tmpVar+='A file33\t3.bin'[$i]$'\n'
tmpVar+='A file44$4.bin'[$i]$'\n'
tmpVar+='A file55"$(echo EXE)"5.bin'[$i]$'\n'
tmpVar+='M exec1.bin'[$i]$'\n'
tmpVar+=' M test.launch1'[$i]$'\n'
tmpVar+=' M myproject/src/main/java/util/MyUtil.java'[$i]$'\n'
tmpVar+='M myproject/src/test/util/MyUtilTest.java'[$i]$'\n'
tmpVar+='R104R104 myproject/src/test/util/MyUtil2Test.java'[$i]$'\n'
tmpVar+=' A invalidAdd'[$i]$'\n'
tmpVar+='R invalidRename'[$i]$'\n'
tmpArr+=(" A addedW1[$i]")
tmpArr+=("A addedI1[$i]")
tmpArr+=("AM addedA1[$i]")
tmpArr+=(" C copiedW1[$i]")
tmpArr+=("C copiedI1[$i]")
tmpArr+=("CR copied A1[$i]")
tmpArr+=(" D removedW1[$i]")
tmpArr+=("D removedI1[$i]")
tmpArr+=("DM removedA1[$i]")
tmpArr+=(" M modifiedW1[$i]")
tmpArr+=("M modifiedW1[$i]")
tmpArr+=("MR modifiedA1[$i]")
tmpArr+=(" R101 renamedW1[$i]")
tmpArr+=("R102 renamedI2[$i]")
tmpArr+=("R103D renamedA1[$i]")
tmpArr+=(" T typeChangedW1[$i]")
tmpArr+=("T typeChangedI1[$i]")
tmpArr+=("TM typeChangedA1[$i]")
tmpArr+=("?? exec2.bin[$i]")
tmpArr+=("?? file1.txt[$i]")
tmpArr+=("?? test.launch2[$i]")
tmpArr+=("A file00 0.bin[$i]")
tmpArr+=("A file11*1.bin[$i]")
tmpArr+=("A file22\03457zwei.bin[$i]")
tmpArr+=("A file33\t3.bin[$i]")
tmpArr+=("A file44$4.bin[$i]")
tmpArr+=('A file55"$(echo EXE)"5.bin['$i']')
tmpArr+=("M exec1.bin[$i]")
tmpArr+=(" M test.launch1[$i]")
tmpArr+=(" M myproject/src/main/java/util/MyUtil.java[$i]")
tmpArr+=("M myproject/src/test/util/MyUtilTest.java[$i]")
tmpArr+=("R104R104 myproject/src/test/util/MyUtil2Test.java[$i]")
tmpArr+=(" A invalidAdd[$i]")
tmpArr+=("R invalidRename[$i]")
done
# Perf-test array or string var filtering via grep
_IFS="$IFS"; IFS=$'\n'
startTime="$EPOCHREALTIME"
grep '^[MTARDC]' <<<"${tmpArr[*]}" &>/dev/null
stopTime="$EPOCHREALTIME"
IFS="$_IFS"
echo
awk 'BEGIN { printf "ELAPSED TIME via grep filtering from ARRAY: "; print '"$stopTime"' - '"$startTime"' }'
# Perf-test array filtering via Pattern Matching in Parameter Substitution
startTime="$EPOCHREALTIME"
printf '%s\n' "${tmpArr[@]/#[? ][?MTARDC]*([0-9]) *}" &>/dev/null
stopTime="$EPOCHREALTIME"
echo
awk 'BEGIN { printf "ELAPSED TIME via parameter substitution from ARRAY: "; print '"$stopTime"' - '"$startTime"' }'
# Perf-test string var filtering via Pattern Matching in Parameter Substitution
startTime="$EPOCHREALTIME"
printf '%s\n' "${tmpVar//[? ][?MTARDC]*([0-9]) *([^$'\n'])?($'\n')}" &>/dev/null
stopTime="$EPOCHREALTIME"
echo
awk 'BEGIN { printf "ELAPSED TIME via parameter substitution from VAR: "; print '"$stopTime"' - '"$startTime"' }'
# RESULT:
#ELAPSED TIME via grep filtering from ARRAY: 0.054975
#ELAPSED TIME via parameter substitution from ARRAY: 0.00031805
#ELAPSED TIME via parameter substitution from VAR: 4.546
Como pode ser visto, grep
é bom, mas a variante nº 2 (filtragem de matriz via Parameter substitution
) é muito mais rápida, então, para matrizes grandes, há uma boa alternativa ao grep.
A filtragem de strings var via Parameter Substitution
, por outro lado, é terrivelmente lenta.
Principalmente devido ao fato de que o padrão de correspondência não pode terminar com *
(o que removeria tudo até o final da string da primeira correspondência), mas porque *([^$'\n'])?($'\n')
, em vez disso, ele precisa, para corresponder (e remover) tudo em uma correspondência, até a próxima nova linha e, até certo ponto, devido à tmpVar//
correspondência gananciosa.
Existe outra maneira/padrão para o exemplo, para processar strings var com Pattern Matching
, da mesma forma que para array - sem usar a problemática e lenta correspondência de caracteres de negação de nova linha e para chegar perto da velocidade do exemplo de array?