
Donn Cave wrote:
On Thu, 11 May 2006, Brian Hulley wrote: ...
-- catenate all files in a specified directory
catenate outputFile dir = withDir dir $ ls >>= cat outputFile
So, you would apply this like catenate "result" "/etc/stuff" ? String literals need quotes?
Yes - why not? Also, on Windows for example, filenames can have spaces so quotes are needed anyway with any shell at the moment if such filenames are used. However if this was a real problem it *might* be possible to relax the need for quotes by using a much more complicated parsing algorithm that could take into account the types of expected args and coerce the appropriate unquoted lexemes/expressions into strings, but I don't know if this would really be worth the trouble, and it would introduce ambiguity eg is etc/stuff a filename or an arithmetic expression?
Of course the above could no doubt be improved but surely it is already far easier to understand and much more powerful than the idiosyncratic text based approach used in UNIX shells (including rc).
(cd /etc/stuff; cat * > result)
Well the problem here is that the command leaves you in /etc/stuff so you have to remember this when you subsequently execute another command. The advantage of withDir is that the original directory is restored afterwards, which might make it easier to write modular scripts. In any case you could also make a cd command in Haskell, and write: cd "etc/stuff" >> ls >>= cat "result"
?
renif extFrom extTo fileName = case split fileName of (n, ext) | ext == extFrom -> rename fileName (unsplit (n, extTo)) _ -> return ()
% ls >>= mapM_ (renif "txt" "hs")
$ for a in *.txt; do mv $a $(basename $a .txt); done
Well someone had to define the meaning of basename so if we make the definition of renif similarly built-in the comparison is between ls >>= mapM_ (renif "txt" "hs") and for a in *.txt; do mv $a $(basename $a .txt); done So the Haskell command is shorter, easier to read, and more re-usable, because mapM_ (renif "txt" "hs") can be used anywhere that supplies a list of files whereas "for a in *.txt" doesn't make the source of the list explicit. Do they come from the current directory? What if some other list of files should be used?
? Not saying the UNIX shell is a rich and well structured programming environment, and maybe FP is a good direction for that problem. But don't underestimate it, the principles behind it are sharp, and while I think you could expect to win on complex data structures, you can't afford to lose on simple commands, because that's where most of the action is.
From the above even the simple commands are easier in Haskell. The only drawback is the need to put quotes round filenames/paths but imho this doesn't seem like a major problem compared to the ease with which complex commands can be built up and the advantage of only having to learn one universal language.
Hm. Not to pick at the details too much, but you know "cat" is actually a standard UNIX command, that writes to standard output and has no output file parameter? What's up with the new parameter in your version - was it not going to be workable the way it was?
I forgot about this. You could define cat in Haskell as: cat :: [FileName] -> Shell String and have another command analogous to > to write a string into a file, say "into" into :: FileName -> String -> Shell () Then you could catenate all files in the current directory into a file called "result" by: ls >>= cat >>= into "result" (Same as cat * > result) So in balance I think that while some UNIX commands may be slightly shorter, the shortness comes at the expense of the assumptions they have to make about the kinds of things you want to do eg cat * works well if the only possible source of files is the current directory, but doesn't work at all if you want to create a list of files from some other operation (unless you create a temporary directory with symlinks etc but it easily degenerates into a very complicated mess compared to Haskell). Regards, Brian.