5

Occasionally I use AWK to extract and/or reverse columns in a data file.

awk '{print $2,",",$1}' filename.txt

How would I do the same using Emacs Lisp?

(defun awk (filename col1 &optional col2 col3 col4 col5)
  "Given a filename and at least once column, print out the column(s)
values in the order in which the columns are specified."
...
)
;; Test awk
(awk "filename.txt" 1); Only column 1
(awk "filename.txt" 2 1); Column 2 followed by column 1
(awk "filename.txt" 3 2 1); Columns 3,2 then 1

Sample filename.txt:

a   b  c
1   2  5

Sample output:

b , a
2 , 1
Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166

3 Answers3

2

How do you intend to use this? Are you planning on using it as a command-line script? In which case, you'll need to package it like this hello world question.

Or, are you planning on using it interactively, in which case you probably want the output in a new buffer...

This code gets the basics done. You'll need to update it to match your usage model.

(defun awk (filename &rest cols)
  "Given a filename and at least once column, print out the column(s) values
in the order in which the columns are specified."
  (let* ((buf (find-file-noselect filename)))
    (with-current-buffer buf
      (while (< (point) (point-max))
        (let ((things (split-string (buffer-substring (line-beginning-position) (line-end-position))))
              (c cols)
              comma)
          (while c
            (if comma
                (print ", "))
            (print (nth (1- (car c)) things))
            (setq comma t)
            (setq c (cdr c)))
          (print "\n")
          (forward-line))))
    (kill-buffer buf)))
Community
  • 1
  • 1
Trey Jackson
  • 73,529
  • 11
  • 197
  • 229
  • I've heard that `with-current-buffer` should be used instead of `save-excursion` and `set-buffer`. – pheaver Feb 14 '10 at 11:01
  • Why are you printing the newline after each column? Surely you mean to print it after printing all the selected columns from a line? – A. Levy Oct 24 '10 at 04:25
0

I took Trey's solution and produced a script that runs from a Unix shell. It doesn't take command line parameters because I wasn't sure how to convert the command-line-args-left results into a proper parameter.


#!/usr/bin/emacs --script

;; ./awk.el; # Change the last line of this file to contain the desired values.
;;
(defun awk (filename &rest cols)
  "Given a filename and at least once column, print out the column(s) values
in the order in which the columns are specified."
  (let* ((buf (find-file-noselect filename)))
    (with-current-buffer buf
      (while (&lt (point) (point-max))
        (let ((things (split-string (buffer-substring (line-beginning-position) 
                          (line-end-position))))
              (c cols)
              comma)
          (while c
            (if comma
                (princ ", "))
            (princ (nth (1- (car c)) things))
            (setq comma t)
            (setq c (cdr c)))
            (princ "\n")
          (forward-line))))
    (kill-buffer buf)))

(awk "/tmp/foo.txt" 2 1)

0

Use functions from dash.el and s.el:

(defun print-columns (s &rest is)
  (s-join "\n"
          (--map (s-join ", "
                         (-select-by-indices is (cons it (s-split " " it t))))
                 (s-lines s))))

(print-columns "a  b c\n1  2 3" 3 2 1 0) ; output:
;; c, b, a, a  b c
;; 3, 2, 1, 1  2 3

By default, awk treats text as a sequence of records (separated by newline), each record being a sequence of fields (separated by space). So in the above example c is a field of the record a b c. The function print-columns receives a text, separates by newlines with s-lines, selects certain fields from each record, joins them with a comma with s-join, joins the result with newlines. The most important function is dash's -select-by-indices, which picks elements from a list by their indexes and returns in the same order as the list of indices:

(-select-by-indices '(2 1 0) '(a b c d e)) ; => (c b a)
Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166