Isso realmente depende de quem você está tentando impedir de ler o script e quais recursos você está esperando que o sistema tenha.
Uma opção é simplesmente usar muitos programas diferentes para fazer diferentes partes do seu script: shell, awk, sed, perl, etc., bem como muitos parâmetros obscuros de ferramentas, forçando o leitor a se referir constantemente a páginas man.
Mesmo dentro de um shell, você pode criar funções e variáveis desnecessárias, tornando-as interdependentes de maneira confusa. E, claro, dar a eles nomes enganosos.
Mais complicado, você pode acrescentar dados binários ao final do seu shell e fazer com que o seu shell extraia e execute o binário. Acredito que os drivers Linux da nVidia e o JDK da Sun estejam instalados dessa forma (os dados binários são um RPM, que o shell extrai e instala). Outro exemplo que acabei de baixar no outro dia é o programa soapUI .
Nesse sentido, é possível ter um arquivo de texto que possa ser compilado ou interpretado em vários idiomas, para que ele possa iniciar como um shell, compilar-se como um programa em C e executar o resultado. O IOCCC tem alguns exemplos.