aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2017-01-27 17:37:36 +1300
committerTom Ryder <tom@sanctum.geek.nz>2017-01-27 17:47:46 +1300
commita94e37a8a6e30d38522e2dc601b0e7b321f42720 (patch)
treee787de80480efbdfee4a3525fbbe136e9a457852
parentAdd quo(1df) (diff)
downloaddotfiles-a94e37a8a6e30d38522e2dc601b0e7b321f42720.tar.gz
dotfiles-a94e37a8a6e30d38522e2dc601b0e7b321f42720.zip
Add chn(1df)
-rw-r--r--README.markdown1
-rwxr-xr-xbin/chn70
-rw-r--r--man/man1/chn.1df47
-rw-r--r--man/man1/maybe.1df2
-rw-r--r--man/man1/try.1df2
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>