While, as others have pointed out, other tools might be more suitable for this job, it can certainly be done with Emacs. 1.9GB is not an unreasonably large file size for Emacs to handle on a somewhat modern computer.
Using Emacs Lisp
There is a built-in command copy-matching-lines
which takes you some of the way, but I believe the below custom command would be faster for your purposes:
(defun my-copy-matching-lines (small-infile big-infile outfile)
(interactive
(list (read-file-name "Small file containing list of items to extract:\n")
(read-file-name "Large text file:\n")
(read-file-name "Output file:\n")))
(save-excursion
(find-file small-infile)
(let ((match-strings (split-string
(string-trim
(buffer-substring-no-properties
(point-min) (point-max)))
"\n")))
(while match-strings
(let (line)
(find-file big-infile)
(goto-char (point-min))
(when (search-forward
(concat "\t" (pop match-strings) "\t")
nil t)
(setq line (buffer-substring-no-properties
(line-beginning-position)
(line-end-position)))
(find-file outfile)
(insert line "\n")))))
(find-file outfile)
(save-buffer)))
(call-interactively #'my-copy-matching-lines)
To use this, copy the code above into a file with an .el
extension, like mylisp.el
and open it in Emacs. You should now have a menu bar item called "Emacs-Lisp". Click it to reaveal a sub-menu with an entry called "Evaluate Buffer", which you should click. You will be asked for a file path/name three times:
- The first time, navigate to the presumably smaller file containing the list of items to extract. On my system I now have to, confusingly, press save and confirm that I want to replace the file (it still works as expected otherwise).
- The second time, navigate to the large 1.9GB file, and
- the third time input a new file to save the output. (You might additionally have to confirm that you really want to open the 1.9GB large file.)
The code I shared makes some assumptions and might have to be adapted to fit your exact use case:
- It assumes that the list of items to extract contains one item per line, with "\n" as linebreak character, and should exactly match field two. A partial match would not return anything, since the field separators, which are assumed to be tabs
"\t"
, are included in the search string. You might want to remove the "\t"
's above to allow partial matches, or replace them with spaces " "
if the file doesn't contain actual tabs.
- It only returns the line of the first match per item, so if there are more matches these won't appear in the output file.
Using keyboard macros
An alternative, if you want to avoid writing Emacs Lisp, is to record a keyboard macro. Type M-x kmacro-start-macro-or-insert-counter
or <f3>
to start recording a macro and M-x kmacro-end-or-call-macro
or <f4>
to stop. M
stands for the meta key (and C
for control).
Doing so will record a macro in the "Emacs command language". The macro given below will do almost the same thing as the Lisp code above (it doesn't loop), assuming you have the three files as described above open in Emacs, with names "insmall.tsv", "inbig.tsv", and "outfile.tsv", and that you are visiting the "insmall.tsv" file when calling the macro:
C-SPC ;; set-mark-command
C-e ;; move-end-of-line
s-c ;; ns-copy-including-secondary
C-a ;; move-beginning-of-line
C-x b ;; switch-to-buffer
inbig.tsv ;; self-insert-command * 9
RET ;; newline
M-< ;; beginning-of-buffer
<<copy-matching-lines>> ;; copy-matching-lines
TAB ;; indent-for-tab-command
s-v ;; yank
TAB ;; indent-for-tab-command
RET ;; newline
C-x b ;; switch-to-buffer
outfile.tsv ;; self-insert-command * 11
RET ;; newline
s-v ;; yank
C-x b ;; switch-to-buffer
insmall.tsv ;; self-insert-command * 11
RET ;; newline
C-n ;; next-line
To use this you can record a macro replicating the above yourself (note that <<copy-matching-lines>>
stands for M-x copy-matching-lines
). Alternatively you can just record a shorter non-empty macro (so that there is something to edit), then call M-x kmacro-edit-macro
or C-x C-k RET
, paste the above macro, and press C-c C-c to finish. You can now call your macro N
times by typing C-u N M-x kmacro-end-or-call-macro
or C-u N <f4>
when your cursor is on the line at which you want to start in "insmall.tsv".
awk
orperl
to me...