It really depends on what you want to achieve with this shell. If you have a reliable shell you want to piggyback then your approach is as good as mine... (except instead of having a regex for each command, do the UNIX modular design: make some small commands and one regex for a whole group of them)
If you want to enhance a shell ( DOS comes to mind ) you need to decide what to emulate. Make some recursive subs that break a command into parts, emulate pipes "|" by openning things with open(PRINTER, "| lpr -Plp1") Perl pipe opens, emulate file io ">","<" with... I guess pipe opens again. To run commands you can have a variable with the directory that contains all the executables, upon command submittal you can check whether the typed command is one of those executables and then run it.