Seu último exemplo é o mais seguro contra falhas.
trap 'rm -rf "$dir"' EXIT
Isso será executado enquanto o próprio shell ainda estiver funcional. Basicamente, o SIGKILL é a única coisa que ele não manipula, uma vez que o shell é terminado à força.
(talvez o SIGSEGV também não tenha tentado, mas pode ser capturado)
Se você não deixar o shell para limpar depois de si mesmo, a única outra alternativa possível é fazer com que o kernel faça isso. Isso normalmente não é um recurso do kernel, no entanto, há um truque que você pode fazer, mas ele tem seus próprios problemas:
#!/bin/bash
mkdir /tmp/$$
mount -t tmpfs none /tmp/$$
cd /tmp/$$
umount -l /tmp/$$
rmdir /tmp/$$
do_stuff
Basicamente, você cria uma montagem tmpfs e depois a desmonta. Quando o script estiver pronto, ele será removido.
A desvantagem, além de ser excessivamente complexa, é que, se o script morrer por qualquer motivo antes da desmontagem, você não terá uma montagem por aí.
Isso também usa tmpfs, que consumirá memória. Mas você pode tornar o processo mais complexo e usar um sistema de arquivos em loop, e remover o arquivo que o faz depois de montado.
Por fim, o trap
é melhor em termos de simplicidade e segurança, e, a menos que o script esteja sendo regularmente SIGKILLed, eu continuaria com ele.