8

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 ?

2
  • 2
    You can’t, since the pipe doesn’t save the output. Would you be ok with temporary files?
    – Jeff Schaller
    Commented Feb 13, 2018 at 16:37
  • @JeffSchaller I am ok with temporary files, I will most probably accept the answer from ikkachu.
    – Tsathoggua
    Commented Feb 14, 2018 at 8:19

3 Answers 3

6

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.

5
  • Since all files are written to $dir you could possibly just less "$dir"/* and drop the use of $outfiles completely.
    – Kusalananda
    Commented Feb 13, 2018 at 16:58
  • @Kusalananda That would mean, though, that you possibly less the output out of order, which may or may not be desired.
    – Ned64
    Commented Feb 13, 2018 at 22:26
  • @Ned64 Hmm... With the output filename set the way he does it here, it will depend on what "whatever" in the code does.
    – Kusalananda
    Commented Feb 13, 2018 at 22:30
  • It works if I do outfiles+=("$dir/$outfile"). I didn't knew about mktemp, interresting command.
    – Tsathoggua
    Commented Feb 14, 2018 at 7:59
  • @Tsathoggua, d'OH! Yes, of course the output directory should be there. Thanks for the note.
    – ilkkachu
    Commented Feb 14, 2018 at 9:00
5

If you want to move with :n and :p there is no other way but to run the commands, output the outputs to files, then less them:

svndiff () 
{
    d=$(mktemp -d /tmp/svndiffsXXXXX)
    for a in $(svn status | \grep ^M | sed 's/M       //');
    do
        svn diff "$a" > $(mktemp $d/diffXXXXX) 2>&1;
    done
    less "$d"/diff*
    rm -fr "$d"
}

(If you need them in order let me know and we can apply numbering.)

Otherwise, you could call a shell executing all of your commands, then pipe the concatenated output to less.

4
  • There's no need to override the system's $TMPDIR by forcing the the path and name of the temporary directory.
    – Kusalananda
    Commented Feb 14, 2018 at 9:04
  • 1
    @Kusalananda, unless they want to give the directory a meaningful name... On a quick test, the mktemp versions from GNU coreutils and e.g. from FreeBSD work differently with regard to -t, so something like mktemp -d "${TMPDIR:-/tmp}/svndiffs.XXXXX" would be needed to use TMPDIR and work on both. yngh.
    – ilkkachu
    Commented Feb 14, 2018 at 9:15
  • @Kusalananda The existence of /tmp and our right to use it is guaranteed by the FSH Standard so no need to make things overly complicated, especially as we clean up after ourselves.
    – Ned64
    Commented Feb 14, 2018 at 9:38
  • @Ned64 Perfectly reasonable. I was just thinking on about systems similar to our local cluster where we definitely want people to use $TMPDIR for temporary storage. But that's my local situation and possibly not relevant here.
    – Kusalananda
    Commented Feb 14, 2018 at 9:45
2

Using GNU Parallel you could do something like:

files=$(svn status | \grep ^M | sed 's/M       //' |
  parallel --files svn diff {})
less $files
rm $files

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .