Hacker News new | ask | show | jobs
by __david__ 4774 days ago
I think you want s///g on your second sed... Also that doesn't work in Mac OS X for some reason (their sed doesn't appear to interpret \n in the replacement text). I replaced it with perl to make that part work:

    perl -pe 's/ *\| */\n/g'
I still haven't gotten the whole thing to work yet because my history contains the above history pipeline and so it's splitting the "|" that inside the sed command onto multiple lines which is causing "xargs which" to balk because quotes are not matching or something:

    xargs: unterminated quote
Shells are amazing until spaces or quotes are involved! :-)
1 comments

Yes, I've inserted the "g" in the appropriate sed commands. Thanks - good catch.

Some systems seem to require \r instead of \n - I know vim's behavior differs from sed's in this, so that might be an issue.

With xargs balking, you can throw the error stream at that point, for convert it to a loop over the alleged commands:

    for c in $( long thing before the xargs )
    do
        which $c
    done \
    | long thing after the xargs.
Specifically:

    for f in $( \
        history                      \
            | sed "s/^[0-9 ]*//"     \
            | sed "s/ *| */\n/g"     \
            | awk '{print $1}'       \
        )
    do
        which $f 2> /dev/null
    done                         \
        | sed "s.^/usr.."        \
        | grep ^.bin             \
        | sed "s/^.*\///"        \
        | sort                   \
        | uniq -c                \
        | sort -rn               \
        > commands.txt
And yes spaces are a pig, and can lead to all sorts of ambiguities that don't have reasonable ways of resolving them, especially in filenames.
"\r" doesn't work either. It's just not interpreting those kind of escapes. I get lines like this:

    historyrsed "s/^[0-9 ]*//"rsed 's/ *r*/\r/g'rless
The for loop is a good idea, though I prefer the "while read" idiom since it fits in with the pipeline better:

    ...
    | awk '{print $1}'                      \
    | while read line; do which $line; done \
    ...
That finally works for me. Here's the whole thing:

    history                                     \
        | sed "s/^[0-9 ]*//"                    \
        | perl -pe 's/ *\| */\n/g'              \
        | awk '{print $1}'                      \
        | while read line; do which $line; done \
        | sed "s.^/usr.."                       \
        | grep ^.bin                            \
        | sed "s/^.*\///"                       \
        | sort                                  \
        | uniq -c                               \
        | sort -rn
This works for me in zsh on OSX

history \ | sed "s/^[0-9 ]//" \ | perl -pe 's/ \| */\n/g' \ | gawk '{counts[$1] += 1} END { for (x in counts) { print counts[x],x}}' OFS="\t" \ | column -t \ | sort -k 1,1nr \ | head -100

Ok not sure how to format this on HN https://gist.github.com/nyxwulf/5608955#file-gistfile1-sh

From what I recall to represent s newline in sed on osx you need to add an actual carriage return.

Take the poster's script above and copy it to a text file. Now replace \n with a new line, save and it should run.