Obtendo a funcionalidade "garantir" / "finalmente" em um comando shell (não script)?

5

Eu preciso saber se um comando foi bem-sucedido ou não, e executar algumas limpezas incondicionalmente depois.

Nenhuma das opções normais para executar comandos sequenciais parece ser aplicável aqui:

$ mycmd.sh && rm -rf temp_files/    # correct exit status, cleanup fails if mycmd fails
$ mycmd.sh ;  rm -rf temp_files/  # incorrect exit status, always cleans up
$ mycmd.sh || rm -rf temp_files/    # correct exit status, cleanup fails if mycmd succeeds

Se eu fosse fazer isso em um shell script, faria algo assim:

#!/usr/bin/env bash
mycmd.sh
RET=$?
rm -rf temp_files
exit $RET

Existe uma maneira mais idiomática de realizar isso na linha de comando do que o ponto-e-vírgula encadeando todos esses comandos juntos?

    
por Ian 05.06.2017 / 21:43

5 respostas

10

As novas linhas em um script são quase sempre equivalentes a ponto e vírgula:

mycmd.sh; ret=$?; rm -rf temp_files; exit $ret

Em resposta à edição:

Como alternativa, você também pode usar um trap e um subshell:

( trap 'rm -rf temp_files' EXIT; mycmd.sh )
    
por 05.06.2017 / 21:47
5

Se você está procurando uma cópia de alguns idiomas ' try { } finally { } , existe outra maneira: usar o trap embutido em bash e outros POSIXy shells (veja help trap ).

#!/bin/bash

# exit with this by default, if it is not set later
exit_code=0  

# the cleanup function will be the exit point
cleanup () {
  # ignore stderr from rm incase the hook is called twice
  rm -rf "temp_files/" &> /dev/null  
  # exit(code)
  exit $exit_code
}

# register the cleanup function for all these signal types (see link below)
trap cleanup EXIT ERR INT TERM

# run your other script
mycmd.sh

# set the exit_code with the real result, used when cleanup is called
exit_code=$?

Leia sobre os argumentos do comando trap.

Observe que cleanup é chamado:

  • se este script for enviado SIGINT ou SIGTERM ou se CTRL-C for pressionado (SIGINT)
  • se este script sair normalmente com 0
  • se mycmd.sh sair com status diferente de zero (talvez não seja o que você deseja - remover ERR dos argumentos da interceptação para desativar )
por 06.06.2017 / 02:57
3

Em zsh :

{mycmd.sh} always {rm -rf temp_files}

A parte always será executada mesmo no caso de um erro como um glob sem correspondência ou erro de sintaxe de tempo de execução que sairia do script.

    
por 13.06.2017 / 14:42
1
mycmd.sh && { rm -r temp_files;  true; } || { rm -r temp_files; false; }
    
por 06.06.2017 / 00:06
0

Eu uso os operadores && e || exclusivamente agora, em vez de if;then . Mas às vezes é muito mais claro voltar para 'se; então'. Este é um daqueles momentos desde que você precisa de uma solução de linha de comando.

Eu reescreveria assim ...

$ if mycmd.sh; then { success; actions; } else { fail; actions; } fi;  rm -rf temp_files/
  • mycmd.sh é executado e if usa seu resultado
  • As chaves garantem que a lista de vários comandos dentro são tratados como um bloco e aumentam a clareza. Os espaços em torno das chaves e ponto e vírgula após cada comando dentro, incluindo os últimos são obrigatórios.
  • Como o rm ocorre após o fi , ele será executado incondicionalmente.
por 05.06.2017 / 23:46