Your timeout problem is very similar to what was discussed in Eval leaving zombies when dying with alarm. If you don't want to use SIGALRM, there's an approach based on IO::Select - see that thread for more details.
Another issue I see is that you should close STDIN to the bash process after sending the command: