|
Hosted by Ed Schaefer
Instead of using temporary files in shell scripts, you could use named pipes
or fifos (First-In, First-Out). This month, Nathaniel Donat (donat.n@pg.com),
defines named pipes, introduces Bourne script build.ss
using named pipes, and finally, directs us to some Unix Interprocess Communication
(IPC) resources on the Internet:
What are Named Pipes?
Have you ever wanted to pipe your standard output to another set of commands, but required some intervening non-pipelined step? Use named pipes. Depending on the system, named pipes are created using:
mkfifo [-p] [-m mode] filename
mknod [-m mode] filename
mknod -f [-m mode] filename
A quick, nonsensical command line example:
mkfifo -m 600 foo
ls -l > foo &
cat foo
Direct the process writing to the fifo to run in the background, otherwise
the foreground process is blocked until something reads the pipe.
Scripts do not have to use temp files, which later get moved, removed,
or waste space. In general, named pipes have the advantages of being accessed
by name; they may be accessed at any time by unrelated processes, and
unless deleted, persist after the process ends. Essentially, pipes are
a reusable sink. Since named pipes exist on the file system, they can
also be much larger than internal pipes, which are usually 4K to 8K circular
buffers.
Script build.ss is something
I put together that demonstrates and encourages the use of named pipes
in shell scripts. The example Bourne script, build.ss, copies files of
a given type from a build or test area to a production area. This script
directs standard output to a named piped while being interactive with
a user.
build.ss helps a user move files, via command-line argument, from a
build directory to the corresponding production directory. In this case,
the build directory contains "build" somewhere in the path and
the production directory has "prod" in place of "build".
As the user interacts to prompts, the named pipe keeps the log of files
copied. The log could go to a file somewhere, but here the log is for
the user only.
Executing build.ss in the build directory:
build.ss exe
copies files with an exe extension to the production directory.
The production directory is at the same level as build.
Centering Text with sed
As an advocate of sed, I think the centering function should get special
attention:
_center ()
{
echo "$1" |
sed '
s/^[ ]*//
s/[ ]*$//
:a
s/^.\{1,72\}$/ &/
ta
s/\( *\)\1/\1/
'
}
First, all initial and terminal space and tab characters are removed.
Next, a loop is used to pad the string(s) with space characters until
attaining a maximum length of 72 characters. As long as the string is
fewer than 72 characters, substitution (space insertion) occurs and the
test (t) command branches to "a". Once the string
reaches 72 characters, there is no substitution, and the test command
fails and branching discontinues.
The final substitution seeks a regular expression made up of continuous
space characters followed by the same number of space characters, hence,
\1 is one-half the total padding of space characters. The
whole regular expression of space characters is simply replaced by the
string of one-half the total padding, thus centering the text. If the
desired page width is something other than 72 characters (e.g., 80), replace
72 with the desired value.
If you are cutting and pasting, beware that the brackets in the sed explanations contain a space character followed by a tab character.
IPC Examples on the Internet
Shell Programming Examples
Steven Heiner's brilliant script, hfinger (http://www.shelldorado.com/scripts/cmds/hfinger.txt),
runs in the background and keeps logs and mail messages whenever the finger
command runs on your login name.
James Van Zandt's useful Bash script (http://docsrv.caldera.com:8457/en/AdvBashHowto/contributed-scripts.html#FIFO)
uses named pipes for making daily backups.
C Examples
Steven Engelhardt introduces using named pipes with "Writing a Simple
Signature Randomizer in C Using FIFOs" (http://www.deez.info/sengelha/projects/sigrandd/sigrandd.html).
Ed Schaefer's Sys Admin article "Using
the UNIX Pipe in C" (July/August 1993) also covers pipes.
Nate's build.ss Bourne
script:
#!/bin/sh
#
# Named pipe example:
# build.ss: Moving Files From Build Environment to Production
# Author: Nathaniel Donat
#
# Copies files of given type from BUILD into PRODUCTION. Must be in BUILD
# directory. Copies files when BUILD version has a more recent
# modification date than PRODUCTION or if the file does not yet exist
# in PRODUCTION.
#
# Set build and production directory variables and the file type
BCWD=`pwd`
PCWD=`pwd | sed 's/\/build\//\/prod\//;s/\/build$/\/prod/'`
#echo $BCWD $PCWD
TYPE="*.$1"
header="Copy ${TYPE} files from BUILD to PRODUCTION"
# Do assertions
if [ $# -ne 1 ]; then
echo "Usage: `basename $0` file-type" 1>&2
exit 1
elif [ "${BCWD}" = "${PCWD}" ]; then
echo "May not be in build directory" 1>&2
exit 1
elif [ `ls ${TYPE} 2>/dev/null | wc -l` -eq 0 ]; then
echo "No ${TYPE} files found in ${PCWD}" 1>&2
exit 1
elif [ ! -d $PCWD ]; then
echo "Production directory does not exist" 1>&2
exit 1
fi
# This function centers a line of text.
# Cut & paste issue: the brackets in the sed explanation contain
# a space character followed by a tab character
_center ()
{
echo "$1" |
sed '
s/^[ ]*//
s/[ ]*$//
:a
s/^.\{1,72\}$/ &/
ta
s/\( *\)\1/\1/
'
}
# Display header information and center it
echo
_center "$header"
echo
# Get user choice for how to proceed
while [ "${ANS}" = "" ]
do
echo "Do you want automatic overwrites of ${TYPE} files to PROD? (y/n/q) \c"
read ANS
ANS=`echo ${ANS} | tr "[:upper:]" "[:lower:]"`
if [ "${ANS}" = "q" ]; then
echo "Quiting `basename $0`"
exit 0
elif [ "${ANS}" != "y" -a "${ANS}" != "n" ]; then
ANS=""
fi
done
# Create named pipe to keep log and use trap for eventual display and removal
PIPE=${TMPDIR:=/tmp}/`basename $0`.$$
trap "cat $PIPE; rm -f $PIPE" 0
trap "exit 1" 1 2 3 15
# Set mode to read-write for user and group
mkfifo -m 660 $PIPE || (echo "cannot create named pipe"; exit 1)
echo "\n" > $PIPE &
# Loop through all files of the type
for file in ${TYPE}
do
# Set build and production file variables
BFILE="${BCWD}/${file}"
PFILE="${PCWD}/${file}"
# Is the build file a regular file?
if [ -f "${BFILE}" ]; then
# Is production file non-existent or older than build version
GO_AHEAD=`find . -name "${PFILE}" -type f -newer "${BFILE}"`
if [ "${GO_AHEAD}" = "" ]; then
# Is prompting expected?
if [ "${ANS}" = "n" ]; then # Obtain user choice
QUE=""
while [ "${QUE}" = "" ]
do
echo "overwrite PROD:${file}? (y/n) \c"
read QUE
QUE=`echo "${QUE}" | tr "[:upper:]" "[:lower:]"`
if [ "${QUE}" = "y" ]; then
cp -f "${BFILE}" "${PCWD}"
echo "Copied ${file} to PRODUCTION" >> $PIPE &
elif [ "${QUE}" = "q" ]; then
exit 0
elif [ "${QUE}" != "n" ]; then
QUE=""
fi
done
else
cp -f "${BFILE}" "${PCWD}"
echo "Copied ${file} to PRODUCTION" >> $PIPE &
fi
fi
fi
done
|