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
$