out: an output utility
Intro
Whilst writing the tests for the Job Logging feature in Upstart, I discovered that I needed some very specific job behaviour that was not easy to produce using any simple existing utility I could find.The Problem
What I wanted was a utility that would produce specified amounts of data to either standard output, standard error or direct to the terminal without using shell redirection. The reason for eschewing the shell being that Upstart is clever and intelligently determines whether a shell should be involved. From a user perspective, that's fantastic, but for testing purposes, I needed to force Upstart down particular code paths where it would not for example automatically pass the job through a shell. In fact the constraint was even more restrictive; what I really wanted to do was this:Produce output (including null bytes) to stdout, stderr or the terminal without using any shell meta characters.Unless I used some extremely esoteric shell, that effectively meant "no shells" ;-) ksh and zsh actually came close as their printf/print shell built-ins allow output to a specified file descriptor, but not to the terminal. Anyway, by this stage, I'd decided I wanted a generic utility, not some specific shell feature.
The Pragmatic Solution
For the Upstart tests, I ended up using various different command-line utilities for different tests that could produce the data I wanted. However, I couldn't help thinking there should be a better way. Yes, I could have written a bespoke C program or Python/Perl script to achieve my goal, but in the spirit of Larry Walls view that "Easy things should be easy", I thought that there should be a simpler method.The Improved Solution
In the end this niggling problem got the better of me so I decided to write a tool to satisfy the above constraint. What I came up with is a small utility which sits somewhere between echo(1) and printf(1) (with just a dash of seq(1) thrown in) which, since it produces output, I've called simply "out".
Here's an example of how you could print "hello" direct to the terminal (the redirection here is just to show that you still get output even though we've discarded stdout and stderr output):
$ out -t hello >/dev/null 2>&1 helloThis command below will echo "hello" to stdout, the terminal and stderr:
$ out hello -t -r 1 -e -r 1Along with allowing output redirection to any file descriptor or the terminal without the support of shell redirection, it supports the standard C-style escape sequences (like '\n' and '\a'). It also allows you to redefine the escape character.
Additionally, out supports printf(1)-style Unicode escape sequences such as '\uFFFFFFFF' . After adding the Unicode support, I thought it would be rather fun to add ranges (similar to bash "sequence expressions") so out also supports some new sequences that allow sets of characters to be generated. These are great fun for exploring Unicode. Here's an example of generating the alphabet in lower-case:
$ out '\{a..z}\n' abcdefghijklmnopqrstuvwxyzHow about some Greek?
$ out "\{α..ω}\n" αβγδεζηθικλμνξοπρςστυφχψωOr we could specify this using Unicode characters to get the same result:
$ out "\{\u03b1..\u03c9}\n" αβγδεζηθικλμνξοπρςστυφχψωAnd here's how you could generate the Unicode braille block:
$ out '\{\u2800..\u28FF}\n' ⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿out also allows arbitrary delays to be introduced, repeats, basic random character generation and a few other tidbits. Here is the man page including a lot of examples:
OUT(1) OUT(1) NAME out - manual page for out SYNOPSIS out [OPTION]... [STRING]... DESCRIPTION Echo strings to specified output stream(s). OPTIONS -a, --intra-char=<char> Insert specified character (which may be a 1-character escape character) between all output characters. -b, --intra-pause=<delay> Pause between writing each character. -e, --stderr Write subsequent strings to standard error (file descriptor 2). -h, --help This help text. -i, --interpret Interpret escape characters (default). -l, --literal Write literal strings only (disable escape characters). -o, --stdout Write subsequent strings to standard output (file descriptor 1). -p, --prefix=<prefix> Use <prefix> as escape prefix (default='\'). -r, --repeat=<repeat> Repeat previous value <repeat> times. -s, --sleep=<delay> Sleep for <delay> amount of time. -t, --terminal Write subsequent strings directly to terminal. -u, --file-descriptor=<fd> Write to specified file descriptor. -x, --exit=<num> Exit with value <num>. ESCAPE CHARACTERS out recognises C-style escape sequences as used by printf(1) . By default an escape sequence is introduced by the backslash character ('\'), however this may be changed with the -p option. out also sup‐ ports some additional sequences: \0 - nul byte (hex value 0x00) \a - alert (bell) \b - backspace \c - no further output \e - escape character (used for changing terminal attributes) \f - form feed \g - generate pseudo-random printable character \n - newline \oNNN - byte with octal value NNN (1 to 3 digits) \r - carriage return \t - horizontal tab \uNNNN - 2 byte Unicode (ISO/IEC 10646) character with hex value NNNN (4 digits) \UNNNNNNNN - 4 byte Unicode (ISO/IEC 10646) character with hex value NNNNNNNN (8 digits) \v - vertical tab \xNN - byte with hexadecimal value NN (1 to 2 digits) RANGE ESCAPES out also supports range escapes which allow a range of characters to be specified in a compact format. \{N..N} - specify a range by two 1-byte literal characters. \{oNNN..oNNN} - specify a range by two 3-byte octal values. \{uNNNN..uNNNN} - specify a range by two 2-byte Unicode values. \{UNNNNNNNN..UNNNNNNNN} - specify a range by two 4-byte Unicode values. \{xNN..xNN} - specify a range by two 2-byte hex values. Note that ranges take two values of the same type and the maximum width for that type must be specified. NOTES · Arguments are processed in order. · With the exception of '-x', arguments may be repeated any number of times. · All output will be sent to standard output until an output redi‐ rection option is specified that changes the output stream (namely -e or -t (or their long-option equivalents), or if output has already been redirected -o (or its long-option equivalent)). · If <str> is the empty string ("" or '') it will be treated as \0 such that a nul byte will be displayed. · To cancel the effect of -a, specify a null string: -a ''. · If <repeat> is '-1', repeat forever. · Replace the 'Z' in the range formats above with the appropriate characters. · Ranges can be either ascending or descending. · <delay> can take the following forms where <num> is a positive integer: <num>ns : nano-seconds (1/1,000,000,000 second) <num>us : micro-seconds (1/1,000,000 second) <num>ms : milli-seconds (1/1,000 second) <num>cs : centi-seconds (1/100 second) <num>ds : deci-seconds (1/10 second) <num>s : seconds <num>m : minutes <num>h : hours <num>h : days <num> : seconds If <num> is -1, wait until any signal is received. If signal is SIGNUM continue, else exit immediately. · Generated printable random characters may not display unless you are using an appropriate font. EXAMPLES # Print "foofoofoo" to stderr, followed by "barbar" to stdout. out "foo" -r 2 -o "bar" -r 1 # Write 50 nul bytes direct to the terminal. out -t "" -r 49 # Write continuous stream of nul bytes direct to the terminal, # 1 per second. out -b 1s -t '' -r -1 # Display a greeting slowly (as a human might type) out -b 20cs "Hello, $USER.\n" # Display a "spinner" that loops 4 times. out -b 20cs -p % "%r|%r/%r-%r\%r" -r 3 # Display all digits between zero and nine with a trailing # newline. out "\{0..9}\n" # Display slowly the lower-case letters of the alphabet, # backwards without a newline. out -b 1ds "\{z..a}" # Display upper-case 'ABC' with newline. out '\u0041\u0042\u0043\n' # Display 'foo' with newline. out '\o146\u006f\x6F\n' # Clear the screen. out '\n' -r $LINES # Write hello to stdout, stderr and the terminal. out 'hello' -t -r 1 -e -r 1 # Display upper-case letters of the alphabet using octal # notation, plus a newline. out "\{\o101..\o132}" # Display 'h.e.l.l.o.' followed by a newline. out -a . "hello" -a '' "\n" # Display upper-case and lower-case letters of the alphabet # including the characters in-between, with a trailing newline. out "\{A..z}\n" # Display lower-case alphabet followed by reversed lower-case alphabet # with the digits zero to nine, then nine to zero on the next line. out "\{a..z}\{z..a}\n\{0..9}\{9..0}\n" # Display lower-case Greek letters of the alphabet. out "\{α..ω}" # Display cyrillic characters. out "\{Ѐ..ӿ}" # Display all printable ASCII characters using hex range: out "\{\x21..\x7e}" # Display all printable ASCII characters using 2-byte UTF-8 range: out "\{\u0021..\u007e}" # Display all printable ASCII characters using 4-byte UTF-8 range: out "\{\U00000021..\U0000007e}" # Display all braille characters. out "\{\u2800..\u28FF}" # Display 'WARNING' in white on red background. out '\e[37;41mWARNING\e[0m\n' # Generate 10 random characters. out '\g' -r 9 AUTHOR Written by James Hunt <james.hunt@ubuntu.com> COPYRIGHT Copyright © 2012 James Hunt <james.hunt@ubuntu.com> LICENSE GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. SEE ALSO echo(1) printf(1) User Commands 2012-09-10 OUT(1)
Comments
Post a Comment