Haskell's reputation exaggerates how this API was less powerful, or missing altogether, in early versions of the language. But its reputation is not what you write programs in.
Functional languages are all about keeping side effects under control so that you can tell what will and what will not change the state of the system. Think of it as incorporating awareness of program state into the type-system of the language.
Imagine a "normal" shell type chain:
ps -ef | grep 'elephant' | sort -nk2 >> logfile.txt
neither `grep`, nor `sort` should have any side effects, and if they do, then you have potential for weird bugs later on. (imagine your / is mounted read only, and sort has a side effect of writing everything to a temp file. now suddenly this otherwise sane looking chain stops working).In the command, you have a single input, and a single output (which has a specific mode, append). Those are the only IO functions which you've specified, but you have no guarantee that anything else will not make a mess of things.
The dream for "pure functional" shell scripting would be where the side effects are more obvious, and controllable, not to eliminate them.
(Not to mention checking locales to decide how to sort, checking if the output is terminal so that we can add colors, but only if the environment variable says so, and on, and on...)
The meme that Haskell can't do side-effects or that's its hard just won't die...