Title: Common LISP awk macro for easy text file operations
       Author: Solène
       Date: 04 February 2020
       Tags: awk lisp
       Description: 
       
       I like Common LISP and I also like awk. Dealing with text files in
       Common LISP
       is often painful. So I wrote a small awk like common lisp macro, which
       helps a
       lot dealing with text files.
       
       Here is the implementation, I used the uiop package for split-string
       function,
       it comes with sbcl. But it's possible to write your own split-string or
       reused
       the infamous split-str function shared on the Internet.
       
       
           (defmacro awk(file separator &body code)
             "allow running code for each line of a text file,
              giving access to NF and NR variables, and also to
              fields list containing fields, and line containing $0"
               `(progn
                  (let ((stream (open ,file :if-does-not-exist nil)))
                    (when stream
                      (loop for line = (read-line stream nil)
                         counting t into NR
                         while line do
                           (let* ((fields (uiop:split-string line :separator
       ,separator))
                                  (NF (length fields)))
                             ,@code))))))
       
       
       It's interesting that the "do" in the loop could be replaced with a
       "collect",
       allowing to reuse awk output as a list into another function, a quick
       example I
       have in mind is this:
       
           ;; equivalent of awk '{ print NF }' file | sort | uniq
           ;; for counting how many differents fields long line we have
           (uniq (sort (awk "file" " " NF)))
       
       
       Now, here are a few examples of usage of this macro, I've written the
       original
       awk command in the comments in comparison:
       
           ;; numbering lines of a text file with NR
           ;; awk '{ print NR": "$0 }' file.txt
           ;;
           (awk "file.txt" " "
                (format t "~a: ~a~%" NR line))
       
       last field in the list)
           ;; awk -F ';' '{ print NF-1 }' file.csv
           ;;
           (awk "file.csv" ";"
                (print (nth (- NF 2) fields)))
       
           ;; awk '/unbound/ { print }' /var/log/messages
           ;;
           (awk "/var/log/messages" " "
                (when (search "unbound" line)
                  (print line)))
       
           ;; awk -F ';' '{ print $4 }' data.csv
           ;;
           (awk "data.csv" ";"
                (print (nth 4 fields)))