Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Zsh functionality similar to Perl 'map'?

"Benjamin R. Haskell" <zsh@xxxxxxxxxx> writes:

> I frequently find myself diff'ing files that have minor differences I'd 
> like to filter out.  E.g. this gem from my historyfile:
> diff -ur =(LD_LIBRARY_PATH=/opt/chromium.org/lib ldd /opt/chromium.org/chrome-linux/chrome | perl -lpwe 's/\(.*\)//' | sort | uniq) =(ldd /opt/chromium.org/chrome-linux/chrome | perl -lpwe 's/\(.*\)//' | sort | uniq)
> (That is: find the differences in the loaded libraries for the Linux 
> version of Chromium, depending on whether LD_LIBRARY_PATH contains 
> /opt/chromium.org/lib)
> These diffs follow the general pattern:
> diff -ur =(do-something-1 | filter) =(do-something-2 | filter)
> My question is: is there a nice way to 'map' this, so I don't have to 
> type/edit the '| filter' portion twice?  The filter portion often changes, 
> and is usually incrementally developed.
> (E.g. in the Chromium case, I have the consecutive commands:
> diff -ur =(LD_LIBRARY_PATH=/opt/chromium.org/lib ldd /opt/chromium.org/chrome-linux/chrome) =(ldd /opt/chromium.org/chrome-linux/chrome)
> diff -ur =(LD_LIBRARY_PATH=/opt/chromium.org/lib ldd /opt/chromium.org/chrome-linux/chrome | perl -lpwe 's/\(.*\)//') =(ldd /opt/chromium.org/chrome-linux/chrome | perl -lpwe 's/\(.*\)//')
> diff -ur =(LD_LIBRARY_PATH=/opt/chromium.org/lib ldd /opt/chromium.org/chrome-linux/chrome | perl -lpwe 's/\(.*\)//' | sort | uniq) =(ldd /opt/chromium.org/chrome-linux/chrome | perl -lpwe 's/\(.*\)//' | sort | uniq)
> )
> I suspect most people's preferred solutions will involve functions... 
> something along the lines of:
> function filter () { perl -lpwe 's/\(.*\)//' | sort | uniq } ; diff -ur =(blah-1 | filter) =(blah-2 | filter)
> But I was wondering if there was another way that didn't disrupt the flow 
> as much.  (I dislike that the filter function ends up getting 'fronted', 
> to use the linguistic term.  It's kind of like saying, "A butter knife, he 
> spread the jam with it and the jelly with it," when I'd rather say, "He 
> spread the jam and the jelly with a butter knife," or even "He spread 
> with a butter knife the jam and the jelly.")
> Plus, this isn't the best or most-general example.  Often the '| filter' 
> part is actually run on two different files, rather than piped-in.  (So:
> diff -ur =(filter file1) =(filter file2)
> )

You could always do this:

filterdiff () {
    emulate -L zsh
    local i a
    local -a opts
    local -a args
    while [[ $# > 0 ]]
        case $a in
            (--)  args=($args $*) 
            (-?*) opts=($opts $a)
            (*)   args=($args $a)
    if (( $#args != 3 ))
        print "usage: $0 [options] <filter> <file1> <file2>" >&2
        return 1
    diff $opts -- <( eval $args[1] < $args[2] ) <( eval $args[1] < $args[3] )

Use it like this:

  filterdiff 'tr a-z A-Z | tac' /etc/passwd /etc/hosts

You have to quote the filter of course...
> Ideally, it'd end up as something like:
> diff -ur =({do-something1,do-something2} | filter)
> or
> diff -ur =(filter {file1,file2})
> ...and, yes, I know that's a *huge* stretch.  And, maybe my Perl bias for 
> map'ping everything is showing, but still, figured I'd ask.

I'm afraid this fancy syntax won't be possible.  Well, you could by
hacking the completion system, but that would be unmaintainable 


Messages sorted by: Reverse Date, Date, Thread, Author