diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2017-01-27 17:37:36 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2017-01-27 17:47:46 +1300 |
commit | a94e37a8a6e30d38522e2dc601b0e7b321f42720 (patch) | |
tree | e787de80480efbdfee4a3525fbbe136e9a457852 | |
parent | Add quo(1df) (diff) | |
download | dotfiles-a94e37a8a6e30d38522e2dc601b0e7b321f42720.tar.gz dotfiles-a94e37a8a6e30d38522e2dc601b0e7b321f42720.zip |
Add chn(1df)
-rw-r--r-- | README.markdown | 1 | ||||
-rwxr-xr-x | bin/chn | 70 | ||||
-rw-r--r-- | man/man1/chn.1df | 47 | ||||
-rw-r--r-- | man/man1/maybe.1df | 2 | ||||
-rw-r--r-- | man/man1/try.1df | 2 |
5 files changed, 120 insertions, 2 deletions
diff --git a/README.markdown b/README.markdown index df6e6e66..2ae64927 100644 --- a/README.markdown +++ b/README.markdown @@ -426,6 +426,7 @@ Installed by the `install-bin` target: * `cfr(1df)` does the same as `cf(1df)`, but recurses into subdirectories as well. * `chc(1df)` caches the output of a command. +* `chn(1df)` runs a filter over its input a given number of times. * `clog(1df)` is a tiny timestamped log system. * `clrd(1df)` sets up a per-line file read, clearing the screen first. * `clwr(1df)` sets up a per-line file write, clearing the screen before each diff --git a/bin/chn b/bin/chn new file mode 100755 index 00000000..46a8a27a --- /dev/null +++ b/bin/chn @@ -0,0 +1,70 @@ +#!/bin/sh +# Repeat a command to filter input several times +self=chn + +# Check arguments. +if [ "$#" -lt 2 ] ; then + printf >&2 '%s: Need a count and a program name\n' "$self" + exit 2 +fi + +# Shift off the repetition count. +c=$1 +shift + +# Check the repetition count looks sane. Zero is fine! +if [ "$c" -lt 0 ] ; then + printf >&2 '%s: Nonsensical negative count\n' "$self" + exit 2 +fi + +# If the count is zero, just run the input straight through! +if [ "$c" -eq 0 ] ; then + cat + exit +fi + +# Create a temporary directory with name in $td, and handle POSIX-ish traps to +# remove it when the script exits. +td= +cleanup() { + [ -n "$td" ] && rm -fr -- "$td" + if [ "$1" != EXIT ] ; then + trap - "$1" + kill "-$1" "$$" + fi +} +for sig in EXIT HUP INT TERM ; do + # shellcheck disable=SC2064 + trap "cleanup $sig" "$sig" +done +td=$(mktd "$self") || exit + +# Define and create input and output files +if=$td/if of=$td/of +touch -- "$if" "$of" + +# Iterate through the count +while [ "${n=1}" -le "$c" ] ; do + + # Start a subshell so we can deal with FDs cleanly + ( + # If this isn't the first iteration, our input comes from $if + [ "$n" -eq 1 ] || + exec <"$if" + + # If this isn't the last iteration, our output goes to $of + [ "$n" -eq "$c" ] || + exec >"$of" + + # Run the command with the descriptors above; if the command fails, the + # subshell will exit, which will in turn exit the program + "$@" + ) || exit + + # Copy the output file over the input one + cp -- "$of" "$if" + + # Increment the counter for the next while loop run + n=$((n+1)) +done diff --git a/man/man1/chn.1df b/man/man1/chn.1df new file mode 100644 index 00000000..576e5425 --- /dev/null +++ b/man/man1/chn.1df @@ -0,0 +1,47 @@ +.TH CHN 1df "January 2017" "Manual page for chn" +.SH NAME +.B chn +\- filter standard input through multiple runs of a command +.SH USAGE +.B chn +COUNT +COMMAND [ARG1...] +.SH DESCRIPTION +Run the given command the specified number of times, passing the standard +output of each run into the standard input of the next. +.P +As an example, to quote some text with quo(1df) repeatedly: +.P + $ cat msg + Hello! + $ chn 2 quo < msg + >> Hello! + $ chn 5 quo < msg + >>>> Hello! +.P +Zero is a valid count; in this case the input is passed untouched to output: +.P + $ chn 0 quo < msg + Hello! +.P +Don't confuse this with simply repeating a command. This happens to work: +.P + $ chn 5 sync +.P +But this will not do what you expect: +.P + $ chn 5 echo foo +.SH CAVEATS +It's slow. +.P +It's not a real pipe. The commands are run successively, not in parallel. That +means you can't pass one line to it and have it return another line before +sending EOF, for unbuffered (e.g. linewise) tools. +.P +There's almost certainly a better way to do this, fixing one or both of the +above issues, and possibly even in shell; maybe with curlier file descriptor +logic to save unneeded open(2) syscalls. I smell `eval` usage on the horizon. +.SH SEE ALSO +maybe(1df), try(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/maybe.1df b/man/man1/maybe.1df index e313eb17..3b89b09d 100644 --- a/man/man1/maybe.1df +++ b/man/man1/maybe.1df @@ -22,6 +22,6 @@ of success or failure. rndi(1df) is used for the randomness. $ maybe 3 $ maybe 2 5 .SH SEE ALSO -true(1), false(1), try(1df), rndi(1df) +true(1), false(1), chn(1df), try(1df), rndi(1df) .SH AUTHOR Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/try.1df b/man/man1/try.1df index d982c1d3..63db5209 100644 --- a/man/man1/try.1df +++ b/man/man1/try.1df @@ -17,6 +17,6 @@ run. $ try maybe $ try -n5 -s10 gms .SH SEE ALSO -maybe(1df) +maybe(1df), chn(1df) .SH AUTHOR Tom Ryder <tom@sanctum.geek.nz> |