I'm a newbie to programming, so please bear with me here...
I have a directory full of files called "foo01.txt", "foo02.txt", etc. and a function called MyFunction. I want to open each file as a buffer, run MyFunction on it, write the buffer to its file, kill the buffer and move on to the next file in the series until all the files are done.
I think all the pieces I need to do this are described in the Cookbook (http://emacswiki.org/emacs/ElispCookbook) but I'm not really understanding how to put it all together. Thanks!
Answer
If you're looking for an answer in pure elisp, you could do something like this:
(defun process-file (f)
(save-excursion
(find-file f)
(my-function) ; Call your function here.
(write-file f)
(kill-buffer (current-buffer))))
(defun process-files (dir)
(mapc 'process-file
(directory-files dir t ".txt$")))
process-files will iterate over each file in a given directory and apply process-file to all .txt files. You can call it like so:
(process-files "~/target-directory")
You can copy this into a *scratch* buffer and play around with the individual parts. The most interesting functions are:
mapc - applies a function to each item in a list
directory-files - gets all files and folders in a directory, in this case retrieving all .txt files
find-file - opens a file in a buffer (this is what is run when you type C-x C-f)
Learning Lisp
If you're learning Lisp for its own sake, I can recommend Practical Common Lisp. You'll be able to work through a surprising amount of the book using Elisp. Otherwise, download a Common Lisp environment like SBCL.
The good in Emacs is that there are often many ways to solve a given problem, thanks to Emacs openness.
For instance, you could learn an easy trick in Emacs, that will help you now and in the future:
Here is a dired listing, eg from C-x f/home/me/mydir/
/home/me/mydir:
total used in directory 32 available 5575136
drwxr-xr-x 10 me brainers 340 Jan 18 15:50 .
drwxr-xr-x 78 me brainers 2652 Feb 2 18:08 ..
-rw-r--r-- 4 me brainers 136 Apr 1 2012 a.txt
-rw-r--r-- 16 me brainers 544 Feb 1 09:56 b.txt
-rw-r--r-- 6 me brainers 204 Apr 6 2012 c.txt
go to the first one (using up and down keys), ie a.txt, and do
C-x ( to start a macro recording
f to open that file
M-x myfunction to run that myfunction function
C-x C-s to save the file
C-x k to close that file, back to dired
down key to go to next file (b.txt in this case)
C-x ) to end the macro
then for each file (from b.txt), do
C-x e to execute the macro, it will do the same with b.txt, and then point to c.txt. (You could just do e to re-execute the macro if you don't do anything in between two macro executions)
Be careful not to run the macro on something that you don't want to be processed.
Notes:
if you make any mistake during the creation of the macro, Emacs will interrupt the recording process (thus, C-x ) will complain there is no macro being recorded). In this case the macro has to be started again from C-x (.
C-key is Control key, M-key is Meta key usually Alt-key. And C-x k means Control x then k key.
Related
Say I had opened vim with no arguments, then from within vim opened 10 .txt
files and 10 .py files. Also, let's say the files are scattered around my file
system. The buffer list contains all 20 files.
Now I want to add the 10 .py files to the arguments list. I want to do
something like :argadd *.py, but this just creates a new file called '*.py'
and adds it to the arguments list.
I see from the help that argd[elete] can use a pattern, so I could delete all
the .py files with :argd *.py. Is there a way to do something similar for
:arga[dd]?
:[count]arga[dd] {name} ..
Add the {name}s to the argument list.
:argd[elete] {pattern} ..
Delete files from the argument list that match the
{pattern}s.
Surely there's a better way than navigating to each .py file and running
:argadd?
You can go through all your buffers and add them to the argument list based on some discriminant with a single easy command:
:bufdo if &ft == 'python' | argadd | endif
or, if you really don't like typing:
:bufdo if&ft=='python'|arga|en
See :help :bufdo.
Well, it's sort of a "made up" question, I think. One rarely needs so many buffers/args at the same time that naive :argadd #10 #12 #15 #20 would be totally impractical.
But if you insist you need this then I'd suggest to try expression register, sort of
:argadd <C-R>=join(expand("*.py",1,1))<CR>
I have a collection of files, and I want to use vim macro to make some changes on them. In first file, I used "qa" to initiate the macro recording, and it showed "recording" in command line. Then I continued my sequence "gg" (go to beginning of the file) -->"/class" (search and jump to first class) --> do something. However, the "recording" disappeared when I started search and "reg a" only gave "a gg, nothing else...
Could anyone please tell me why macro stopped when I did a search? Is there a way search can be part of macro?
Thanks in advance?
I'm using VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Jan 2 2014 19:39:32) and it's working fine
What is the best way to take all the lines that begin with the word "define" and paste them at the end of a text file with vi?
For example, if I have:
define XXX
a
define YYY
b
and I want to get:
define XXX
a
define YYY
b
define XXX
define YYY
Solutions:
In vi, g/^define/t$ works nicely.
See below for vim solution.
You can use Go<ESC> (gee, owe, and the escape key) to open up a line at the end of the file, and then enter the characters:
!!grep '^define' %<ENTER>
That replaces your current line (the one you just opened at the end of the file) with the output of that grep command (where % is the current file).
You need to ensure the file is saved first since it uses the on-disk copy, not the in-memory copy and keep in mind this is with Vim - older variants of Vi may not have this facility.
You can set a list of directories for vim to use for the swapfile (with it defaulting to the first one it can find) with:
set directory=~/tmp,~/var/tmp,/var/tmp,/tmp
I want to know which directory vim has chosen for its swapfile so I can stick some temp data in there.
Parsing &directory and looping through the values seems like a waste when vim should have already figured that out.
The :swapname command will tell you which swapfile is used for the current buffer. You need to use :redir => varname to capture the output and store it in a variable.
I think the "used" directory is not saved somewhere by vim, like a variable or something. It could be different from buffer to buffer. (from file to file).
e.g.
you have a non-root user. say kent, his directory setting is:
set directory=.,~/tmp,/var/tmp,/tmp
now kent opens /etc/host.conf in his vim, and editing. the first .(dot) means current dir. obviously kent cannot write file to /etc, so take the 2nd ~/tmp, but kent doesn't have tmp under his home, so next, /var/tmp is used.
if kent opens a file under his HOME, or under /tmp, for example, the .(dot) would be used. And note that the dot for different files are different.
kent can open many files in buffers, so it is hard to say, which dir would be used for swap files.
so the swapfile - dir is not fixed to a directory. you should check it based on the buffer/file.
If you do this often, I suggest to write a function:
function! SwapDirectoryForCurrentFile()
redir => filename
silent! swapname
redir end
return fnamemodify(filename, ":h")
endfunction
I have the requirement of separating an ASCII document into pages of max length 58 lines per page. At the bottom of each page there is a 3 line footer. I'm not aware of any pagination abilities within Vim that would accomplish this.
Is there a good way to do this with Vim? Perhaps highlighting every 58th line or something of the sort.
N.B. I'm seeing answers involving using a separate tool to do this; which I have thought of. What I'm interested in is a Vim solution.
Thanks.
The proper tool you're looking for is very likely a2ps.
a2ps --lines-per-page 58 --footer=footer_text document.txt
It's possible in vim as a script. Put the following in a file and :source it while the file to change is open. The s:footer list are the lines to insert after each run of 58 lines.
let s:footer = ["","Footer",""]
let s:line = 0
while s:line <= line("$") - 58
let s:line = s:line + 58
call append(s:line, s:footer)
let s:line = s:line + len(s:footer)
endwhile
Why is it important to use vim? You could use split and cat more efficiently.
Assuming your original file is called file and you have a file, footer, created that includes your footer text.
$ split -l 58 file file_parts
$ for i in file_parts*; do cat $i footer > $i.footered; done
$ cat file_parts*.footered > file.footered
file.footered would have your original file with the contents of footer inserted at every 58th line.
This is assuming you want it all back in the original file. If you don't, then the resulting file_parts*.footered files would be the already separated pages so you could skip the last step.
The two most effective ways for doing that in Vim are a script (like
#Geoff has already suggested) and a substitution command, like
:%s#\%(.*\n\)\{58}#\0---\rfooter\r---\r#
A macro (as suggested in a comment to the question) is the slowest,
a script is the fastest. A substitution command is slower than script,
but much faster than a macro.
So probably substitution is the best Vim-only solution unless its
performance is unacceptable. Only in that case, I think, it is worth
writing a script.
You're probably trying to use the wrong tool for this. You could do it much easier programmatically, for example with this simple Perl oneliner:
perl -pe'print "your\nfooter\nhere\n" unless $. % 58' inputfilename > outputfilename
A recursive macro might work. Experiment with the following (position the cursor on the first character of the first line and switch to normal mode):
qqq
qq
57j
:read footer.txt
3j
#q
q
Note that the register to which you record the macro must be cleared (qqq) and that you must not use tab-completion when reading the footer-file (:read footer.txt).
You can then use the macro (normal mode):
#q