Isso está acontecendo porque você está lançando subshells por todo o lugar: -)
A sintaxe que você está usando, onde você faz { some_stuff } 2>/dev/null | other_stuff
, cria um subshell para cada bit de código entre as chaves. Isso pode ser demonstrado facilmente com o seguinte script:
#!/bin/bash
{ sleep 1; } | { sleep 2; } | { sleep 3; } & ps axf
Que resulta na seguinte saída:
phemmer 26014 19 0 0.0 0.0 S+ 00:00 \_ bash /tmp/test.sh
phemmer 26015 19 0 0.0 0.0 S+ 00:00 \_ bash /tmp/test.sh
phemmer 26018 19 0 0.0 0.0 S+ 00:00 | \_ sleep 1
phemmer 26016 19 0 0.0 0.0 S+ 00:00 \_ bash /tmp/test.sh
phemmer 26020 19 0 0.0 0.0 S+ 00:00 | \_ sleep 2
phemmer 26017 19 0 0.0 0.0 S+ 00:00 \_ bash /tmp/test.sh
phemmer 26021 19 0 0.0 0.0 S+ 00:00 | \_ sleep 3
phemmer 26019 19 0 0.0 0.0 R+ 00:00 \_ ps axf
A razão para isso é porque todos os comandos em um pipeline devem ser executados ao mesmo tempo. Então a casca tem que bifurcar-se.
Este não é um grande desperdício de recursos, já que o Linux usa a alocação de memória copy-on-write ao forjar. O que significa que quando um processo se bifurca, o uso de memória não é duplicado. Ambos os processos usarão a mesma memória até que um dos processos altere essa memória. Depois de alterado, essa página de memória específica é copiada.
A única solução para isso é não executar várias partes do script no mesmo pipeline. Como você consegue isso depende inteiramente de você.
Uma opção pode ser usar um exec
para o último comando no subshell. Desta forma, o comando que está sendo executado assume o PID do shell.