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?

3
  • 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. Jun 16, 2016 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, 2016 at 0:23

5 Answers 5

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).

1
  • 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. Jun 16, 2016 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.

3
  • This seems to be doing what I want. exiting is a bit tedious but I can live with that. Jun 16, 2016 at 18:23
  • Something like awk '{system("sleep .5")||exit; print} would work, right? Or you could use perl which doesn't need to use an external command to sleep. Jun 16, 2016 at 20:49
  • This is clever!
    – cat
    Jun 16, 2016 at 21:41
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.

15
  • 2
    Also, not that this is Code Review but I implore you to A) not cast the result of malloc and B) not use atoi in place of better alternatives which should always be used instead of atoi
    – cat
    Jun 16, 2016 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(). Jun 16, 2016 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. Jun 16, 2016 at 21:33
  • 1
    There's no reason to malloc anyways, a char* line[32]; getline(...); will work fine.
    – cat
    Jun 16, 2016 at 21:34
  • 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... Jun 16, 2016 at 21:56
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
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)
    }
}

You must log in to answer this question.

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