Evita que um comando seja executado após outro comando

3

Eu gostaria que o shell me avisasse de possíveis erros que estou prestes a cometer.

Como posso evitar que um comando cmd2 seja executado após cmd1 .

Por exemplo:

$ tar tf file.tar
$ rm !$

Aqui, esqueci que listei o arquivo em vez de extrair seu conteúdo.

Estou procurando uma solução genérica, que faça uma escolha com base no nome e no valor de retorno do comando anterior e no comando atual.

    
por alecail 08.07.2012 / 13:24

4 respostas

4

Você está pedindo muito com uma resposta geral, mas deve ser possível fazê-lo com relativa facilidade até certo ponto.

No entanto, bash tem pelo menos dois mecanismos gerais que podem ajudar a resolver isso. Você pode definir a variável PROMPT_COMMAND e isso será executado depois de qualquer coisa que você executar no seu prompt:

$ PROMPT_COMMAND='echo BOFH says hi!'
BOFH says hi!
$ man man
BOFH says hi!
$

Podemos usar isso para salvar o último comando digitado e, em seguida, relê-lo, para que possamos armazená-lo na memória para uso posterior (a expansão do histórico não funciona neste caso):

$ PROMPT_COMMAND='history -a; tail -n1 ~/.bash_history'
PROMPT_COMMAND='history -a; tail -n1 ~/.bash_history'
$ wc < test.sh | grep .
 5 11 63
wc < test.sh | grep .

Agora vem a parte lógica, que é completamente com você. Vamos usar apenas uma lógica muito simplista para evitar o exemplo na questão. O importante é lembrar que as funções do bash ainda funcionam lá, então você não precisa empinar tudo em uma única linha, em uma única variável.

$ # define the logic check
$ checkSanity() { grep -q "tar  *[tf][[:alpha:]]*[tf] " <<< "$@" && echo "be careful, don't delete this"; }
checkSanity() { grep -q "tar  *[tf][[:alpha:]]*[tf] " <<< "$@" && echo "be careful, don't delete this"; }
$ # feed it the last command
$ PROMPT_COMMAND='history -a; last=$(tail -n1 ~/.bash_history); checkSanity "$last"'
$ # simple test
$ tar tf test.sh 2>/dev/null 
be careful, don't delete this
$ tar xf test.sh 2>/dev/null 
$

Naturalmente, essa abordagem é útil para evitar o PEBKAC (especialmente se você adicionar sleep 3 no final), mas ele não pode quebrar o próximo comando por conta própria.

Se isso é realmente o que você quer, capture o sinal DEBUG (por exemplo, trap 'echo "I am not deterministic, haha"' DEBUG ), pois ele é executado de antemão. Tenha cuidado ao combinar essas duas abordagens, pois a saída / ação será dobrada:

$ df
/.../
$ trap 'tail -n1 ~/.bash_history' DEBUG
df
df
trap 'tail -n1 ~/.bash_history' DEBUG
trap 'tail -n1 ~/.bash_history' DEBUG

Para fazer com que a armadilha quebre um comando, você terá que ativar extdebug ( shopt -s extdebug ). Você também não precisa salvar e reler o histórico constantemente, mas pode inspecionar $BASH_COMMAND para obter o comando prestes a ser executado. Então você só precisa garantir que o verificador lógico retorne 1 quando detectar algo ruim e 0 caso contrário.

          extdebug
                  If set, behavior intended for use by debuggers is enabled:
    /.../
                  2.     If  the command run by the DEBUG trap returns a non-zero value, the next
                         command is skipped and not executed.


$ checkSanity() { if grep -q "tar  *[tf][[:alpha:]]*[tf] " <<< "$1"; then  echo "be careful, don't delete this"; return 1; fi; }
$ trap 'checkSanity "$BASH_COMMAND"' DEBUG
$ # simple test
$ tar tf test.sh 2>/dev/null 
be careful, don't delete this
$ tar xf test.sh 2>/dev/null 
$
    
por 08.07.2012 / 20:31
6

No bash, você pode definir shopt -s histverify , o que impediria que o histórico fosse expandido e executado em uma etapa. Aqui o $ rm !$ acima se expandirá para $ rm file.tar no primeiro pressionamento de Enter , permitindo que você verifique ou edite antes de pressionar Enter novamente para executar.
Uma solução mais geral se transformaria no território "Fazer o que eu quero dizer" , exigindo que um programa adivinhasse suas intenções.

    
por 08.07.2012 / 18:07
4

Nesse caso, você se beneficiaria de avisos de erro do comando rm .

Em seus scripts de inicialização do shell (assumindo bash , portanto, ~/.bashrc ), você poderia alias rm para -i ou -I .

alias rm="rm -i"

De man 1 rm

   -i     prompt before every removal

   -I     prompt once before removing more than three files, or when
          removing recursively.  Less intrusive than -i, while still
          giving protection against most mistakes

A questão mais genérica de "Como eu protejo meu sistema de mim mesmo" tem sido discutido antes . Em poucas palavras, preste atenção ao que você está fazendo , e só execute comandos como root quando necessário.

    
por 08.07.2012 / 16:23
0

No seu caso específico, eu faria:

tar tf file.tar
if [ -f file ]; then   # Assuming you meant to extract a file
  rm -f file.tar
else
  echo "file is missing. Command failed."
fi

Se você quiser verificar se há erros e somente executar o próximo comando se o primeiro for bem-sucedido

some_command && another_command

Embora no seu caso específico, o segundo comando ainda seria executado, pois não cometeu erros, embora não tenha feito o que você queria.

    
por 12.07.2012 / 13:53