Programação shell, evitando tempfiles

7

Eu frequentemente escrevo scripts shell do KSH que seguem o mesmo padrão:

  • (1) recupera a saída de um ou mais comandos
  • (2) formate-o usando o grep | cut | awk | sed e imprima-o na tela ou em um arquivo

Para fazer isso, geralmente armazeno a saída de (1) em um arquivo temporário e, em seguida, faço a formatação em (2) nesse arquivo.

Tome esse código, por exemplo:

TMPFILE=file.tmp

# If tmpfile exists rm it.
[ -f $TMPFILE ] && rm -f $TMPFILE

for SERVICE in $(myfunc); do
    getInfo $SERVICE > $TMPFILE # Store raw output in the TMPFILE

    # I retrieve the relevant data from the TMPFILE
    SERV_NAME=$(head -1 $TMPFILE | sed -e 's/ $//')
    SERV_HOSTNAME=$(grep HOSTNAME $TMPFILE | cut -d "=" -f2)
    SERV_ARGS=$(grep Arguments $TMPFILE | cut -d ":" -f2)

    print $SERV_NAME $SEP $SERV_HOSTNAME $SEP $SERV_ARGS
    rm -f $TMPFILE #rm the TMPFILE in vue of next iteration
done

Existe uma maneira, usando pipes, redirecionamentos e whatnots, para evitar escrever um arquivo no disco a cada vez?

Se isso ajudar, estou usando o ksh Versão M-11/16 / 88i

    
por rahmu 28.09.2011 / 14:10

4 respostas

9

Seu código parece um exemplo inteiramente justificado de usar tempfiles para mim. Eu ficaria: fique com essa abordagem. A única coisa que realmente precisa ser alterada é a maneira como você cria o arquivo temporário. Use algo como

 TMP=$(tempfile)

ou

 TMP=$(mktemp)

ou pelo menos

 TMP=/tmp/myscript_$$

Dessa forma, você não permitirá que o nome seja facilmente previsto (segurança) e a interferência de regras entre várias instâncias do script seja executada ao mesmo tempo.

    
por 28.09.2011 / 14:19
5

Você pode usar uma variável:

info="$(getInfo $SERVICE)"
SERV_NAME="$(head -1 $TMPFILE <<<"$info" | sed -e 's/ $//')"
...

De man ksh :

<<<word       A  short  form of here document in which word becomes the
              contents of the here-document after any parameter  expan-
              sion,  command  substitution, and arithmetic substitution
              occur.

As vantagens incluem:

  • Ativa a execução paralela.
  • Na minha experiência, isso é muito mais rápido do que arquivos temporários. A menos que você tenha tantos dados que você acabe trocando, ele deve ser muito mais rápido (apenas barramento de buffers de armazenamento em cache HD, o que pode ser tão rápido para pequenas quantidades de dados).
  • Outros processos ou usuários não podem atrapalhar seus dados.
por 28.09.2011 / 14:28
2

Você tem duas opções:

  1. Você recupera os dados uma vez (no seu exemplo com getInfo ) e os armazena em um arquivo como você faz.

  2. Você busca os dados a cada vez e não os armazena localmente, ou seja, você chama getInfo a cada vez

Eu não vejo o problema em criar um arquivo temporário para evitar o reprocessamento / re-busca.

Se você está preocupado em deixar o arquivo temporário em algum lugar, você sempre pode usar trap para ter certeza de apagá-lo caso o script seja eliminado / interrompido

trap "rm -f $TMPFILE" EXIT HUP INT QUIT TERM

e use mktemp para criar um nome de arquivo exclusivo para seu arquivo temporário.

    
por 28.09.2011 / 14:17
1

Em vez de gerar um arquivo, construa instruções de atribuição de shell e avalie essa saída.

for SERVICE in $(myfunc); do
    eval $(getInfo $SERVICE |
               sed -n -e '1/\(.*\) *$/SERV_NAME=""/p' \
                   -e '/HOSTNAME/s/^[^=]*=\([^=]*\).*/SERV_HOSTNAME=""/p' \
                   -e '/Arguments/^[^:]*:\([^:]*\).*/SERV_ARGS=""/p')
    print $SERV_NAME $SEP $SERV_HOSTNAME $SED $SERV_ARGS
done

Ou se você quiser apenas imprimir as informações:

for SERVICE in $(myfunc); do
    getInfo $SERVICE | awk -vsep="$SEP" '
        BEGIN{OFS=sep}
        NR == 1 { sub(/ *$/,""); SERV_NAME=$0 }
        /HOSTNAME/ { split($0, HOST, /=/; SERV_HOSTNAME=HOST[2]; }
        /Arguments/ { split($0, ARGS, /:/; SERV_ARGS }
        END { print SERV_NAME, SERV_HOSTNAME, SERV_ARGS }'
done
    
por 28.09.2011 / 16:33