5

On a cluster where I am part of the management team, I often have to go through the multipage standard output of various commands such as sudo find / to look for any troubles such as broken links or to check the directory trees. At other times, I need to review long text files with lists of items on them to see if there are any unusual names.

Normally, piping the output through less, I can scroll pagewise but I figure it would be sufficient if the standard output scrolled a little slowly just like the credit roll at the end of a movie.

Is there a way to accomplish this in bash or any other terminal environment?

Ketan Maheshwari
  • 9,054
  • 6
  • 40
  • 53
  • http://unix.stackexchange.com/questions/283721/display-file-text-one-page-at-a-time-wait-20-seconds-then-auto-advance – thrig Jun 16 '16 at 16:08
  • 7
    IMO `less` is far preferable to the hacky solutions provided by the answers, It's easier to type, should be available on every distribution, and you'll need a pager anyway whenever you suddendly decide that you want to scroll or search. – Martin von Wittich Jun 16 '16 at 16:55
  • When I need more than one look at the results, `sudo find / > output.txt` saves the output to a file, which can then be read in any text editor. – SauceCode Jun 17 '16 at 00:23

5 Answers5

12

pv, the pipe viewer, will let you print one line every second, use it like:

cat foo | pv --quiet --line-mode --rate-limit 1 

(or, shorter, pv -qlL1). In --line-mode, the --rate-limit (-L) flag defines the number of lines per second that will be printed; the higher the number, the faster the output.

It should be available in your distro's repositories (e.g. aptitude install pv to get it).

  • I didn't find a way to let the first several lines come through fast, and only rate limit after that. Of course, I couldn't imagine ever using this instead of `less`, and I think the OP is insane for wanting anything like this in the first place. – Peter Cordes Jun 16 '16 at 20:46
9

Answer from thrig's comment on OP. Works very well. Change the decimal after sleep to modify the time between lines.

sudo find / | awk '{system("sleep .5");print}'

Quit with ctrl+z and then kill the job (when using bash); ctrl+c only exits that line.

Edit:

Did some research based on a comment below. The suggestion awk '{system(sleep.5)||exit;print}' wasn't working on my system, but the following does seem to allow a ctrl+c exit.

awk '{if (system("sleep .5 && exit 2") != 2) exit; print}'

Putting it in a script or giving it an alias will save you from carpal tunnel.

Landon Bland
  • 146
  • 3
4

As promised in a previous answer, a simple C program. Why not? Boring day.

slower.c

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(int argc, char** argv) {
    int delay;
    char* rem;
    if (argc > 1) {
        delay = strtol(argv[1], &rem, 10);
    } else {
        delay = 500;
    }

    char* line;
    size_t bufsize = 0;

    struct timespec ts;
    ts.tv_sec = delay / 1000;
    ts.tv_nsec = (delay % 1000) * 1000000;

    while (getline(&line, &bufsize, stdin) != -1) {
        printf("%s", line);
        nanosleep(&ts, NULL);
    }
    free(line);
}

Compile with a nice gcc slower.c -o slower. If used with no arguments, the milliseconds per line defaults to 500.

Usage: sudo find / | ./slower [MILLISECONDS PER LINE]

Edit: Fixed up some of the code thanks to input in the comments. Apparently, getline doesn't even need your filthy malloc, or any default values. Let getline allocate the crap out of itself.

Edit 2: Removed the improper while (!feof(stdin)) usage and added back in the bufsize initialization for compatibility.

Landon Bland
  • 146
  • 3
  • *Why not? Boring day.* You torture yourself when you're bored? D: – cat Jun 16 '16 at 21:26
  • 2
    Also, not that this is [codereview.se] but I implore you to A) [not cast the result of malloc](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) and B) [not use atoi](http://stackoverflow.com/questions/17710018/why-shouldnt-i-use-atoi) in place of [better alternatives](http://stackoverflow.com/a/14176593/4532996) which should always be used instead of atoi – cat Jun 16 '16 at 21:28
  • 1
    32 seems awfully short to hardcode the max line length. Also, if you're hardcoding, then why not just statically allocate the line buffer as a char array instead of messing with malloc(). – Digital Trauma Jun 16 '16 at 21:32
  • 1
    I always appreciate a review of my code; I'm merely a student, and know next to nothing about best practices. I'll fix those shortly. – Landon Bland Jun 16 '16 at 21:33
  • Also, I know it's a tiny utility but you never `free(line)`... – cat Jun 16 '16 at 21:33
  • 1
    There's no reason to `malloc` anyways, a `char* line[32]; getline(...);` will work fine. – cat Jun 16 '16 at 21:34
  • @DigitalTrauma Perhaps I misunderstood, but it seemed to me that 32 was merely the length of each section the code is pulling from stdin. The variable name "line" is perhaps a bit misleading. I may be wrong on this, but I thought I tested this on longer outputs and it worked all right. – Landon Bland Jun 16 '16 at 21:36
  • @cat Ooh, no free after malloc... Now that I know better than. The code monkey's enemy. – Landon Bland Jun 16 '16 at 21:38
  • Well, as I said, there's no reason to malloc anyways even if you set the line length higher. You can always check out [codereview.se], too, but read their [help center](http://codereview.stackexchange.com) first. :D – cat Jun 16 '16 at 21:42
  • 1
    *Reads `man getline`*. Wow I didn't realize `getline()` does this: "If the buffer is not large enough to hold the line, getline() resizes it with realloc(3), updating *lineptr and *n as necessary." Carry on... – Digital Trauma Jun 16 '16 at 21:56
  • @DigitalTrauma Holy crap. What is this, Java? ;) – Landon Bland Jun 16 '16 at 22:01
  • I get a segfault unless I initialize `bufsize`: `size_t bufsize = 0;` – Dennis Williamson Jun 17 '16 at 16:24
  • 1
    Also you might find [Why is “while ( !feof (file) )” always wrong?](http://stackoverflow.com/a/26557243/26428) useful. You wouldn't need to read a line before the loop. – Dennis Williamson Jun 17 '16 at 16:54
  • @DennisWilliamson Is what I have now closer to a proper loop for getline? Also, I added back in a bufsize initialization; worked on mine without it, so I thought it might be ok. – Landon Bland Jun 20 '16 at 15:34
  • Yes, that's what I did, too. – Dennis Williamson Jun 20 '16 at 17:30
3

If its just the output of find you need to rate-limit in this manner, then you can just use find's -exec parameter to perform the sleep for each line:

sudo find / -exec sleep 0.2 \; -print
Digital Trauma
  • 8,404
  • 2
  • 23
  • 39
1

A simple Go program, to moderate output.

// moderare will slow down printing.
//
// $ find ~ | moderare
package main

import (
    "bufio"
    "flag"
    "fmt"
    "io"
    "log"
    "os"
    "time"
)

var (
    sleep = flag.Duration("s", 1*time.Second, "sleep after each line")
    br    = bufio.NewReader(os.Stdin)
)

func main() {
    flag.Parse()
    for {
        line, err := br.ReadString('\n')
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        time.Sleep(*sleep)
        fmt.Printf(line)
    }
}
miku
  • 673
  • 5
  • 16