Progress Bars
|Some background information
The progress bar.
Not just the name of a sadly defunct bar in London or a sign depicting the prevention of egress, progress bars serve a useful purpose to inform the user just how long they’re going to have to sit around with a thumb up their arse whilst they perform their highly skilled non-DMCA-based white-collar workloads.
As you may be aware, Microsoft will be dropping support for several classes of progress bar in 2014, for reasons of security to the Microsoft revenue base.
A lot of tasks don’t have an easily-defined halting condition, so you get slightly different progress bars that move a little to the right, and then a little to the left, or look like barberpoles if you use one of those computers that they look like barberpoles on.
These days of course, it’s more modern to expose your end-users to “throbbers” instead, which give a pleasing sense of going round in circles, and are probably a good indication of what limbo is going to end up being like, if you’re religiously inclined.
They appear in many guises, such as
Some foreground information
So anyway, I’m not one to stand in the way of progress (ha), and now that I think you might know what a progress bar is, here’s something to make your scp task generate some javascript which you can dump into a browser to make a little rectangle get larger as the copy process takes place.
People generally expect to see this sort of thing during long periods of what appears to be inactivity, so you might as well give it to them so as not to surprise them, which apparently is a usability nightmare, unless you’re trying to cram the next great evolution in gesture-based interfaces down people’s throats.
The program itself is called scpwrap, and it’s vaguely similar to that previous post about piping stdout/stderr streams in VBA, since that’s what it does, but this implementation is in C, which is much more self-respecting. You could probably drop it into a bash script without people thinking they should wrap it in a chef recipe or a puppet whatever-they-call-their-module-components [1].
It’s slightly more complicated than it should be because scp won’t normally display progress information if it’s not running in a terminal, hence the forkpty pseudo-terminal malarky on line 319. The code is based on (and is backwards-compatible with) the scp-to-zenity wrapper written by Clayton Shepard, but also handles non-standard stdout/stderr streams, has customisable output templates, and deals with error conditions in a relatively sane way.
Here’s the sourcecode for the scpwrap program, and the groff source for the manpage, which took almost as long to write up.
There’s also a link to the randomnoun apt repository at the bottom of this post, if you want to install it automatically.
And here’s the manpage itself:
SCPWRAP(1) User Commands SCPWRAP(1) NAME scpwrap - a wrapper for scp to convert its output into machine-readable format SYNOPSIS scpwrap [--js] [--stdoutTemplate template ] [--stderrTemplate template ] [--startTemplate template ] [--progressTemplate template ] [--endTemplate template ] -- scp-options ... DESCRIPTION scpwrap wraps the scp(1) command such that the output generated by that command is converted into a format more easily processed by other commands. scp(1) progress information is captured and converted into a machine- readable format for use in more sophisticated user interfaces (which in turn would require even more effort to turn back into a machine- readable format). And so the circle of life continues. The built-in text and javascript output templates are suitable for zenity(1), or some as-yet-to-be-documented javascript processor, respectively. See the EXAMPLES section to see the general syntax of these defaults. OPTIONS --js Use the default javascript templates rather than the default text templates (see the DEFAULTS section below). This switch also enables javascript-output escaping (see the JAVASCRIPT OUTPUT ESCAPES section below). --stdoutTemplate template Use the supplied template to generate machine-readable text when unrecognised output is detected on stdout. The placeholder %s in the template will be replaced by the text received on stdout. --stderrTemplate template Use the supplied template to generate machine-readable text when any output is detected on stderr (e.g. to display text contained in the remote system's sshd (8) Banner file). The placeholder %s in the template will be replaced by the text received on stderr. --startTemplate template Use the supplied template to generate machine-readable text as scp progress begins. --progressTemplate template Use the supplied template to generate machine-readable text as scp progresses. The placeholders %f, %p, %t, %s and %e are available (see the PLACEHOLDERS section below) --endTemplate template Use the supplied template when scp completes. The %c placeholder is available. scp-options these command-line options are passed directly to scp(1) and should conform to the expected syntax of that command. PLACEHOLDERS Placeholders can (and should) be embedded in template strings, which will be replaced by the appropriate value when the stdout/stderr of scp(1) is parsed by this utility. Most placeholders are only available in specific template types. Recognised placeholders are: %s Free-form stdout or stderr text %f The name of the file being copied (e.g. test.txt) %p The percentage progress of the current file being copied (e.g. 13) %t The transfer size of the current file being copied (e.g. 2112KB) %s The transfer speed of the current file being copied (e.g. 2.1MB/s) %e The ETA of the current file being copied (e.g. --:-- or 05:23) %c The exit code of the scp(1) process (e.g. 0), or a negative number if the process was terminated by a signal (the absolute value of this number corresponds to the signal number received). PLACEHOLDER ESCAPES The following escape sequences are recognised in placeholder strings supplied on the command-line: \n Newline \r Carriage return \t Tab character \\ Backslash JAVASCRIPT OUTPUT ESCAPES If the text generated by scp contains characters that would cause javascript parsing errors, and the --js command-line option is in effect, then the strings substituted into the %s placeholder will be javascript-escaped (e.g. newlines will be replaced by \n, characters above ASCII codepoint 127 will be replaced by \u0000-style sequences.) DEFAULTS The following defaults are used, in the absence of any template overrides: Text output (without --js parameter) Note that many templates are simply "" (i.e. the empty string, therefore no output is generated) --stdoutTemplate "" --stderrTemplate "" --startTemplate "" --progressTemplate "%p\n" --endTemplate "" Javascript output (with --js parameter) The default javascript output relies on a script-visible 'ui' object, as shown below. --stdoutTemplate "ui.addOutput(\"%s\");\n" --stderrTemplate "ui.addOutputError(\"%s\");\n"; --startTemplate "var sp = ui.startScpProgress();\n"; --progressTemplate "sp.setProgress(\"%f\", %p, \"%t\", \"%s\", \"%e\");\n"; --endTemplate "ui.stopScpProgress(%c);\n"; EXIT STATUS The scpwrap command will return the same exit code as the child scp process; i.e. it exits 0 on success, and >0 if an error occurs. If a signal interrupts processing of the child process, then scpwrap terminates with an exit status of 1. The signal number can be determined using the %c placeholder to the --endTemplate template. EXAMPLES Example 1 (text output) The command scpwrap -- -i key.pem somefile.tar.gz \ user@somehost:/home/user/somefile.tar.gz might produce output something similar to the following: 0 45 47 49 ... 20 lines omitted ... 98 100 Example 2 (javascript output) The command scpwrap --js -- -i key.pem somefile.tar.gz \ user@somehost:/home/user/somefile.tar.gz \ might produce output something similar to the following: ui.addOutputError("NOTICE TO USERS\n"); ui.addOutputError("\n"); ui.addOutputError("This service is for authorised clients only.\n"); ui.addOutputError("\n"); ui.addOutputError("This computer system is the private property of its owner, whether\n"); ui.addOutputError("individual, corporate or government. It is for authorized use only.\n"); ui.addOutputError("Users (authorised or unauthorised) have no explicit or implicit\n"); ui.addOutputError("expectation of privacy.\n"); ui.addOutputError("\n"); ui.addOutputError("It is a criminal offence to:\n"); ui.addOutputError(" i. Obtain access to data without authority\n"); ui.addOutputError(" (Penalty 2 years imprisonment)\n"); ui.addOutputError(" ii Damage, delete, alter or insert data without authority\n"); ui.addOutputError(" (Penalty 10 years imprisonment)\n"); ui.addOutputError("\n"); ui.addOutputError("For more information, see http://www.randomnoun.com/login-banner.html\n"); var sp = ui.startScpProgress(); sp.setProgress("somefile.tar.gz", 0, "0", "0.0KB/s", "--:--"); sp.setProgress("somefile.tar.gz", 45, "2112KB", "2.1MB/s", "00:01"); sp.setProgress("somefile.tar.gz", 47, "2208KB", "1.9MB/s", "00:01"); sp.setProgress("somefile.tar.gz", 49, "2320KB", "1.7MB/s", "00:01"); sp.setProgress("somefile.tar.gz", 52, "2448KB", "1.5MB/s", "00:01"); sp.setProgress("somefile.tar.gz", 54, "2576KB", "1.4MB/s", "00:01"); ... 20 lines omitted ... sp.setProgress("somefile.tar.gz", 98, "4624KB", "266.7KB/s", "00:00"); sp.setProgress("somefile.tar.gz", 100, "4693KB", "187.7KB/s", "00:25"); ui.stopScpProgress(0); Example 3 (custom javascript output) The command scpwrap --js --stderrTemplate '' --stdoutTemplate '' \ --startTemplate '' --endTemplate '' \ --progressTemplate 'setProgress(%p);\n' -- -i key.pem somefile.tar.gz \ user@somehost:/home/user/somefile.tar.gz might produce output something similar to the following: setProgress(0); setProgress(45); setProgress(47); ... 20 lines omitted ... setProgress(98); setProgress(100); BUGS It might be preferable to get the default strings from something in /etc The whole thing's a bit pointless AUTHOR Greg KnoxLICENCE (c) 2013 randomnoun. All Rights Reserved. This work is licensed under a BSD Simplified License. (http://www.randomnoun.com/bsd-simplified.html) SEE ALSO vmaint(1), https://www.randomnoun.com/wp/2013/10/31/progress-bars/ vmaint OCTOBER 2013 SCPWRAP(1)
If you want to install it in binary form, I've created an apt repository for a couple of ubuntu distribution/architecture combinations (maverick/amd64 and precise/i386), which you can add to your /etc/apt/sources.list file automatically using these commands:
$ sudo add-apt-repository "deb http://packages.randomnoun.com/ubuntu $(lsb_release -sc) main" $ gpg --keyserver hkp://pool.sks-keyservers.net --recv-key A21A1486 && gpg --export --armor A21A1486 | sudo apt-key add - gpg: requesting key A21A1486 from hkp server pool.sks-keyservers.net gpg: key A21A1486: "Greg Knox" not changed gpg: Total number processed: 1 gpg: unchanged: 1 OK $ sudo apt-get update Get:1 http://packages.randomnoun.com maverick Release.gpg [198B] Get:2 http://packages.randomnoun.com maverick Release [2,242B] Ign http://packages.randomnoun.com maverick/main Sources Ign http://packages.randomnoun.com maverick/main amd64 Packages ... $ sudo apt-get install scpwrap Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: scpwrap 0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. Need to get 12.0kB of archives. After this operation, 69.6kB of additional disk space will be used. Get:1 http://packages.randomnoun.com/ubuntu/ maverick/main scpwrap amd64 1.0-1 [12.0kB] Fetched 12.0kB in 0s (113kB/s) Selecting previously deselected package scpwrap. (Reading database ... 59957 files and directories currently installed.) Unpacking scpwrap (from .../scpwrap_1.0-1_amd64.deb) ... Processing triggers for man-db ... Setting up scpwrap (1.0-1) ...
If you don't have add-apt-repository
installed in your OS, then try sudo apt-get install python-software-properties
first (or you could manually edit /etc/apt/sources.list instead).
Update 4/11/2013: Added instructions for the packages.randomnoun.com apt repository; added BSD licence to source code
Well that was certainly time well spent.
You’re telling me.