Run shell command on buffer or region

Emacs has tonnes of features built in, and is infinitely expandable with emacs lisp, but sometimes there is already a shell command that can do the job you want more easily. You can use M-! (wich runs shell-command) to run a shell command with the contents of the current buffer passed to stdin (standard input for the command), or M-| (which runs shell-command-on-region) to do the same for the currently selected text in the buffer.

The output of the shell command is then shown as a new buffer in emacs.

As a trivial example, suppose a buffer contained the following, and we wanted to sort the lines (N.B. this is easy to do in emacs)

bbb
aaa
ddd
ccc

Hitting M-! prompts for a shell command, and we then enter sort to call the standard unix/linux sort command. We now get a buffer called *Shell Command Output* containing

aaa
bbb
ccc
ddd

Since I am not the world’s greatest lisp programmer, I use this quite often for things that could probably be accomplished in emacs. For example, I wanted to pull out all of the xmlUrl addresses from a large file that looked like this

<opml version="1.0">
    <head>
        <title>Ben subscriptions in feedly Cloud</title>
    </head>
    <body>
        <outline text="daily" title="daily">
            <outline type="rss" text="Information Is Beautiful" title="Information Is Beautiful" xmlUrl="http://feeds.feedburner.com/InformationIsBeautiful" htmlUrl="http://www.informationisbeautiful.net"/>
            <outline type="rss" text="Planet Emacsen" title="Planet Emacsen" xmlUrl="http://planet.emacsen.org/atom.xml" htmlUrl="http://planet.emacsen.org/"/>
            <outline type="rss" text="What If?" title="What If?" xmlUrl="http://what-if.xkcd.com/feed.atom"/>
        </outline>
    </body>
</opml>

I couldn’t come up with a way to do this internally in emacs, but it is easy for me in perl, so I just ran M-! and then entered

perl -ne '/xmlUrl=(".+?")/; print "$1\n"' | uniq

to get a buffer containing just

"http://feeds.feedburner.com/InformationIsBeautiful"
"http://planet.emacsen.org/atom.xml"
"http://what-if.xkcd.com/feed.atom"

I’d be curious if anyone has a nice way to do this in emacs alone!

  • JohnKitchin

    You can do this all with emacs if you parse the xml.

    #+BEGIN_SRC emacs-lisp

    (let* ((s ”

    Ben subscriptions in feedly Cloud

    “)

    (xml (car (with-temp-buffer

    (insert s)

    (xml-parse-region (point-min) (point-max)))))

    (body (car (xml-get-children xml ‘body)))

    (outer-outline (car (xml-get-children body ‘outline)))

    (inner-outlines (xml-get-children outer-outline ‘outline)))

    (loop for io in inner-outlines collect (xml-get-attribute io ‘xmlUrl)))

    #+END_SRC

    • Ben Maughan

      Interesting, but has something gone wrong with the quotes here?

      • JohnKitchin

        I don’t think so. the quotes in the quotes are escaped in the string. You would not normally do it that way, but rather read the string directly from buffer or file.

  • I too don’t consider my elisp skills to be up to the job often and I use M-| a lot as well, but in your example parsing case I think I’d probably resort to using a macro to pull out the urls and then optionally run one more M-| (or M-!) to grep out just the lines I wanted.

    Also, helpful to note for other readers that a prefix to M-| (C-u M-|) has the affect of replacing the region with the results of the shell command. I often will filter content with a common grep command ala C-u M-| grep

  • hmelman

    I’d do it iteratively in emacs. I’d copy the text into a new buffer. Use replace-string to change xmlurl= to ^jxmlurl= and then use delete-non-matching-lines to remove all lines that don’t match ^xmlurl=. Then either macro to clean up each line or rectangle-kill to remove xmlurl=” from the beginning of each line and replace-string to remove the ending “.*$.