Is the shell permitted to optimize out useless terminating commands?

No, that would be a bad idea.

cat hugeregularfile.txt > /dev/null and touch -a hugeregularfile.txt are not the same. cat will read the whole file, even if you redirect the output to /dev/null. And reading the whole file might be exactly what you want. For example in order to cache it so that later reads will be significantly faster. The shell can't know your intention.

Similarly, a C compiler will never optimize out reading a file, even if you don't look at the stuff you read.


No, since /dev/null is just a name, which could be used for any other device or for a file other than what "normally" is a data sink.

So a shell (or any other program) has no idea, based on the name, whether the file it is writing to is doing something "for real" with the data. There are AFAIK also no system calls the shell program can make, to determine that e.g. the file descriptor is actually not doing anything.

Your comparison with optimising away code in a C program does not work, as a shell does not have the total overview that a C compiler has over a piece of source code. A shell doesn't know enough about /dev/null to optimize your example away, more like a C compiler doesn't know enough about code in a function call it dynamically links to, to not make the call.


It will not optimise out running commands (and you've already received a number of fine answers telling you why it should not), but it may optimise out forks, pipe/socketpairs, reads in some cases. The kind of optimisations it may do:

  • With some modern shells, the last command in a script can be executed in the process of the shell unless some traps have been set. For instance in sh -c ls, most sh implementations (bash, mksh, ksh, zsh, yash, some versions of ash) won't fork a process to run ls.
  • in ksh93, command substitution will not create a pipe or fork a process until an external command is called ($(echo foo) for instance will expand to foo without a pipe/socketpair or fork).
  • the read built-in of some shells (bash, AT&T ksh) will not do single-byte reads if they detect stdin is seekable (in which case they will do large reads and seek back to the end of what they are meant to read).