Simple Unix Tools

Simple Unix commandline tools written in Haskell.

This is intended as a beginner’s tutorial for learning Haskell from a “Let’s just solve things already!” point of view. The examples should help give a flavor of the beauty and expressiveness of Haskell programming.

These functions can be executed as one liners from a shell. For example, to use the Haskell version of ‘wc’:

   $ cat file.txt | ghc -e 'wc_l' UnixTools.hs

Or, one could define ‘main’ to be a chosen tool/function (add a line to the effect that “main = wc_l”) and then compile the tool with

   $ ghc --make UnixTools.hs
import Control.Monad.Instances
import Data.List
import Data.Char
import Data.Maybe
import Text.Printf
import System.Environment

-- First, two helpers
io f = interact (unlines . f . lines)

showln  = (++ "n") . show

-- remove duplicate lines from a file (like uniq)
uniq    = nub

-- repeat the input file infinitely
rpt     = cycle

-- Return the head -10 line of a file
take'   = take 10

-- Remove the first 10 lines of a file
drop'   = drop 10

-- Return the head -1 line of a file
head'   = head

-- Return the tail -1 line of a file
tail'   = last

-- return the last ten lines of a file
tail10  = drop =<< subtract 10 . length

-- Reverse lines in a file (tac)
tac     = reverse

-- Reverse characters on each line (rev)
rev     = map reverse

-- Reverse words on each line
rev_w   = map (unwords . reverse . words)

-- Count number of characters in a file (like wc -c)
wc_c    = showln . length

-- Count number of lines in a file, like wc -l
wc_l    = showln . length . lines

-- Count number of words in a file (like wc -w)
wc_w    = showln . length . words

-- double space a file
space   = intersperse ""

-- undo double space
unspace = filter (not.null)

-- remove the first occurrence of the line "str"
remove  = delete

-- make a string all upper case
upper   = map toUpper

-- remove leading space from each line
clean   = map (dropWhile isSpace)

-- remove trailing whitespace
clean'  = map (reverse . dropWhile isSpace . reverse)

-- delete leading and trailing whitespace
clean'' = map (f . f)
    where f = reverse . dropWhile isSpace

-- insert blank space at beginning of each line
blank   = map (s ++)
     where s = replicate 8 ' '

-- join lines of a file
join = return . concat

-- Translate the letter 'e' to '*', like tr 'e' '*' (or y// in sed)
tr a b = interact (map f)
    where f c = if c == a then b else c

-- Delete characters from a string.
tr_d a = tr a ' '

-- grep lines matching "^foo" from a file
grep = filter (isPrefixOf "foo")

-- grep lines that don't match "^foo" (grep -v)
grep_v  = filter (not . isPrefixOf "foo")

-- number each line of a file
num  = zipWith (printf "%3d %s") [(1::Int)..]

-- Compute a simple cksum of a file
cksum   =  foldl' k 5381
   where k h c = h * 33 + ord c

Efficient wiki editing with w3m and vim

I find I spend quite some time editing wiki pages, such as the Haskell.org wiki. However, browser-based editing leaves a lot to be desired, generally making the editing process more inefficient and difficult than it needs to be. I suspect that these inefficiencies hamper contributions, especially in longer texts. The main problem with wiki editing is the browser-based editor forms aren’t proper editors, so you’re just wasting your time using them.

Key missing features are:

  • Syntax highlighting
  • Regular expressions substitutions
  • Spell checking
  • Programming tool support (particular for a PL wiki like Haskell.org)

We can fill these holes, and improve efficiency, by using the text-based browser w3m along with your favourite text editor, which for me is Vim. w3m is a simple, fast, terminal browser, which uses an external editor by default, for editing online forms.

Syntax highlighting for wiki files, with embedded Haskell, and Vim is available here. Save this to your ~/.vim/syntax directory, and enable it dynamically when editing online with :setf haskellwiki.

Using your favourite editor from w3m is as simple as setting: editor: vim -c "setf haskellwiki" in the w3m configuration screen (type o in w3m to bring this up). This also enables syntax highlighting by default, including Haskell fragments embedded in “<haskell>” tags.

And of course, vim being vim, you have access to unix tools via the shell (such as aspell), programming tools like lambdabot (pointfree refactoring of wiki code, anyone?) and more.

More details on text editor support for wiki editing, from a variety of browsers. Thanks to jgrimes for advice on this.

Finally, for the future, I’d like to investigate a generic mechanism for exporting wiki pages to a local revision control system, such as darcs. For large wiki subsections I work on, I currently save files locally in a new darcs repository, edit them, then upload the subsection in a single pass when complete. This is tedious — power users should always have a revision control back end to the wiki, for pushing and pulling changes directly. This is the way this blog is published: developed in a local darcs repository, and then pushed to a public repository, which blosxom then renders. Ideally, preparing wiki articles too should involve only the editor and a revision control system, for publishing.