| 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))) | |