user4495048
user4495048

Reputation: 60

Bash alias to pipe an arbitrary command to less

In a nutshell I'm trying to make Perforce work with less. At the moment a command such as p4 diff will happily dump 5000 lines to the console. I want commands like this to automatically pipe into less without me having to type it (you know, like g*t does).

My two best "solutions" so far:

"Solution" 1 - A bash function

function p4() {
    pager=""
    if [ "$1" = "diff" ]
    then
        pager=" | less"
    fi

    /bin/sh -c "/usr/local/bin/p4 "$@" $pager"
}

Problem with this is Perforce syntax overlaps Unix shell syntax. i.e. p4 jobs ...@\>2015/01/20 will fail horribly because of the > symbol.

"Solution" 2 - Function with a different name

function p4d() {
    p4 diff "$@" | less
}

Which is lame because now I have to remember to type p4d instead of p4 diff and won't work for p4 diff2 for the same reason as solution 1.

Are there any shell or Perforce tricks I can use to get commands like p4 diff to pipe into less without me having to remember to type it all the time?

Is there a way I can safely wrap the p4 arguments ($@) so that shell symbols like > are not reinterpreted but | is?

Does not have to be bash. If you have some funky shell from the 60s which solves my problem let's hear it!

Thank you for your help.

Upvotes: 3

Views: 1728

Answers (2)

rici
rici

Reputation: 241861

Eval-type solutions generally fail to be robust against arguments with odd characters in them. On the other hand, they are often unnecessary.

Instead of

/bin/sh -c "/usr/local/bin/p4 "$@" $pager"

which has a quotation error (the quotes around $@ are actually unquoting $@ although fixing that won't help since you would actually need to insert escapes into the string passed to bash -c), you can just execute the command directly:

/usr/local/bin/p4 "$@" | "$pager"

That requires that $pager be defined; you would probably want:

if [[ $pager ]]; then
  /usr/local/bin/p4 "$@" | "$pager"
else
  /usr/local/bin/p4 "$@"
fi

The explicit path is a bit annoying. With bash, you can make this more robust by using the command builtin:

if [[ $pager ]]; then
  command p4 "$@" | "$pager"
else
  command p4 "$@"
fi

That introduces a small duplication of code, and it's a bit much if you're going to use that snipped in various places. You could define the following function (in your ~/.bashrc file, for example):

page() {
  if [[ $pager ]]; then
    "$@" | "${pager[@]}"
  else
    "$@"
  fi
}

Then your p4 script could be:

p4() {
  local pager=
  if [[ $1 = diff ]]; then pager=less; fi
  page command p4 "$@"
}

There are lots of other variants. Note the use of "${pager[@]}" in the page function; this allows pager to be an array, in case you want to pass arguments to less. For example:

( pager=(less -N); p4 diff ...; )

(The parentheses are to make the setting of pager local; you can't use the normal var=value command syntax with arrays.)

Upvotes: 4

John Kugelman
John Kugelman

Reputation: 361889

My suggestion for how to implement your p4 function:

function p4() {
    if [[ $1 == diff ]]; then
        command p4 "$@" | less
    else
        command p4 "$@"
    fi
}

This will eliminate the quoting and escaping problems you're seeing.

You could eliminate the repeated command p4 "$@" pieces if piping to cat is okay (some commands will behave differently when stdout is connected to a TTY vs. a pipe; I don't know if perforce cares):

function p4() {
    command p4 "$@" | if [[ $1 == diff ]]; then less; else cat; fi
}

Upvotes: 3

Related Questions