Bash – pipe multiple commands to less

bashlesspipe

How can I do

{$several_commands} | less

and have less considere it as several files and enable navigation using :n and :p.

That may not be the clearer explanation, so let us consider an example. I currently have a function

svndiff () 
{ 
    for a in `svn status | \grep ^M | sed 's/M       //'`;
    do
        svn diff $a | less;
    done
}

The purpose obviously is to see with less the difference of all my modified files. But with this syntax, I have to use key Q to close one "file" and open the next one. I would like to be able to navigate between files with the less commands :n (next file) and :p (previous file). How can I do that ?

Best Answer

You could use process substitution:

less -f <(svn diff this) <(svn diff that)

But that's hard to use in a loop. Probably best to just use temporary files:

#!/bin/bash
dir=$(mktemp -d)
outfiles=()
IFS=$'\n'
set -f 
for file in $(svn status | \grep ^M | sed 's/M       //') ; do
    outfile=${file#.}             # remove leading dot (if any)
    outfile=${outfile//\//__}     # replace slashes (if any) with __
    svn diff "$file" > "$dir/$outfile";
    outfiles+=("$dir/$outfile")   # collect the filenames to an array
done
less "${outfiles[@]}"
rm -r "$dir"

The above tries to keep the filenames visible in the names of the temp files, with some cleanup for slashes and leading dots. (In case you get paths like ./foo/bar. I can't remember how svn outputs the file names, but anyway...)

The array is there to keep the order, though as @Kusalananda said, we could just do "$dir"/* instead, if the order doesn't matter. set -f and IFS=$'\n' in case someone creates file names with glob characters or white space.

Of course we could simplify the script a bit and create, say numbered temp files instead.

Related Question