help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Emacs equivalent of the ":g" command in vi


From: MBR
Subject: Re: Emacs equivalent of the ":g" command in vi
Date: Fri, 22 Jul 2011 16:27:44 -0400
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.17) Gecko/20110414 Thunderbird/3.1.10

On 7/22/2011 4:42 AM, C K Kashyap wrote:
Hi,
Could someone please tell me how I could go about something like this -
I need to perform a certain action (such as delete the line) on each line of a buffer if the line matches a regular _expression_. In vim, we can use the :g command for this.
Regards,
Kashyap

I looked for the same thing ages ago when I switched from vi to emacs.  Eventually I figured out that:
:g/regular _expression_/operation
reflects an 'ed' mindset, and that an Emacs macro with a repeat count is actually far more powerful .  (In case you're wondering what 'ed' is, it was the original, line-oriented, Unix editor.  'vi' was Bill Joy's visual mode version of 'ex', which was his enhanced version of 'ed'.)

An Emacs macro is a series of emacs commands that you can replay.  You use C-( and C-) as follows to create a macro:
C-(
type any commands you want Emacs to remember
C-)
Then, whenever you type C-x e, Emacs will replay the commands.

At this point, you're probably wondering how this can substitute for vi's g//.  Simple.  Just start your macro off with a regular _expression_ search, do whatever you want, and then replay it multiple times with:
C-u count C-x e
Specify a large enough repeat count, and you can make your macro apply to the whole file.

For example, to delete each line of a buffer if the line matches a regular _expression_, you'd define the macro with:
C-(            ;; Begin recording macro
C-M-s regexp    ;; Search for regular _expression_
C-a            ;; Go to beginning of line

C-k             ;; Kill one line by typing C-k twice
C-k            ;;
C-)            ;; End recording macro
Then you'd execute the macro with:
C-u 10000 C-x e
Of course, there's an easier way to delete lines that match a regular _expression_:
M-x delete-matching-lines
But you described the general problem as needing to perform a certain action on each line of a buffer if the line matches a regular _expression_.  And the approach of defining a macro to do what you want and then executing it with a large repeat count gives you a general purpose mechanism to do arbitrary operations rather than just delete the line.

For example, if I have a file of lines of the format:
zip,street address,city,state,phone,name
That I wanted to rearrange to:
name,phone,street address,city,state,zip
I could run:
M-x replace-regexp
^\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)$
\6,\5,\2,\3,\4,\1
which is the equivalent of vi's:
:g/^\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)$/s//\6,\5,\2,\3,\4,\1/g
But making sure you've got that regular _expression_ right can be tricky.  It can be much easier to just do it with emacs commands applied to a single line.  Although the following is difficult to read, I think if you try it out you'll find it pretty straightforward.  First fill an emacs buffer with lots of lines of the form:
name,phone,street address,city,state,zip
Then type these emacs keystrokes (omitting the comments):
;; Begin recording macro
C-(
;; Narrow the buffer to the current line:
C-SPC C-n C-x n n M-<
;; Move the part after the fifth ","
;; to the beginning of the line
C-s , C-s C-s C-s C-s C-b C-k C-a C-y , C-a C-d
;; Move the part that's now after the fifth ","
;; to the second comma-delimited position
C-s , 4*C-s C-b C-k C-a M-f C-y
;; Move the third comma-delimited part to the end
M-d C-e C-y
;; Move forward over the newline to the next line
C-f
;; Widen so you can see the whole buffer
C-x n w
;; End recording macro
C-)
Replay it 10,000 times with:
C-u 10000 C-x e
It will stop as soon as it runs out of matching lines.

The point is that the operations you can repeat this way are limited only by your imagination.

There's a variant of this that I use frequently.  I often find that a non-emacs application wants me to type lots of information into individual fields of an input screen.  It's a pain to have to type all that data, especially when I have that data in a text file.  In emacs I can organize the data into the same order as the input fields in the application, putting each field's data on a separate line of the emacs buffer.  Then I do the following in emacs:
;; Begin recording macro
C-(
;; Mark region from beginning to end of line
C-a
C-SPC C-e
;; Copy region so it can be pasted into another application
C-w
;; Move forward over the newline to the next line
C-f
;; End recording macro
C-)
Once I've defined that macro, I can repeatedly type:
;; This is not an Emacs command.  It tells the window manager
;; to give keyboard focus to the other application.
ALT-TAB
;; Paste into the input field
C-v
;; Move focus to the next input field
TAB
;; Give keyboard focus to Emacs.
ALT-TAB
;; Repeat the macro, which copies the next line.
C-x e

At that point, I repeatedly type:
C-x e ALT-TAB C-v TAB ALT-TAB
While it's not fully automated, If I've got lots of data that has to be entered through a GUI interface, it makes things go a whole lot faster.

If you should want to save a macro you've created this way so you can use it in future Emacs sessions:
Open your .emacs file: C-x C-f ~/.emacs
Give it a name by with: M-x name-last-kbd-macro
Insert it into your .emacs with: M-x insert-kbd-macro
Save your .emacs file: C-x C-s
Mark Rosenthal
address@hidden
P.S. - Interesting side-note.  Did you know that the ed command:

    g/regular _expression_/operation

is where the name "grep" came from?  In ed and ex, the "g" means do a global search for the immediately following regular _expression_, and apply the operation to every matching line.  One such operation is "p" meaning "print".  Using "re" as shorthand for "regular _expression_", the ed command to print every line that matches a particular regular _expression_ is:

                g/re/p



reply via email to

[Prev in Thread] Current Thread [Next in Thread]