Quais shells (se houver) evitam ler heredocs na memória de uma só vez?

4

Existem boas técnicas para estruturar scripts de shell enormes e gerados automaticamente para que possam ser executados por sh mesmo se o arquivo não couber na memória? Além disso, existe algum tipo de garantia de que um heredoc não será lido na memória de uma vez só pelo próprio shell? Quais shells evitam armazenar os heredocs na memória, na prática, pode-se confiar em uma sh em conformidade com alguma máquina arbitrária para fazer isso?

Estou lendo sobre o GNU shar e queria saber se ele poderia ser usado para arquivos grandes demais para caber na memória. Usa heredocs para armazenar conteúdo.

sed -e 's/^X//' << 'SHAR_EOF' | uudecode &&
Msome binary content
Xsome text content
SHAR_EOF

No entanto, existem vários heredocs desse tipo, e há algum conteúdo fixo não nonedoc no final do script shar que presumivelmente precisa ser analisado antes que qualquer parte do script possa ser executada. Se o shell não analisasse todo o script, seria impossível rejeitar scripts malformados antes que o primeiro comando fosse executado.

Aqui está um trecho dos comentários do shell final de um arquivo shar:

...
else
test 'LC_ALL=C wc -c < 'a.binary'' -ne 126472 && \
  ${echo} "restoration warning:  size of 'a.binary' is not 126472"
  fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
     exit 1
fi
exit 0
    
por Gregory Nisbet 25.03.2017 / 23:40

1 resposta

4

Não há garantia de que o shell irá ou não carregar todo o documento aqui na memória. Enormes scripts não são típicos, então este não é um caso que os implementadores de shell possam otimizar. É até indesejável que todo o script não seja carregado na memória antes que a execução comece a executá-lo, mas todas as shells comuns são executadas antes do carregamento completo, o que significa que elas executam lixo se o arquivo de script for modificado durante a execução. >

Experimentalmente, no Debian jessie, dash, bash, mksh e zsh carregam um documento de 130kB aqui na memória, enquanto o ksh93 copia 64kB sem alocar mais memória. Então, a única maneira de usar um documento aqui que não cabe na memória é garantir que seu script seja executado com ksh93 (ou talvez ksh88) - e antes de fazer isso, por favor, assegure-se que este é o caso de outras versões, verificou que o comportamento é o mesmo com todas as compilações de ksh.

O que você pode fazer de forma mais portável é colocar todos os dados no final do script e usar tail -c $offset para extrair a carga útil. Ele funciona na prática porque nenhum dos shells usuais carrega o script completamente na memória antes de executá-lo. Esse método tem a vantagem de que a carga útil pode ser binária - os documentos aqui não podem conter a cadeia de caracteres final-de-heredoc ou bytes nulos.

Se o seu script é constante, você pode codificar o deslocamento da carga útil. Se não for, você pode colocar um marcador distinto no final do script e usar o awk para determinar sua localização:

offset=$(awk '{offset += length($0) + 1}
              $0 == "# payload starts here (do not remove this magic comment)" {
                  print offset; exit
              }')
…
tail -c "$offset" <"$0" — …
…
# payload starts here (do not remove this magic comment)

Se você tiver mais de uma carga útil, precisará de um comando mais complexo para extraí-las. Tenha em atenção que head -c não existe em todas as variantes unix. Você pode usar dd ibs=1 count=$bytes para obter o mesmo efeito, mas pode ser muito lento, pois muitas implementações de dd copiarão um byte de cada vez. O que eu recomendaria é acrescentar um arquivo tar e extrair arquivos pelo nome dele.

    
por 27.03.2017 / 02:28