Spinner Function for Shell Scripts
dteske at vicor.com
Sat Nov 13 23:03:30 UTC 2010
On Nov 13, 2010, at 1:19 PM, Carlos A. M. dos Santos wrote:
> On Thu, Nov 11, 2010 at 6:12 PM, Devin Teske <dteske at vicor.com> wrote:
>> Hi fellow hackers... I come with baring gifts!
>> So, just as the subject-line says, ... here's an efficient and robust
>> spinner function compatible with many shells.
>> DONE=$( /bin/sh -c 'read -t 0 DONE; echo $DONE' )
> Is this expected to be portable to other operating systems? The dash
> shell, used as /bin/bash in Ubuntu, does not acept the "-t" argument
> to the read builtin command. Using /bin/bash solves the problem.
I was shooting for bourne-shell, but later realized that bash's read statement functions fundamentally different than bourne's (hence the direct invocation of /bin/sh).
$ echo 'Hello' | /bin/sh -c 'read -t 0 DONE; echo $DONE'
$ echo 'Hello' | /bin/bash -c 'read -t 0 DONE; echo $DONE'
$ echo 'Hello' | /bin/bash -c 'read -t 1 DONE; echo $DONE'
$ /bin/bash -c 'read -t 0.5 DONE; echo $DONE'
/bin/bash: line 0: read: 0.5: invalid timeout specification
As you can see from the above, passing a timeout specification of zero to bourne-shell has the desired effect... if the stdin file descriptor has something to offer the read-statement, input will be read up-to the ending newline. Whereas, you can see that bash's implementation returns nothing from the read despite data being available. Naturally, one can get the data into bash's read by using a timeout of one, but that would be unacceptable to our spinner to rotate the spinner one-quarter spin each second. Last, the potential work-around of using finer measurements of time is not allowed.
However, this shouldn't be considered a bug, given the following description of the timeout option in bash's InfoTex manual:
Cause `read' to time out and return failure if a complete
line of input is not read within TIMEOUT seconds. This
option has no effect if `read' is not reading input from the
terminal or a pipe.
Now, compare that with the description from bourne-shell's man-page (sh(1)):
If the -t option is specified and the timeout elapses before any
input is supplied, the read command will return without assigning
any values. The timeout value may optionally be followed by one
of ``s'', ``m'' or ``h'' to explicitly specify seconds, minutes
or hours. If none is supplied, ``s'' is assumed.
So, as you can see, bash will timeout if a full line is not received in the duration specified as the timeout, while bourne-shell will timeout only if no input is supplied for the same duration.
For our purposes, we want bourne-shell's implementation, not bash's (at least on FreeBSD).
I also tested Mac OS X 10.6.1, FreeBSD-8.1, CentOS 4.7, RedHat Enterprise Linux (RHEL) 4 Nahant Update 4 (U4), and also RHEL4U8.
Here's the breakdown for the test of ``read -t 0 LINE; echo $LINE'':
Mac OS X 10.6.1:
RedHat Enterprise Linux (RHEL) 4 Update 4 (U4):
RedHat Enterprise Linux (RHEL) 4 Update 8 (U8):
This is not so surprising... On all of the above-tested Linux OSes, /bin/sh is a symbolic link to bash(1). On Mac OS X, /bin/sh is simply a bash binary (try /bin/sh --version).
It's rather unfortunate that bourne-shell has what we need but is not available on all operating systems since many OSes have started swapping out bourne-shell for it's younger cousin.
Does anyone else happen to know of a portable way to quickly check if data is available on a given file descriptor?
-> CONTACT INFORMATION <-
Business Solutions Consultant II
FIS - fisglobal.com
510-621-2020 Office Fax
devin.teske at fisglobal.com
-> LEGAL DISCLAIMER <-
This message contains confidential and proprietary information
of the sender, and is intended only for the person(s) to whom it
is addressed. Any use, distribution, copying or disclosure by any
other person is strictly prohibited. If you have received this
message in error, please notify the e-mail sender immediately,
and delete the original message without making a copy.
-> END TRANSMISSION <-
More information about the freebsd-hackers