diff options
Diffstat (limited to 'examples/scripts')
26 files changed, 3700 insertions, 0 deletions
diff --git a/examples/scripts/adventure.sh b/examples/scripts/adventure.sh new file mode 100755 index 0000000..1694450 --- /dev/null +++ b/examples/scripts/adventure.sh @@ -0,0 +1,553 @@ +#!/bin/bash +# ash -- "Adventure shell" +# last edit: 86/04/21 D A Gwyn +# SCCS ID: @(#)ash.sh 1.4 + +OPATH=$PATH + +ask() +{ + echo -n "$@" '[y/n] ' + read ans + + case "$ans" in + y*|Y*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +CAT=${PAGER:-more} + +ash_inst() +{ + cat <<- EOF + + Instructions for the Adventure shell + + Welcome to the Adventure shell! In this exploration of the UNIX file + system, I will act as your eyes and hands. As you move around, I will + describe whatever is visible and will carry out your commands. The + general form of a command is + Verb Object Extra_stuff. + Most commands pay no attention to the "Extra_stuff", and many do not + need an "Object". A typical command is + get all + which picks up all files in the current "room" (directory). You can + find out what you are carrying by typing the command + inventory + The command "help" results in a full description of all commands that I + understand. To quit the Adventure shell, type + quit + + There are UNIX monsters lurking in the background. These are also + known as "commands with arguments". + + Good luck! + EOF +} + +ash_help() +{ +echo "I understand the following commands (synonyms in parentheses):" +echo "" + +echo "change OBJECT to NEW_NAME changes the name of the object" +echo "clone OBJECT as NEW_NAME duplicates the object" +echo "drop OBJECTS leaves the objects in the room" +echo "enter (go) PASSAGE takes the labeled passage" +echo "examine OBJECTS describes the objects in detail" +echo "feed OBJECT to MONSTER stuffs the object into a UNIX monster" +echo "get (take) OBJECTS picks up the specified objects" +echo "gripe (bug) report a problem with the Adventure shell" +echo "help prints this summary" +echo "inventory (i) tells what you are carrying" +echo "kill (destroy) OBJECTS destroys the objects" +echo "look (l) describes the room, including hidden objects" +echo "open (read) OBJECT shows the contents of an object" +echo "quit (exit) leaves the Adventure shell" +echo "resurrect OBJECTS attempts to restore dead objects" +echo "steal OBJECT from MONSTER obtains the object from a UNIX monster" +echo "throw OBJECT at daemon feeds the object to the printer daemon" +echo "up takes the overhead passage" +echo "wake MONSTER awakens a UNIX monster" +echo "where (w) tells you where you are" +echo "xyzzy moves you to your home" +} + +MAINT=chet@ins.cwru.edu + +PATH=/usr/ucb:/bin:/usr/bin:/usr/local/bin:. +export PATH + +trap 'echo Ouch!' 2 3 +#trap '' 18 # disable Berkeley job control + +#ash_lk(){ echo " $1 " | fgrep " $2 " >&- 2>&-; } +ash_lk(){ echo " $1 " | fgrep -q " $2 " >/dev/null 2>&1 ; } +ash_pr(){ echo $* | tr ' ' '\012' | pr -5 -t -w75 -l$[ ( $# + 4 ) / 5 ]; } +ash_rm(){ echo " $1 " | sed -e "s/ $2 / /" -e 's/^ //' -e 's/ $//'; } + +# enable history, bang history expansion, and emacs editing +set -o history +set -o histexpand +set -o emacs + +cd +LIM=.limbo # $HOME/$LIM contains "destroyed" objects +mkdir $LIM || { + echo "ash: cannot mkdir $LIM: exiting" + exit 1 +} +KNAP=.knapsack # $HOME/$KNAP contains objects being "carried" +if [ ! -d $KNAP ] +then mkdir $KNAP >/dev/null 2>&1 + if [ $? = 0 ] + then echo 'You found a discarded empty knapsack.' + else echo 'You have no knapsack to carry things in.' + exit 1 + fi +else echo 'One moment while I peek in your old knapsack...' +fi + +kn=`echo \`ls -a $KNAP | sed -e '/^\.$/d' -e '/^\.\.$/d'\`` + +if ask 'Welcome to the Adventure shell! Do you need instructions?' +then + ash_inst + echo -n 'Type a newline to continue: ' + read +fi + +wiz=false +cha=false +prev=$LIM +while : +do room=`pwd` + if [ $room != $prev ] + then if [ $room = $HOME ] + then echo 'You are in your own home.' + else echo "You have entered $room." + fi + exs= + obs= + hexs= + hobs= + f=false + for i in `ls -a` + do case $i in + .|..) ;; + .*) if [ -f $i ] + then hobs="$hobs $i" + elif [ -d $i ] + then hexs="$hexs $i" + else f=true + fi + ;; + *) if [ -f $i ] + then obs="$obs $i" + elif [ -d $i ] + then exs="$exs $i" + else f=true + fi + ;; + esac + done + if [ "$obs" ] + then echo 'This room contains:' + ash_pr $obs + else echo 'The room looks empty.' + fi + if [ "$exs" ] + then echo 'There are exits labeled:' + ash_pr $exs + echo 'as well as a passage overhead.' + else echo 'There is a passage overhead.' + fi + if sh -c $f + then echo 'There are shadowy figures in the corner.' + fi + prev=$room + fi + + read -e -p '-advsh> ' verb obj x # prompt is '-advsh> ' + if [ $? != 0 ] + then verb=quit # EOF + fi + + case $verb in + change) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then set -- $x + case "$1" in + to) if [ "$2" ] + then if [ -f $2 ] + then echo "You must destroy $2 first." + set -- + fi + if [ "$2" ] + then if mv $obj $2 # >&- 2>&- + then echo "The $obj shimmers and turns into $2." + obs=`ash_rm "$2 $obs" "$obj"` + else echo "There is a cloud of smoke but the $obj is unchanged." + fi + fi + else echo 'To what?' + fi + ;; + *) echo "Change $obj to what?" + ;; + esac + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + else echo "I see no $obj here." + fi + fi + else echo 'Change what?' + fi + ;; + clone) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then if [ ! -r $obj ] + then echo "The $obj does not wish to be cloned." + else set -- $x + case "$1" in + as) if [ "$2" ] + then if [ -f $2 ] + then echo "You must destroy $2 first." + else if cp $obj $2 # >&- 2>&- + then echo "Poof! When the smoke clears, you see the new $2." + obs="$obs $2" + else echo 'You hear a dull thud but no clone appears.' + fi + fi + else echo 'As what?' + fi + ;; + *) echo "Clone $obj as what?" + ;; + esac + fi + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + else echo "I see no $obj here." + fi + fi + else echo 'Clone what?' + fi + ;; + drop) if [ "$obj" ] + then for it in $obj $x + do if ash_lk "$kn" "$it" + then if [ -w $it ] + then echo "You must destroy $it first." + else if mv $HOME/$KNAP/$it $it # >&- 2>&- + then echo "$it: dropped." + kn=`ash_rm "$kn" "$it"` + obs=`echo $it $obs` + else echo "The $it is caught in your knapsack." + fi + fi + else echo "You're not carrying the $it!" + fi + done + else echo 'Drop what?' + fi + ;; + enter|go) if [ "$obj" ] + then if [ $obj != up ] + then if ash_lk "$exs $hexs" "$obj" + then if [ -x $obj ] + then if cd $obj + then echo 'You squeeze through the passage.' + else echo "You can't go that direction." + fi + else echo 'An invisible force blocks your way.' + fi + else echo 'I see no such passage.' + fi + else if cd .. + then echo 'You struggle upwards.' + else echo "You can't reach that high." + fi + fi + else echo 'Which passage?' + fi + ;; + examine) if [ "$obj" ] + then if [ $obj = all ] + then $obj=`echo $obs $exs` + x= + fi + for it in $obj $x + do if ash_lk "$obs $hobs $exs $hexs" "$it" + then echo "Upon close inspection of the $it, you see:" + ls -ld $it 2>/dev/null + if [ $? != 0 ] + then echo "-- when you look directly at the $it, it vanishes." + fi + else if ash_lk "$kn" "$it" + then echo 'You must drop it first.' + else echo "I see no $it here." + fi + fi + done + else echo 'Examine what?' + fi + ;; + feed) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then set -- $x + case "$1" in + to) if [ "$2" ] + then shift + if PATH=$OPATH $* <$obj 2>/dev/null + then echo "The $1 monster devours your $obj." + if rm -f $obj # >&- 2>&- + then obs=`ash_rm "$obs" "$obj"` + else echo 'But he spits it back up.' + fi + else echo "The $1 monster holds his nose in disdain." + fi + else echo 'To what?' + fi + ;; + *) echo "Feed $obj to what?" + ;; + esac + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + else echo "I see no $obj here." + fi + fi + else echo 'Feed what?' + fi + ;; + get|take) if [ "$obj" ] + then if [ $obj = all ] + then obj="$obs" + x= + fi + for it in $obj $x + do if ash_lk "$obs $hobs" "$it" + then if ash_lk "$kn" "$it" + then echo 'You already have one.' + else if mv $it $HOME/$KNAP/$it # >&- 2>&- + then echo "$it: taken." + kn="$it $kn" + obs=`ash_rm "$obs" "$it"` + else echo "The $it is too heavy." + fi + fi + else echo "I see no $it here." + fi + done + else echo 'Get what?' + fi + ;; + gripe|bug) echo 'Please describe the problem and your situation at the time it failed.\nEnd the bug report with a line containing just a Ctrl-D.' + cat | mail $MAINT -s 'ash bug' + echo 'Thank you!' + ;; + help) ash_help + ;; + inventory|i) if [ "$kn" ] + then echo 'Your knapsack contains:' + ash_pr $kn + else echo 'You are poverty-stricken.' + fi + ;; + kill|destroy) if [ "$obj" ] + then if [ $obj = all ] + then x= + if ask "Do you really want to attempt to $verb them all?" + then obj=`echo $obs` + else echo 'Chicken!' + obj= + fi + fi + for it in $obj $x + do if ash_lk "$obs $hobs" "$it" + then if mv $it $HOME/$LIM # <&- >&- 2>&- + then if [ $verb = kill ] + then echo "The $it cannot defend himself; he dies." + else echo "You have destroyed the $it; it vanishes." + fi + obs=`ash_rm "$obs" "$it"` + else if [ $verb = kill ] + then echo "Your feeble blows are no match for the $it." + else echo "The $it is indestructible." + fi + fi + else if ash_lk "$kn" "$it" + then echo "You must drop the $it first." + found=false + else echo "I see no $it here." + fi + fi + done + else echo 'Kill what?' + fi + ;; + look|l) obs=`echo $obs $hobs` + hobs= + if [ "$obs" ] + then echo 'The room contains:' + ash_pr $obs + else echo 'The room is empty.' + fi + exs=`echo $exs $hexs` + hexs= + if [ "$exs" ] + then echo 'There are exits plainly labeled:' + ash_pr $exs + echo 'and a passage directly overhead.' + else echo 'The only exit is directly overhead.' + fi + ;; + magic) if [ "$obj" = mode ] + then if sh -c $cha + then echo 'You had your chance and you blew it.' + else if ask 'Are you a wizard?' + then echo -n 'Prove it! Say the magic word: ' + read obj + if [ "$obj" = armadillo ] + then echo 'Yes, master!!' + wiz=true + else echo "Homie says: I don't think so" + cha=true + fi + else echo "I didn't think so." + fi + fi + else echo 'Nice try.' + fi + ;; + open|read) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then if [ -r $obj ] + then if [ -s $obj ] + then echo "Opening the $obj reveals:" + $CAT < $obj + if [ $? != 0 ] + then echo '-- oops, you lost the contents!' + fi + else echo "There is nothing inside the $obj." + fi + else echo "You do not have the proper tools to open the $obj." + fi + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + found=false + else echo "I see no $obj here." + fi + fi + else echo 'Open what?' + fi + ;; + quit|exit) if ask 'Do you really want to quit now?' + then if [ "$kn" ] + then echo 'The contents of your knapsack will still be there next time.' + fi + rm -rf $HOME/$LIM + echo 'See you later!' + exit 0 + fi + ;; + resurrect) if [ "$obj" ] + then for it in $obj $x + do if ash_lk "$obs $hobs" "$it" + then echo "The $it is already alive and well." + else if mv $HOME/$LIM/$it $it # <&- >&- 2>&- + then echo "The $it staggers to his feet." + obs=`echo $it $obs` + else echo "There are sparks but no $it appears." + fi + fi + done + else echo 'Resurrect what?' + fi + ;; + steal) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then echo 'There is already one here.' + else set -- $x + case "$1" in + from) if [ "$2" ] + then shift + if PATH=$OPATH $* >$obj 2>/dev/null + then echo "The $1 monster drops the $obj." + obs=`echo $obj $obs` + else echo "The $1 monster runs away as you approach." + rm -f $obj # >&- 2>&- + fi + else echo 'From what?' + fi + ;; + *) echo "Steal $obj from what?" + ;; + esac + fi + else echo 'Steal what?' + fi + ;; + throw) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then set -- $x + case "$1" in + at) case "$2" in + daemon) if sh -c "lpr -r $obj" + then echo "The daemon catches the $obj, turns it into paper,\nand leaves it in the basket." + obs=`ash_rm "$obs" "$obj"` + else echo "The daemon is nowhere to be found." + fi + ;; + *) echo 'At what?' + ;; + esac + ;; + *) echo "Throw $obj at what?" + ;; + esac + else if ash_lk "$kn" "$obj" + then echo 'It is in your knapsack.' + found=false + else echo "I see no $obj here." + fi + fi + else echo 'Throw what?' + fi + ;; + u|up) if cd .. + then echo 'You pull yourself up a level.' + else echo "You can't reach that high." + fi + ;; + wake) if [ "$obj" ] + then echo "You awaken the $obj monster:" + PATH=$OPATH $obj $x + echo 'The monster slithers back into the darkness.' + else echo 'Wake what?' + fi + ;; + w|where) echo "You are in $room." + ;; + xyzzy) if cd + then echo 'A strange feeling comes over you.' + else echo 'Your spell fizzles out.' + fi + ;; + *) if [ "$verb" ] + then if sh -c $wiz + then PATH=$OPATH $verb $obj $x + else echo "I don't know how to \"$verb\"." + echo 'Type "help" for assistance.' + fi + else echo 'Say something!' + fi + ;; + esac +done diff --git a/examples/scripts/bcsh.sh b/examples/scripts/bcsh.sh new file mode 100755 index 0000000..b810cab --- /dev/null +++ b/examples/scripts/bcsh.sh @@ -0,0 +1,1254 @@ +# 1-Feb-86 09:37:35-MST,30567;000000000001 +# Return-Path: <unix-sources-request@BRL.ARPA> +# Received: from BRL-TGR.ARPA by SIMTEL20.ARPA with TCP; Sat 1 Feb 86 09:36:16-MST +# Received: from usenet by TGR.BRL.ARPA id a002623; 1 Feb 86 9:33 EST +# From: chris <chris@globetek.uucp> +# Newsgroups: net.sources +# Subject: Improved Bcsh (Bourne Shell Cshell-Emulator) +# Message-ID: <219@globetek.UUCP> +# Date: 30 Jan 86 17:34:26 GMT +# To: unix-sources@BRL-TGR.ARPA +# +# This is a new, improved version of my Bourne shell cshell-emulator. +# The code has been cleaned up quite a bit, and a couple of new features +# added (now supports 'noclobber' and 'iclobber' variables). A bug with +# 'eval' that caused "illegal I/O" error messages on vanilla V7 shells has +# also been fixed. + +# I have posted the program in its entirety because a context diff of the +# old and new versions was longer than the new version... + +# --Chris +# Bcsh -- A Simple Cshell-Like Command Pre-Processor For The Bourne Shell +# +# "Copyright (c) Chris Robertson, December 1985" +# +# This software may be used for any purpose provided the original +# copyright notice and this notice are affixed thereto. No warranties of +# any kind whatsoever are provided with this software, and it is hereby +# understood that the author is not liable for any damagages arising +# from the use of this software. +# +# Features Which the Cshell Does Not Have: +# ---------------------------------------- +# +# + command history persists across bcsh sessions +# + global last-command editing via 'g^string1^string2^' syntax +# + edit any command via $EDITOR or $VISUAL editors +# + history file name, .bcshrc file name, alias file name, and number +# of commands saved on termination can be set by environment variables +# + prompt may evaluate commands, such as `pwd`, `date`, etc. +# + the whole text of interactive 'for' and 'while' loops and 'if' +# statements goes into the history list and may be re-run or edited +# + multiple copies of commands and requests to see command history +# are not added to the history list +# + the history mechanism actually stores all commands entered in a +# current session, not just $history of them. This means that you +# can increase $history on the fly and at once have a larger history. +# +# +# Synonyms: +# --------- +# +# logout, exit, bye write out history file and exit +# h, history show current history list +# +# +# Aliases: +# -------- +# +# alias NAME CMND create an alias called NAME to run CMND +# unalias NAME remove the alias NAME +# +# There are no 'current-session only' aliases -- all alias and unalias +# commands are permanent, and stored in the $aliasfile. +# +# If an alias contains positional variables -- $1, $2, $*, etc. -- any +# arguments following the alias name are considered to be values for +# those variables, and the alias is turned into a command of the form +# 'set - arguments;alias'. Otherwise, a simple substitution is performed +# for the alias and the rest of the command preserved. The cshell +# convention of using '\!:n' in an alias to get bits of the current +# command is mercifully abandoned. +# +# Quotes are not necessary around the commands comprising an alias; +# in fact, any enclosing quotes are stripped when the alias is added +# to the file. +# +# A couple of typical aliases might be: +# +# goto cd $1;pwd +# l ls -F +# +# Note that aliasing something to "commands;logout" will not work -- if +# you want something to happen routinely on logout put it in the file +# specified by $logoutfile, default = $HOME/.blogout. +# +# +# Command Substitutions: +# ---------------------- +# +# !! substitute last command from history list +# !!:N substitute Nth element of last command from +# history list -- 0 = command name, 1 = 1st arg +# !!:$ substitute last element of last command from +# history list +# !!:* substitute all arguments to last command +# from history list +# !NUMBER substitute command NUMBER from the history list +# !NUMBER:N as above, but substitute Nth element, where +# 0 = command name, 1 = 1st arg, etc. +# !NUMBER:$ as above, but substitute last element +# !NUMBER:* as above, but substitute all arguments +# !-NUMBER substitute the command NUMBER lines from the +# end of the history list; 1 = last command +# !-NUMBER:N as above, but substitute Nth element, where +# 0 = command name, 1 = 1st arg, etc. +# !-NUMBER:$ as above, but substitute last element +# !-NUMBER:* as above, but substitute all arguments +# !?STRING substitute most-recent command from history list +# containing STRING -- STRING must be enclosed in +# braces if followed by any other characters +# !?STRING:N as above, but substitute Nth element, where +# 0 = command name, 1 = 1st arg, etc. +# !?STRING:$ as above, but substitute last element +# !?STRING:* as above, but substitute all arguments +# +# +# Command Editing: +# ---------------- +# +# CMND~e edit CMND using $EDITOR, where CMND may be found +# using a history substitution +# CMND~v edit CMND using $VISUAL, where CMND may be found +# using a history substitution +# " ^string1^string2^ substitute string2 for string1 in last command" +# command and run it +# " g^string1^string2^ globally substitute string2 for string1 in " +# last command and run it +# !NUMBER:s/string1/string2/ +# substitute string2 for string1 in +# command NUMBER and run it +# !NUMBER:gs/string1/string2/ +# globally substitute string2 for string1 in +# command NUMBER and run it +# !?STRING:s/string1/string2/ +# substitute string2 for string1 in last command +# containing STRING and run it +# !?STRING:gs/string1/string2/ +# globally substitute string2 for string1 in last +# command containing STRING and run it +# +# Any command which ends in the string ":p" is treated as a normal +# command until all substitutions have been completed. The trailing +# ":p" is then stripped, and the command is simply echoed and added to +# the history list instead of being executed. +# +# None of the other colon extensions of the cshell are supported. +# +# +# Shell Environment Variables: +# ---------------------------- +# +# EDITOR editor used by ~e command, default = "ed" +# VISUAL editor used by ~v command, default = "vi" +# MAIL your system mailbox +# PAGER paging program used by history command, default = "more" +# PS1 primary prompt +# PS2 secondary prompt +# history number of commands in history list, default = 22 +# histfile file history list is saved in, default = $HOME/.bhistory +# savehist number of commands remembered from last bcsh session +# aliasfile file of aliased commands, default = $HOME/.baliases +# logoutfile file of commands to be executed before termination +# inc_cmdno yes/no -- keep track of command numbers or not +# noclobber if set, existing files are not overwritten by '>' +# iclobber if both noclobber and iclobber are set, the user is +# prompted for confirmation before existing files are +# overwritten by '>' +# +# Note: if you are setting either noclobber or iclobber mid-session, +# set them to 'yes' +# +# +# Regular Shell Variables: +# ------------------------ +# +# Shell variables may be set via Bourne or cshell syntax, e.g., both +# "set foo=bar" and "foo=bar" set a variable called "foo" with the value +# "bar". However, all variables are automatically set as environment +# variables, so there is no need to export them. Conversely, there +# are NO local variables. Sorry, folks. +# +# A cshell-style "setenv" command is turned into a regular "set" command. +# +# +# The Prompt: +# ---------- +# +# You may, if you wish, have a command executed in your prompt. If +# the variable PS1 contains a dollar sign or a backquote, it is +# evaluated and the result used as the prompt, provided the evaluation +# did not produce a "not found" error message. The two special cases +# of PS1 consisting solely of "$" or "$ " are handled correctly. For +# example, to have the prompt contain the current directory followed +# by a space, enter: +# +# PS1=\'echo "`pwd` "\' +# +# You need the backslashed single quotes to prevent the command being +# evaluated by the variable-setting mechanism and the shell before it +# is assigned to PS1. +# +# To include the command number in your prompt, enter the command: +# +# PS1=\'echo "$cmdno "\' +# +# +# Shell Control-Flow Syntax: +# -------------------------- +# +# 'While', 'for', 'case', and 'if' commands entered in Bourne shell +# syntax are executed as normal. +# +# A valiant attempt is made to convert 'foreach' loops into 'for' loops, +# cshell-syntax 'while' loops into Bourne shell syntax, and 'switch' +# statements into 'case' statements. I cannot guarantee to always get it +# right. If you forget the 'do' in a 'while' or 'for' loop, or finish +# them with 'end' instead of 'done', this will be corrected. +# +# Note that cshell-to-Bourne control flow conversions do not take place +# if control is nested -- e.g., a 'foreach' inside a 'while' will fail. +# +# The simple-case cshell "if (condition) command" is turned into Bourne +# syntax. Other 'if' statements are left alone apart from making the +# 'then' a separate statement, because constructing a valid interactive +# cshell 'if' statement is essentially an exercise in frustration anyway. +# The cshell and Bourne shell have sufficiently different ideas about +# conditions that if is probably best to resign yourself to learning +# the Bourne shell conventions. +# +# Note that since most of the testing built-ins of the cshell are +# not available in the Bourne shell, a complex condition in a 'while' +# loop or an 'if' statement will probably fail. +# +# +# Bugs, Caveats, etc.: +# -------------------- +# +# This is not a super-speedy program. Be patient, especially on startup. +# +# To the best of my knowledge this program should work on ANY Bourne +# shell -- note that if your shell does not understand 'echo -n' you +# will have to re-set the values of '$n' and '$c'. +# +# This program may run out of stack space on a 16-bit machine where +# /bin/sh is not split-space. +# +# Mail checking is done every 10 commands if $MAIL is set in your +# environment. For anything fancier, you will have to hack the code. +# +# Because commands are stuffed in a file before sh is invoked on them, +# error messages from failed commands are ugly. +# +# Failed history substitutions either give nothing at all, or a +# "not found" style of error message. +# +# A command history is kept whether you want it or not. This may be +# perceived as a bug or a feature, depending on which side of bed you +# got out on. +# +# If you want a real backslash in a command, you will have to type two +# of them because the shell swallows the first backslash in the initial +# command pickup. This means that to include a non-history '!' in a +# command you need '\\!' -- a real wart, especially for net mail, +# but unavoidable. +# +# Commands containing an '@' will break all sorts of things. +# +# Very complex history substitutions may fail. +# +# File names containing numbers may break numeric history sustitutions. +# +# Commands containing bizzare sequences of characters may conflict +# with internal kludges. +# +# Aliasing something to "commands;logout" will not work -- if you +# want something to happen routinely on logout, put it in the file +# specified by $logoutfile, default = $HOME/.blogout. +# +# Please send all bug reports to ihnp4!utzoo!globetek!chris. +# Flames will be posted to net.general with 'Reply-to' set to your +# ' path... :-) ' +# +# +# +# ************* VERY IMPORTANT NOTICE ************* +# +# If your shell supports # comments, then REPLACE all the colon 'comments' +# with # comments. If it does not, then REMOVE all the 'comment' lines from the +# working copy of the file, as it will run MUCH faster -- the shell evaluates +# lines starting with a colon but does not actually execute them, so you will +# save the read-and-evaluate time by removing them. + +case "`echo -n foo`" in + -n*) + n= + c="\c" + ;; + foo) + n=-n + c= + ;; + *) + echo "Your 'echo' command is broken." + exit 1 + ;; +esac +history=${history-22} +savehist=${savehist-22} +histfile=${histfile-$HOME/.bhistory} +logoutfile=${logoutfile-$HOME/.blogout} +EDITOR=${EDITOR-ed} +VISUAL=${VISUAL-vi} +PAGER=${PAGER-more} + +aliasfile=${aliasfile-$HOME/.baliases} + +# the alias file may contain 1 blank line, so a test -s will not work + +case "`cat $aliasfile 2> /dev/null`" in + "") + doalias=no + ;; + *) + doalias=yes + ;; +esac + +if test -s "${sourcefile-$HOME/.bcshrc}" + then + . ${sourcefile-$HOME/.bcshrc} +fi + +if test -s "$histfile" + then + cmdno="`set - \`wc -l $histfile\`;echo $1`" + cmdno="`expr \"$cmdno\" + 1`" + lastcmd="`sed -n '$p' $histfile`" + copy=false + ohist=$histfile + while test ! -w "$histfile" + do + echo "Cannot write to history file '$histfile'." + echo $n "Please enter a new history filename: $c" + read histfile + copy=true + done + if $copy + then + cp $ohist $histfile + fi +else + cat /dev/null > $histfile + cmdno=1 + lastcmd= +fi + +# keep track of command number as the default + +inc_cmdno=${inc_cmdo-yes} + +# default prompts -- PS1 and PS2 may be SET but EMPTY, so '${PS1-% }' syntax +# is not used here + +case "$PS1" in + "") + PS1="% " + ;; +esac +case "$PS2" in + "") + PS2="> " + ;; +esac + +export histfile savehist history aliasfile EDITOR VISUAL PAGER cmdno PS1 PS2 + +case "$MAIL" in + "") + ;; + *) + if [ -f $MAIL ]; then + mailsize=`set - \`wc -c $MAIL\`;echo $1` + else + mailsize=0 + fi + ;; +esac + +trap ':' 2 +trap exit 3 +trap "tail -n $savehist $histfile>/tmp/hist$$;uniq /tmp/hist$$ > $histfile;\ +rm -f /tmp/*$$;exit 0" 15 + +getcmd=yes +mailcheck= +exclaim= +echoit= +mailprompt= + +while : +do + + run=yes + case "$mailprompt" in + "") + ;; + *) + echo "$mailprompt" + ;; + esac + case "$getcmd" in + yes) + : guess if the prompt should be evaluated or not + case "$PS1" in + \$|\$\ ) + echo $n "$PS1$c" + ;; + *\`*|*\$*) + tmp="`(eval $PS1) 2>&1`" + case "$tmp" in + *not\ found) + echo $n "$PS1$c" + ;; + *) + echo $n "$tmp$c" + ;; + esac + ;; + *) + echo $n "$PS1$c" + ;; + esac + + read cmd || cmd="exit" + ;; + *) ;; + esac + + case "$MAIL" in + "") + ;; + *) + : check for mail every 10 commands + case "$mailcheck" in + 1111111111) + mailcheck= + if [ -f $MAIL ]; then + newsize="`set - \`wc -c $MAIL\`;echo $1`" + else + newsize=0 + fi + if test "$newsize" -gt "$mailsize"; then + mailprompt="You have new mail" + else + mailprompt= + fi + mailsize=$newsize + ;; + *) + mailcheck=1$mailcheck + ;; + esac + ;; + esac + hist=no + + case "$cmd" in + "") + continue + ;; + sh) + sh + run=no + ;; + !!) + cmd=$lastcmd + echoit=yes + getcmd=no + continue + ;; + *:p) + cmd="`expr \"$cmd\" : '\(.*\):p'` +~+p" + getcmd=no + continue + ;; + foreach[\ \ ]*) + while test "$line" != "end"; do + echo $n "$PS2$c" + read line + cmd="${cmd};$line" + done + echo "$cmd" > /tmp/bcsh$$ + ed - /tmp/bcsh$$ << ++++ + s/end/done/ + s/foreach[ ]\(.*\)(/for \1 in / + s/)// + s/;/;do / + w +++++ + ;; + for[\ \ ]*|while[\ \ ]*) + # try to catch the most common cshell-to-Bourne-shell + # mistakes + + echo $n "$PS2$c" + read line + case "$line" in + *do) + line="do :" + ;; + *do*) + ;; + *) + line="do $line" + ;; + esac + + cmd="${cmd};$line" + while test "$line" != "done" && test "$line" != "end" + do + echo $n "$PS2$c" + read line + case "$line" in + end) + line=done + ;; + esac + cmd="${cmd};$line" + done + echo "$cmd" > /tmp/bcsh$$ + ;; + if[\ \ ]*) + while test "$line" != "fi" && test "$line" != "endif" + do + echo $n "$PS2$c" + read line + case "$line" in + *[a-z]*then) + line="`expr \"$line\" : '\(.*\)then'`;then" + ;; + endif) + line=fi + ;; + esac + cmd="${cmd};$line" + done + echo "$cmd" > /tmp/bcsh$$ + case "`grep then /tmp/bcsh$$`" in + "") + # fix 'if foo bar' cases + + ed - /tmp/bcsh$$ << ++++ + s/)/);then/ + s/.*/;fi/ + w +++++ + ;; + esac + ;; + case[\ \ ]*) + while test "$line" != "esac" + do + echo $n "$PS2$c" + read line + cmd="${cmd}@$line" + done + cmd="`echo \"$cmd\" | tr '@' ' '`" + echo "$cmd" > /tmp/bcsh$$ + ;; + switch[\ \ ]*) + while test "$line" != "endsw" + do + echo $n "$PS2$c" + read line + cmd="${cmd}@$line" + done + echo "$cmd" > /tmp/bcsh$$ + ed - /tmp/bcsh$$ << '++++' + 1,$s/@/\ +/g + g/switch.*(/s//case "/ + s/)/" in/ + 1,$s/case[ ]\(.*\):$/;;\ + \1)/ + 2d + 1,$s/endsw/;;\ +esac/ + g/breaksw/s/// + 1,$s/default.*/;;\ + *)/ + w +++++ + cmd="`cat /tmp/bcsh$$`" + ;; + *!*) + hist=yes + ;; + esac + + case "$hist" in + yes) + # deal with genuine exclamation marks, go back and parse again + + case "$cmd" in + *\>![\ \ ]*|*\\!*) + cmd="`echo \"$cmd\" | sed -e 's@\\!@REALEXCLAMATIONMARK@g'`" + exclaim=yes + getcmd=no + continue + ;; + esac + + # break command into elements, parse each one + + tmp= + for i in $cmd + do + # find element with !, peel off stuff up to ! + + case "$i" in + !) + # most likely a typo for !!, so fix it + front= + $i=!! + ;; + !!*) + front= + i="`expr \"$i\" : '.*\(!!.*\)'`" + ;; + *!!*) + front="`expr \"$i\" : '\(.*\)!!.*'`" + i="`expr \"$i\" : '.*\(!!.*\)'`" + ;; + !*) + front= + i="`expr \"$i\" : '.*!\(.*\)'`" + ;; + *) + tmp="$tmp$i " + continue + ;; + esac + case "$i" in + !!*) + # want last command + + rest="`expr \"$i\" : '!!\(.*\)'`" + i=$lastcmd + ;; + -*) + # we want to search back through the history list + + case "$i" in + -) + rest="`expr \"$i\" : '-\(.*\)'`" + i=$lastcmd + ;; + -[0-9]*) + wanted="`expr \"$i\" : '-\([0-9][0-9]*\).*'`" + rest="`expr \"$i\" : '-[0-9][0-9]*\(.*\)'`" + i="`tail -n $wanted $histfile | sed -e "1q"`" + ;; + esac + ;; + [0-9]*) + # find which number command is wanted + + wanted="`expr \"$i\" : '\([0-9][0-9]*\).*'`" + rest="`expr \"$i\" : '[0-9][0-9]*\(.*\)'`" + i="`grep -n . $histfile | grep \"^$wanted\"`" + i="`expr \"$i\" : \"${wanted}.\(.*\)\"`" + ;; + \?*) + + # find which 'command-contains' match is wanted + + case "$i" in + \?{*}*) + wanted="`expr \"$i\" : '?{\(.*\)}.*'`" + rest="`expr \"$i\" : '?.*}\(.*\)'`" + ;; + \?*:*) + wanted="`expr \"$i\" : '?\(.*\):.*'`" + rest="`expr \"$i\" : '?.*\(:.*\)'`" + ;; + \?*) + wanted="`expr \"$i\" : '?\(.*\)'`" + rest= + ;; + esac + i="`grep \"$wanted\" $histfile | sed -n '$p'`" + ;; + *) + # find which 'start-of-command' match is wanted + + case "$i" in + {*}*) + wanted="`expr \"$i\" : '{\(.*\)}.*'`" + rest="`expr \"$i\" : '.*}\(.*\)'`" + ;; + *:*) + wanted="`expr \"$i\" : '\(.*\):.*'`" + rest="`expr \"$i\" : '.*\(:.*\)'`" + ;; + *) + wanted="$i" + rest= + ;; + esac + i="`grep \"^$wanted\" $histfile | sed -n '$p'`" + ;; + esac + + # see if we actually found anything to substitute + + case "$i" in + "") + badsub="Event not found" + break + ;; + *) + badsub=no + ;; + esac + + case "$rest" in + "") + tmp="$front$tmp$i " + continue + ;; + :[0-9]*) + # find which element of $i is wanted + + number="`expr \"$rest\" : ':\([0-9][0-9]*\).*'`" + rest="`expr \"$rest\" : ':[0-9][0-9]*\(.*\)'`" + + # count through $i till we get to the + # right element + + counter=0 + for element in $i + do + case "$counter" in + $number) + break + ;; + *) + counter="`expr \"$counter\" + 1`" + # counter=$[ $counter + 1 ] + ;; + esac + done + case "$counter" in + $number) + badsub=no + ;; + *) + badsub="Bad command element" + break + ;; + esac + tmp="$tmp$front$element$rest " + continue + ;; + :\$*) + # spin through $i till we hit the last element + + rest="`expr \"$rest\" : ':\$\(.*\)'`" + for element in $i + do + : + done + tmp="$tmp$front$element$rest " + continue + ;; + :\**) + # we want all elements except the command itself + + rest="`expr \"$rest\" : ':\*\(.*\)'`" + save=$i + set - $i + shift + case "$*" in + "") + badsub="No arguments to command '$save'" + break + ;; + *) + badsub=no + ;; + esac + tmp="$tmp$front$*$rest " + continue + ;; + :s*|:gs*) + # we are doing a substitution + # put / on end if needed + + case "$rest" in + :s/*/*/*|:gs/*/*/*) + ;; + :s/*/*|:gs/*/*) + rest="${rest}/" + ;; + esac + + # find what substitution is wanted + + first="`expr \"$rest\" : ':*s\/\(.*\)\/.*\/.*'`" + second="`expr \"$i\" : ':*s/.*/\(.*\)/.*'`" + + # see if it is a global substitution + + case "$rest" in + :gs*) + global=g + ;; + :s*) + global= + ;; + esac + rest="`expr \"$rest\" : '.*/.*/.*/\(.*\)'`" + i="`echo \"$i\" | sed -e \"s@$first@$second@$global\"`" + + # see if subsitution worked + + case "$i" in + "") + badsub="Substiution failed" + break + ;; + *) + badsub=no + ;; + esac + tmp="$tmp$front$i$rest " + continue + ;; + *) + tmp="$tmp$front$i$rest " + ;; + esac + done + case "$badsub" in + no) + ;; + *) + echo "$badsub" + badsub=no + continue + ;; + esac + cmd="$tmp" + echoit=yes + getcmd=no + continue + ;; + *) + run=yes + ;; + esac + + case "$cmd" in + *\^*\^*\^*) + # see if the substitution is global + case "$cmd" in + g*) + global=g + ;; + *) + global= + ;; + esac + + # put a '^' on the end if necessary + case "$cmd" in + *\^) + ;; + *) + cmd="${cmd}^" + ;; + esac + + # find what substitution is wanted + + first="`expr \"$cmd\" : '*\^\(.*\)\^.*\^.*'`" + second="`expr \"$cmd\" : '*\^.*\^\(.*\)\^.*'`" + rest="`expr \"$cmd\" : '*\^.*\^.*\^\(.*\)'`" + cmd="`echo \"$lastcmd\" | sed -e \"s@$first@$second@$global\"`$rest" + + # see if the substitution worked + + case "$cmd" in + "") + echo "Substitution failed" + continue + ;; + esac + echoit=yes + getcmd=no + continue + ;; + *~e) + echo "$cmd" | sed -e "s@~e@@" > /tmp/bcsh$$ + $EDITOR /tmp/bcsh$$ + cmd="`cat /tmp/bcsh$$`" + getcmd=no + continue + ;; + *~v) + echo "$cmd" | sed -e "s@~v@@" > /tmp/bcsh$$ + echo "$lastcmd" > /tmp/bcsh$$ + $VISUAL /tmp/bcsh$$ + cmd="`cat /tmp/bcsh$$`" + getcmd=no + continue + ;; + exec[\ \ ]*) + tail -n $savehist $histfile>/tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + echo $cmd > /tmp/cmd$$ + . /tmp/cmd$$ + ;; + login[\ \ ]*|newgrp[\ \ ]*) + tail -n $savehist $histfile>/tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + echo $cmd > /tmp/cmd$$ + . /tmp/cmd$$ + ;; + logout|exit|bye) + if test -s "$logoutfile" + then + # sh $logoutfile + $SHELL $logoutfile + fi + tail -n $savehist $histfile > /tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + exit 0 + ;; + h|history) + grep -n . $histfile | tail -n $history | sed -e 's@:@ @' | $PAGER + continue + ;; + h[\ \ ]\|*|h[\ \ ]\>*|h\|*|h\>*) + cmd="`echo \"$cmd\" | sed -e \"s@h@grep -n . $histfile | tail -n $history | sed -e 's@:@ @'@\"`" + getcmd=no + continue + ;; + history[\ \ ]*\|*|history[\ \ ]*\>*) + cmd="`echo \"$cmd\" | sed -e \"s@history@grep -n . $histfile | tail -n $history | sed -e 's@:@ @'@\"`" + getcmd=no + continue + ;; + source[\ \ ]*) + set - $cmd + shift + echo . $* > /tmp/cmd$$ + . /tmp/cmd$$ + run=no + ;; + wait) + wait + run=no + ;; + .[\ \ ]*) + echo $cmd > /tmp/cmd$$ + . /tmp/cmd$$ + run=no + ;; + cd|cd[\ \ ]*) + # check if it will work first, or else this shell will terminate + # if the cd dies. If you have a built-in test, you might want + # to replace the try-it-and-see below with a couple of tests, + # but it is probably just as fast like this. + + echo $cmd > /tmp/cmd$$ + if ($SHELL /tmp/cmd$$) ; then + . /tmp/cmd$$ + fi + run=no + ;; + awk[\ \ ]*|dd[\ \ ]*|cc[\ \ ]*|make[\ \ ]*) + # these are the only commands I can think of whose syntax + # includes an equals sign. Add others as you find them. + + echo "$cmd" > /tmp/bcsh$$ + ;; + setenv*|*=*) + # handle setting shell variables, turning cshell syntax to Bourne + # syntax -- note all variables must be exported or they will not + # be usable in other commands + + echo "$cmd" > /tmp/cmd$$ + ed - /tmp/cmd$$ << ++++ + g/^setenv[ ]/s/[ ]/@/ + g/^setenv@/s/[ ]/=/ + g/^setenv@/s/// + g/^set/s/// + .t. + \$s/=.*// + s/^/export / + w +++++ + . /tmp/cmd$$ + rm -f /tmp/cmd$$ + run=no + ;; + unset[\ \ ]*|umask[\ \ ]*|export[\ \ ]*|set[\ \ ]*) + # handle commands which twiddle current environment + + $cmd + run=no + ;; + alias|alias[\ \ ]) + if [ -f $aliasfile ]; then + $PAGER $aliasfile + fi + lastcmd=$cmd + run=no + continue + ;; + alias[\ \ ]*) + case "$cmd" in + alias[\ \ ]\|*|alias[\ \ ]\>*) + cmd="`echo \"$cmd\" | sed -e \"s@alias@cat $aliasfile@\"`" + getcmd=no + continue + ;; + alias[\ \ ]*[\ \ ]*) + ;; + *) + echo "Syntax: alias name command" + cmd= + continue + ;; + esac + set - $cmd + shift + cmd="$*" + + # make sure there is always 1 blank line in file so + # unaliasing will always work -- ed normally refuses + # to write an empty file + echo "" >> $aliasfile + cat << ++++ >> $aliasfile +$cmd +++++ + +# ed - $aliasfile << '++++' +# g/alias[ ]/s/// +# g/^['"]\(.*\)['"]$/s//\1/ +# g/^/s//alias / +# w +#++++ + + sort -u -o $aliasfile $aliasfile + doalias=yes + cmd="alias $cmd" + run=no + ;; + unalias[\ \ ]*) + set - $cmd + case "$#" in + 2) + cmd=$2 + ;; + *) + echo "Syntax: unalias alias_name" + continue + ;; + esac + ed - $aliasfile << ++++ + /^$cmd[ ]/d + w +++++ + case "`set - \`wc -l $aliasfile\`;echo $1`" in + 1) + # just removed last alias + doalias=no + ;; + esac + run=no + ;; + *) + case "$doalias" in + yes) + set - $cmd + tmp="`grep \"^$1 \" $aliasfile`" + case "$tmp" in + $1[\ \ ]*) + shift + cmd=$* + set - $tmp + shift + tmp=$* + case "$tmp" in + *\$*) + # uses positional variables + + cmd="set - $cmd ; $tmp" + getcmd=no + continue + ;; + *) + cmd="$tmp $cmd" + getcmd=no + continue + ;; + esac + ;; + *) + echo "$cmd" > /tmp/bcsh$$ + ;; + esac + ;; + no) + echo "$cmd" > /tmp/bcsh$$ + ;; + esac + ;; + esac + + case "$cmd" in + *+~+p) + cmd="`expr \"$cmd\" : '\(.*\)+~+p'`" + echoit=yes + run=no + ;; + esac + + case "$cmd" in + "") + continue + ;; + *) + case "$exclaim" in + yes) + cmd="`echo \"$cmd\" | sed -e 's@REALEXCLAMATIONMARK@!@g'`" + echo "$cmd" > /tmp/bcsh$$ + ;; + esac + case "$echoit" in + yes) + echo $cmd + ;; + esac + case "$run" in + yes) + case "${noclobber+yes}" in + yes) + case "$cmd" in + *\>![\ \ ]*) + ed - /tmp/bcsh$$ << ++++ + g/>!/s//>/ + w +++++ + ;; + *\>\>*) + ;; + *\>*) + outfile="`expr \"$cmd\" : '.*>\(.*\)'`" + case "$outfile" in + \&*) + ;; + *) + set - $outfile + outfile="$1" + if test -s "$outfile" + then + case "${iclobber+yes}" in + yes) + echo $n "Overwrite ${outfile}? $c" + read answer + case "$answer" in + y*) + ;; + *) + echo ':' > /tmp/bcsh$$ + ;; + esac + ;; + *) + echo "${outfile}: file exists" + echo ':' > /tmp/bcsh$$ + ;; + esac + fi + ;; + esac + ;; + esac + ;; + *) + case "$cmd" in + *\>![\ \ ]*) + ed - /tmp/bcsh$$ << ++++ + g/>!/s//>/g + w +++++ + ;; + esac + ;; + esac + (trap 'exit 1' 2 3; $BASH /tmp/bcsh$$) + ;; + esac + case "$cmd" in + $lastcmd) + ;; + *) + case "$exclaim" in + yes) + cmd="`echo \"$cmd\" | sed -e 's@!@\\\\!@g'`" + ;; + esac + + cat << ++++ >> $histfile +$cmd +++++ + lastcmd=$cmd + + case "$inc_cmdno" in + yes) + cmdno="`expr \"$cmdno\" + 1`" + # cmdno=$[$cmdno + 1] + ;; + esac + ;; + esac + ;; + esac + + # The next commented-out line sets the prompt to include the command + # number -- you should only un-comment this if it is the ONLY thing + # you ever want as your prompt, because it will override attempts + # to set PS1 from the command level. If you want the command number + # in your prompt without sacrificing the ability to change the prompt + # later, replace the default setting for PS1 before the beginning of + # the main loop with the following: PS1='echo -n "${cmdno}% "' + # Doing it this way is, however, slower than the simple version below. + + PS1="${cmdno}% " + + getcmd=yes + echoit=no + exclaim=no +done +exit 0 + +# Christine Robertson {linus, ihnp4, decvax}!utzoo!globetek!chris diff --git a/examples/scripts/cat.sh b/examples/scripts/cat.sh new file mode 100644 index 0000000..78106b2 --- /dev/null +++ b/examples/scripts/cat.sh @@ -0,0 +1,12 @@ +shcat() +{ + while read -r ; do + echo "$REPLY" + done +} + +if [ -n "$1" ]; then + shcat < "$1" +else + shcat +fi diff --git a/examples/scripts/center b/examples/scripts/center new file mode 100644 index 0000000..8d367d3 --- /dev/null +++ b/examples/scripts/center @@ -0,0 +1,24 @@ +#! /bin/bash +# +# center - center a group of lines +# +# tabs in the lines might cause this to look a little bit off +# +# + +width=${COLUMNS:-80} + +if [[ $# == 0 ]] +then + set -- /dev/stdin +fi + +for file +do + while read -r + do + printf "%*s\n" $(( (width+${#REPLY})/2 )) "$REPLY" + done < $file +done + +exit 0 diff --git a/examples/scripts/dd-ex.sh b/examples/scripts/dd-ex.sh new file mode 100644 index 0000000..fafc83f --- /dev/null +++ b/examples/scripts/dd-ex.sh @@ -0,0 +1,476 @@ +#!/bin/sh + +# this is a line editor using only /bin/sh, /bin/dd and /bin/rm + +# /bin/rm is not really required, but it is nice to clean up temporary files + +PATH= +dd=/bin/dd +rm=/bin/rm + +# temporary files we might need +tmp=/tmp/silly.$$ +ed=/tmp/ed.$$ +trap "$rm -f $tmp $tmp.1 $tmp.2 $tmp.3 $tmp.4 $tmp.5 $tmp.6 $ed.a $ed.b $ed.c; exit" 0 1 2 3 + +# from now on, no more rm - the above trap is enough +unset rm + +# we do interesting things with IFS, but better save it... +saveIFS="$IFS" + +# in case "echo" is not a shell builtin... + +Echo () { +case "$1" in + -n) shift + $dd of=$tmp 2>/dev/null <<EOF +$@ +EOF + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null skip=1 2>&1` + IFS="$saveIFS" + $dd if=$tmp bs=1 count=$1 2>/dev/null + ;; + *) $dd 2>/dev/null <<EOF +$@ +EOF + ;; +esac +} + +# this is used to generate garbage files + +true () { + return 0 +} + +false () { + return 1 +} + +zero () { + ( trap 'go=false' 13 + go=true + while $go + do + $dd "if=$0" + case "$?" in + 0) ;; + *) go=false ;; + esac + done + ) 2>/dev/null +} + +# arithmetic using dd! + +# add variable n1 n2 n3... +# assigns n1+n2+n3+... to variable + +add () { + result="$1" + shift + $dd if=/dev/null of=$tmp bs=1 2>/dev/null + for n in "$@" + do + case "$n" in + 0) ;; + *) zero | $dd of=$tmp.1 bs=1 "count=$n" 2>/dev/null + ( $dd if=$tmp; $dd if=$tmp.1 ) 2>/dev/null | $dd of=$tmp.2 2>/dev/null + $dd if=$tmp.2 of=$tmp 2>/dev/null + ;; + esac + done + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null 2>&1` + IFS="$saveIFS" + eval $result='$1' +} + +# subtract variable n1 n2 +# subtracts n2 from n1, assigns result to variable + +subtract () { + result="$1" + zero | $dd of=$tmp bs=1 "count=$2" 2>/dev/null + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null "skip=$3" 2>&1` + IFS="$saveIFS" + case "$1" in + dd*) set 0 ;; + esac + eval $result='$1' +} + +# multiply variable n1 n2 +# variable = n1 * n2 + +multiply () { + result="$1" + zero | $dd "bs=$2" of=$tmp "count=$3" 2>/dev/null + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null 2>&1` + IFS="$saveIFS" + eval $result='$1' +} + +# divide variable n1 n2 +# variable = int( n1 / n2 ) + +divide () { + result="$1" + zero | $dd bs=1 of=$tmp "count=$2" 2>/dev/null + IFS="+" + set `$dd if=$tmp "bs=$3" of=/dev/null 2>&1` + IFS="$saveIFS" + eval $result='$1' +} + +# compare variable n1 n2 sets variable to lt if n1<n2, gt if n1>n2, eq if n1==n2 + +compare () { + res="$1" + n1="$2" + n2="$3" + subtract somename "$n1" "$n2" + case "$somename" in + 0) ;; + *) eval $res=gt; return; + esac + subtract somename "$n2" "$n1" + case "$somename" in + 0) ;; + *) eval $res=lt; return; + esac + eval $res=eq +} + +# lt n1 n2 returns true if n1 < n2 + +lt () { + n1="$1" + n2="$2" + subtract somename "$n2" "$n1" + case "$somename" in + 0) return 1 ;; + esac + return 0 +} + +# le n1 n2 returns true if n1 <= n2 + +le () { + n1="$1" + n2="$2" + subtract somename "$n1" "$n2" + case "$somename" in + 0) return 0 ;; + esac + return 1 +} + +# gt n1 n2 returns true if n1 > n2 + +gt () { + n1="$1" + n2="$2" + subtract somename "$n1" "$n2" + case "$somename" in + 0) return 1 ;; + esac + return 0 +} + +# ge n1 n2 returns true if n1 >= n2 + +ge () { + n1="$1" + n2="$2" + subtract somename "$n2" "$n1" + case "$somename" in + 0) return 0 ;; + esac + return 1 +} + +# useful functions for the line editor + +# open a file - copy it to the buffers + +open () { + file="$1" + set `$dd "if=$file" of=/dev/null 2>&1` + case "$1" in + dd*) return 1 + esac + # copy the first line to $ed.c + go=true + len=0 + while $go + do + case "`$dd "if=$file" bs=1 skip=$len count=1 2>/dev/null`" in + ?*) go=true ;; + *) go=false ;; + esac + add len 1 $len + done + # now $len is the length of the first line (including newline) + $dd "if=$file" bs=1 count=$len of=$ed.c 2>/dev/null + $dd "if=$file" bs=1 skip=$len of=$ed.b 2>/dev/null + $dd if=/dev/null of=$ed.a 2>/dev/null + lineno=1 +} + +# save a file - copy the buffers to the file + +save () { + # make a backup copy of the original + $dd "if=$1" "of=$1.bak" 2>/dev/null + # and save + ( $dd if=$ed.a; $dd if=$ed.c; $dd if=$ed.b ) > "$1" 2>/dev/null +} + +# replace n1 n2 bla replaces n2 chars of current line, starting n1-th + +replace () { + $dd if=$ed.c of=$tmp.1 bs=1 "count=$1" 2>/dev/null + ( $dd if=$ed.c "skip=$1" bs=1 | $dd of=$tmp.2 bs=1 "skip=$2" ) 2>/dev/null + shift + shift + ( $dd if=$tmp.1; Echo -n "$@"; $dd if=$tmp.2 ) > $tmp.3 2>/dev/null + $dd if=$tmp.3 of=$ed.c 2>/dev/null +} + +# rstring n s bla +# replace the n-th occurence of s with bla + +rstring () { + n="$1" + shift; + # first we have to find it - this is fun! + # we have $tmp.4 => text before string, $tmp.5 => text after + $dd if=/dev/null of=$tmp.4 2>/dev/null + $dd if=$ed.c of=$tmp.5 2>/dev/null + string="$1" + shift + $dd of=$tmp.6 2>/dev/null <<EOF +$@ +EOF + while : + do + case "`$dd if=$tmp.5 2>/dev/null`" in + $string*) + if lt $n 2 + then + # now we want to replace the string + Echo -n "$@" > $tmp.2 + Echo -n "$string" > $tmp.1 + IFS="+" + set `$dd bs=1 if=$tmp.1 of=/dev/null 2>&1` + IFS="$saveIFS" + slen=$1 + IFS="+" + ( $dd if=$tmp.4; $dd if=$tmp.2; $dd if=$tmp.5 bs=1 skip=$slen ) \ + 2>/dev/null > $tmp + $dd if=$tmp of=$ed.c 2>/dev/null + return 0 + else + subtract n $n 1 + ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null + $dd if=$tmp of=$tmp.4 2>/dev/null + # and remove it from $tmp.5 + $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null + $dd if=$tmp of=$tmp.5 2>/dev/null + fi + ;; + ?*) # add one more byte... + ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null + $dd if=$tmp of=$tmp.4 2>/dev/null + # and remove it from $tmp.5 + $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null + $dd if=$tmp of=$tmp.5 2>/dev/null + ;; + *) # not found + return 1 + ;; + esac + done +} + +# skip to next line +next () { + add l $lineno 1 + ( $dd if=$ed.a; $dd if=$ed.c ) 2>/dev/null > $tmp.3 + $dd if=$ed.b of=$tmp.4 2>/dev/null + open $tmp.4 + $dd if=$tmp.3 of=$ed.a 2>/dev/null + lineno=$l +} + +# delete current line +delete () { + l=$lineno + $dd if=$ed.a 2>/dev/null > $tmp.1 + $dd if=$ed.b of=$tmp.2 2>/dev/null + open $tmp.2 + $dd if=$tmp.1 of=$ed.a 2>/dev/null + lineno=$l +} + +# insert before current line (without changing current) +insert () { + ( $dd if=$ed.a; Echo "$@" ) 2>/dev/null > $tmp.1 + $dd if=$tmp.1 of=$ed.a 2>/dev/null + add lineno $lineno 1 +} + +# previous line +prev () { + case "$lineno" in + 1) ;; + *) subtract lineno $lineno 1 + # read last line of $ed.a + IFS='+' + set `$dd if=$ed.a of=/dev/null bs=1 2>&1` + IFS="$saveIFS" + size=$1 + # empty? + case "$size" in + 0) return ;; + esac + subtract size $size 1 + # skip final newline + case "$size" in + 0) ;; + *) subtract size1 $size 1 + case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in + ?*) ;; + *) size=$size1 ;; + esac + ;; + esac + go=true + while $go + do + case "$size" in + 0) go=false ;; + *) case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in + ?*) go=true; subtract size $size 1 ;; + *) go=false; add size $size 1 ;; + esac + ;; + esac + done + # now $size is the size of the first n-1 lines + # add $ed.c to $ed.b + ( $dd if=$ed.c; $dd if=$ed.b ) 2>/dev/null > $tmp.5 + $dd if=$tmp.5 of=$ed.b 2>/dev/null + # move line to ed.c + case "$size" in + 0) $dd if=$ed.a of=$ed.c 2>/dev/null + $dd if=/dev/null of=$tmp.5 2>/dev/null + ;; + *) $dd if=$ed.a of=$ed.c bs=1 skip=$size 2>/dev/null + $dd if=$ed.a of=$tmp.5 bs=1 count=$size 2>/dev/null + ;; + esac + # move rest to ed.a + $dd if=$tmp.5 of=$ed.a 2>/dev/null + ;; + esac +} + +# goes to a given line +goto () { + rl="$1" + compare bla "$rl" $lineno + case "$bla" in + eq) return + ;; + gt) while gt "$rl" $lineno + do + next + done + ;; + lt) while lt "$rl" $lineno + do + prev + done + ;; + esac +} + +lineout () { + Echo -n "$lineno: " + $dd if=$ed.c 2>/dev/null +} + +state=closed +name= +autoprint=true + +while true +do + Echo -n '> ' + read cmd arg + case "$cmd:$state" in + open:open) Echo "There is a file open already" ;; + open:*) if open "$arg" + then state=open; name="$arg"; $autoprint + else Echo "Cannot open $arg" + fi + ;; + new:open) Echo "There is a file open already" ;; + new:*) open "$arg" + state=open + name="$arg" + $autoprint + ;; + close:changed) Echo "Use 'discard' or 'save'" ;; + close:closed) Echo "Closed already" ;; + close:*) state=closed ;; + save:closed) Echo "There isn't a file to save" ;; + save:*) case "$arg" in + ?*) save "$arg" ;; + *) save "$name" ;; + esac + state=open + ;; + discard:changed) Echo "Your problem!"; state=closed ;; + discard:*) state=closed ;; + print:closed) Echo "No current file" ;; + print:*) lineout ;; + goto:closed) Echo "No current file" ;; + goto:*) goto "$arg"; $autoprint ;; + next:closed) Echo "No current file" ;; + next:*) next; $autoprint ;; + prev:closed) Echo "No current file" ;; + prev:*) prev; $autoprint ;; + name:closed) Echo "No current file" ;; + name:*) name="$arg" ;; + replace:closed) Echo "No current file" ;; + replace:*) if rstring 1 $arg + then state=changed; $autoprint + else Echo "Not found" + fi + ;; + nreplace:closed) Echo "No current file" ;; + nreplace:*) if rstring $arg + then state=changed; $autoprint + else Echo "Not found" + fi + ;; + delete:closed) Echo "No current file" ;; + delete:*) delete; state=changed; $autoprint ;; + insert:closed) Echo "No current file" ;; + insert:*) insert "$arg"; prev; state=changed; $autoprint ;; + quit:changed) Echo "Use 'save' or 'discard'" ;; + quit:*) Echo "bye"; exit;; + autoprint:*) autoprint="lineout" ;; + noprint:*) autoprint="" ;; + :*) ;; + *) Echo "Command not understood" ;; + esac +done + diff --git a/examples/scripts/fixfiles.bash b/examples/scripts/fixfiles.bash new file mode 100644 index 0000000..15f3ba8 --- /dev/null +++ b/examples/scripts/fixfiles.bash @@ -0,0 +1,92 @@ +#! /bin/bash +# +# From: c2a192@ugrad.cs.ubc.ca (Kazimir Kylheku) +# Newsgroups: comp.unix.shell,comp.os.linux.misc +# Subject: GNU Bash Script to fix filenames +# Date: 28 Mar 1996 14:54:43 -0800 +# Organization: Computer Science, University of B.C., Vancouver, B.C., Canada +# +#This is a script which takes a list of directories, descends through each one +#and ``corrects'' filenames that: +# +# - contain filename globbing characters: * ? [ ] +# - quote characters: ' " +# - control characters: 0-31 (127 is not dealt with---oops) +# - - or + as the first character +# +# The GNU version of 'tr' is required. Also requires 'sed'. +# +# Script to process a given list of directories recursively +# and rename each file to something that is reasonable. +# +# The rules are: +# +# 1. replace each space, [, ], *, ", and ' character in the name with a +# period. +# 2. replace each control character 1..31 with a printable character obtained +# by adding 64 to the ascii value. ^A becomes A, ^B becomes B and so on. +# 3. replace a - or + occuring at the beginning of the name with a # +# +# 4. if the resulting name has been changed in any way, then +# 5. if a file of the new name already exists, then +# 6. add a . to the new name and goto step 5. +# 7. rename the old name to the new name +# +# written by Kaz Kylheku <kaz@cafe.net> +# March 1996 +# Vancouver, Canada +# +# requires GNU 'bash', GNU 'tr', and some sort of 'sed' program. +# +# minimal conversion to bash v2 syntax done by Chet Ramey + +processfile() +{ + new_name="`echo -n $1 | tr '\173\175\052\077\042\047 ' '.......' | + tr '[\000-\037]' '[\100-\137]' | + sed -e 's/^-/#/' -e 's/+/#/'`" + if [ "$new_name" != "$1" ] ; then + while [ -e "$new_name" ] ; do + new_name="${new_name}." + done + echo changing \"$1\" to \"$new_name\" in `pwd` + mv -- "$1" "$new_name" + fi +} + +processdir() +{ + set -f + local savepwd="$PWD" + if cd "$1" ; then + set +f + for file in * ; do + set -f + if [ "$file" != "." ] && [ "$file" != ".." ] ; then + if [ -L "$file" ] ; then + echo "skipping symlink" $file in `pwd` + elif [ -d "$file" ] ; then + processdir "$file" + elif [ -f "$file" ] ; then + processfile "$file" + fi + fi + done + cd "$savepwd" + fi +} + +shopt -s nullglob dotglob + +if [ $# = 0 ] ; then + echo "$0: must specify a list of directories" >&2 + echo "$0: usage: $0 directory [directory ...]" >&2 + exit 2 +fi + +while [ $# != 0 ] ; do + processdir "$1" + shift +done + +exit 0 diff --git a/examples/scripts/hanoi.bash b/examples/scripts/hanoi.bash new file mode 100644 index 0000000..c308125 --- /dev/null +++ b/examples/scripts/hanoi.bash @@ -0,0 +1,21 @@ +# Towers of Hanoi in bash +# +# cribbed from the ksh93 book, example from exercises on page 85 +# +# Chet Ramey +# chet@po.cwru.edu + +hanoi() # n from to spare +{ + typeset -i nm1=$1-1 + ((nm1>0)) && hanoi $nm1 $2 $4 $3 + echo "Move disc $2 to $3" + ((nm1>0)) && hanoi $nm1 $4 $3 $2 +} + +case $1 in +[1-9]) + hanoi $1 1 2 3;; +*) echo "${0##*/}: Argument must be from 1 to 9" + exit 1;; +esac diff --git a/examples/scripts/inpath b/examples/scripts/inpath new file mode 100755 index 0000000..95f28bc --- /dev/null +++ b/examples/scripts/inpath @@ -0,0 +1,19 @@ +#! /bin/sh +# +# Search $PATH for a file the same name as $1; return TRUE if found. +# + +command=$1 +[ -n "$command" ] || exit 1 + +set `echo $PATH | sed 's/^:/.:/ + s/::/:.:/g + s/:$/:./ + s/:/ /g'` + +while [ $# -ne 0 ] ; do + [ -f $1/$command ] && exit 0 # test -x not universal + shift +done + +exit 1 diff --git a/examples/scripts/krand.bash b/examples/scripts/krand.bash new file mode 100755 index 0000000..dfdfd32 --- /dev/null +++ b/examples/scripts/krand.bash @@ -0,0 +1,74 @@ +# Originally +# +# From: bsh20858@news.fhda.edu (Brian S Hiles) +# Newsgroups: comp.unix.shell +# Subject: Re: getting random numbers +# Date: 23 Jan 1997 23:27:30 GMT +# Message-ID: <5c8s52$eif@tiptoe.fhda.edu> + +# @(#) krand Produces a random number within integer limits +# "krand" Korn shell script generates a random number in a +# specified range with an optionally specified ``seed'' value. +# Author: Peter Turnbull, May 1993 +# Modified by: Becca Thomas, January 1994 + +# changed the optional third argument to a -s option, converted to +# bash v2 syntax -- chet@po.cwru.edu + +PROGNAME=${0##*/} +USAGE="usage: $PROGNAME [-s seed] lower-limit upper-limit" + +Seed=$$ # Initialize random-number seed value with PID + +usage() +{ + echo ${PROGNAME}: "$USAGE" >&2 +} + +errexit() +{ + echo ${PROGNAME}: "$@" >&2 + exit 1 +} + +# Process command-line arguments: +while getopts "s:" opt; do + case "$opt" in + s) Seed=$OPTARG ;; + *) usage ; exit 2;; + esac +done + +shift $(($OPTIND - 1)) + +case $# in + 2) Lower=$1; Upper=$2 ;; + *) usage ; exit 2;; +esac + +# Check that specified values are integers: +expr "$Lower" + 0 >/dev/null 2>&1 +[ $? -eq 2 ] && { errexit "lower ($Lower) not an integer"; } +expr "$Upper" + 0 >/dev/null 2>&1 +[ $? -eq 2 ] && { errexit "upper ($Upper) not an integer"; } +expr "$Seed" + 0 >/dev/null 2>&1 +[ $? -eq 2 ] && { errexit "seed ($Seed) not an integer"; } + +# Check that values are in the correct range: +if (( "$Lower" < 0 )) || [ ${#Lower} -gt 5 ]; then + errexit "lower limit ($Lower) less than zero" +fi +if (( "$Upper" > 32767 )) || [ ${#Upper} -gt 5 ]; then + errexit "upper limit ($Upper) greater than 32767" +fi +if (( "$Seed" < 0 )) || (( "$Seed" > 32767 )) || [ ${#Seed} -gt 5 ]; then + errexit "seed value ($Seed) out of range (0 to 32767)" +fi +(( "$Upper" <= "$Lower" )) && errexit "upper limit ($Upper) <= lower limit ($Lower)" + +# Seed the random-number generator: +RANDOM=$Seed +# Compute value, scaled within range: +let rand="$RANDOM % ($Upper - $Lower + 1) + $Lower" +# Report result: +echo $rand diff --git a/examples/scripts/line-input.bash b/examples/scripts/line-input.bash new file mode 100644 index 0000000..3f2efae --- /dev/null +++ b/examples/scripts/line-input.bash @@ -0,0 +1,184 @@ +#! /bin/bash +# +#From: kaz@cafe.net (Kaz Kylheku) +#Newsgroups: comp.unix.shell +#Subject: Funky little bash script +#Message-ID: <6mspb9$ft2@espresso.cafe.net> +#Date: Thu, 25 Jun 1998 06:11:39 GMT + +#Here is something I wrote a few years ago when I was bored one day. +#Warning: this contains control characters. + +# Line input routine for GNU Bourne-Again Shell +# plus terminal-control primitives. +# +# by Kaz Kylheku +# June 1996, Vancouver, Canada + + +# +# Function to disable canonical input processing. +# Terminal modes are saved into variable "savetty" +# +# + +function raw +{ + savetty=$(stty -g) + stty -icanon -isig -echo -echok -echonl inlcr +} + +# +# Function to restore terminal settings from savetty variable +# + +function restore +{ + stty $savetty +} + +# +# Set terminal MIN and TIME values. +# If the input argument is a zero, set up terminal to wait for +# a keystroke indefinitely. If the argument is non-zero, set up +# an absolute timeout of that many tenths of a second. The inter-keystroke +# timer facility of the terminal driver is not exploited. +# + +function settimeout +# $1 = tenths of a second +{ + if [ "$1" = "0" ] ; then + min=1 + timeout=0 + else + min=0 + timeout="$1" + fi + + stty min $min time $timeout + + unset min timeout +} + +# +# Input a single key using 'dd' and echo it to standard output. +# Launching an external program to get a single keystroke is a bit +# of a pig, but it's the best you can do! Maybe we could convince the +# GNU guys to make 'dd' a bash builtin. +# + +function getkey +{ + eval $1="\"\$(dd bs=1 count=1 2> /dev/null)\"" +} + +# +# Input a line of text gracefully. +# The first argument is the name of a variable where the input line is +# to be stored. If this variable is not empty, its contents are printed +# and treated as though the user had entered them. +# The second argument gives the maximum length of the input line; if it +# is zero, the input is unlimited (bad idea). +# ^W is used to delete words +# ^R redraws the line at any time by backspacing over it and reprinting it +# ^U backspaces to the beginning +# ^H or ^? (backspace or del) delete a single character +# ^M (enter) terminates the input +# all other control keys are ignored and cause a beep when pressed +# +# + + +function getline +{ + settimeout 0 # No keystroke timeout. + save_IFS="$IFS" # Save word delimiter and set it to + IFS="" # to null so ${#line} works correctly. + eval line=\${$1} # Fetch line contents + echo -n "$line" # and print the existing line. + while [ 1 ] ; do + getkey key # fetch a single keystroke + case "$key" in + | ) # BS or DEL + if [ ${#line} != 0 ] ; then # if line not empty + echo -n " " # print destructive BS + line="${line%%?}" # chop last character + else # else if line empty + echo -n # beep the terminal + fi + ;; + ) # kill to line beg + while [ ${#line} != 0 ] ; do # while line not empty + echo -n " " # print BS, space, BS + line="${line%?}" # shorten line by 1 + done + ;; + ) # redraw line + linesave="$line" # save the contents + while [ ${#line} != 0 ] ; do # kill to line beg + echo -n " " + line="${line%?}" + done + echo -n "$linesave" # reprint, restore + line="$linesave" + unset linesave # forget temp var + ;; + ) + while [ "${line% }" != "$line" ] && [ ${#line} != 0 ] ; do + echo -n " " + line="${line%?}" + done + while [ "${line% }" = "$line" ] && [ ${#line} != 0 ] ; do + echo -n " " + line="${line%?}" + done + ;; + | | | | | | | | | | |
) + echo -n # ignore various control characters + ;; # with an annoying beep + | | | | | | | | | | | | ) + echo -n + ;; + ' ' | | | | | ) + echo -n + ;; + '' ) # Break out of loop on carriage return. + echo # Send a newline to the terminal. + break # (Also triggered by NUL char!). + ;; + * ) # Append character to the end of the line. + # If length is restricted, and the line is too + # long, then beep... + + if [ "$2" != 0 ] && [ $(( ${#line} >= $2 )) = 1 ] ; then + echo -n + else # Otherwise add + line="$line$key" # the character. + echo -n "$key" # And echo it. + fi + ;; + esac + done + eval $1=\"\$line\" + IFS="$save_IFS" + unset line save_IFS +} + +# uncomment the lines below to create a standalone test program +# +echo "Line input demo for the GNU Bourne-Again Shell." +echo "Hacked by Kaz Kylheku" +echo +echo "Use ^H/Backspace/Del to erase, ^W to kill words, ^U to kill the" +echo "whole line of input, ^R to redraw the line." +echo "Pass an argument to this program to prime the buffer contents" +raw +echo -n "go: " +if [ ${#1} != 0 ] ; then + LINE=$1 +fi +getline LINE 50 +restore + +echo "<$LINE>" diff --git a/examples/scripts/nohup.bash b/examples/scripts/nohup.bash new file mode 100644 index 0000000..3781293 --- /dev/null +++ b/examples/scripts/nohup.bash @@ -0,0 +1,51 @@ +# +# BASH VERSION OF nohup COMMAND +# +ctype() +{ + path=$(builtin type -p $cmd | sed 1q) + if [ -n "$path" ]; then + echo "$path" + return 0 + else + case "$cmd" in + */*) [ -x "$cmd ] && { echo "$cmd" ; return 0; } ;; + *) case "$(builtin type -t $cmd)" in + "") return 1;; + *) echo "$cmd" ; return 0;; + esac ;; + esac + fi + return 1 +} + +trap '' HUP # ignore hangup +command=$(ctype "$1") +oldmask=$(umask) +umask u=rw,og= # default mode for nohup.out +exec 0< /dev/null # disconnect input +if [ -t 1 ]; then # redirect output if necessary + if [ -w . ]; then + echo 'Sending output to nohup.out' + exec >> nohup.out + else echo "Sending output to $HOME/nohup.out" + exec >> $HOME/nohup.out + fi +fi + +umask "$oldmask" + +# direct unit 2 to a file +if [ -t 2 ]; then + exec 2>&1 +fi + +# run the command +case $command in +*/*) exec "$@" + ;; +time) eval "$@" + ;; +*) "$@" + ;; +esac diff --git a/examples/scripts/precedence b/examples/scripts/precedence new file mode 100755 index 0000000..9bbdb97 --- /dev/null +++ b/examples/scripts/precedence @@ -0,0 +1,75 @@ +# @(#)precedence_test 1.0 91/07/24 Maarten Litmaath +# test of relative precedences for `&&' and `||' operators + +echo "\`Say' echos its argument. Its return value is of no interest." +case `echo -n` in + '') Say () { echo -n "$*" ; } ;; + *) Say () { echo "$*\c" ; } ;; +esac + +echo "\`Truth' echos its argument and returns a TRUE result." +Truth () { + Say $1; + return 0; +} + +echo "\`False' echos its argument and returns a FALSE result." +False () { + Say $1; + return 1; +} + +echo "" + +cmd1='$open $test1 && $test2 $close || $test3' +cmd2='$test1 || $open $test2 && $test3 $close' + +grouping_sh= +grouping_C='( )' + +test3='Say 3' + +for i in 1 2 +do + eval proto=\$cmd$i + + for test1 in 'Truth 1' 'False 1' + do + for test2 in 'Truth 2' 'False 2' + do + for precedence in sh C + do + eval set x \$grouping_$precedence + shift + open=${1-' '} + close=${2-' '} + eval cmd=\""$proto"\" + Say "$cmd output=" + output=`eval "$cmd"` + Say "$output" + read correct || { echo 'Input fubar. Abort.' >&2; exit 1; } + test "X$output" = "X$correct" || echo " correct=$correct" + echo '' + done + + echo '' + done + done +done << EOF +12 +12 +123 +123 +13 +13 +13 +13 +13 +1 +13 +1 +123 +123 +12 +12 +EOF diff --git a/examples/scripts/randomcard.bash b/examples/scripts/randomcard.bash new file mode 100644 index 0000000..9cb6b50 --- /dev/null +++ b/examples/scripts/randomcard.bash @@ -0,0 +1,18 @@ +# The following prints a random card from a card deck. +# +# cribbed from the ksh93 book, example from page 70 +# +# chet@po.cwru.edu +# +declare -i i=0 + +# load the deck +for suit in clubs diamonds hearts spades; do + for n in ace 2 3 4 5 6 7 8 9 10 jack queen king; do + card[i]="$n of $suit" + i=i+1 # let is not required with integer variables + done +done + +# and print a random card +echo ${card[RANDOM%52]} diff --git a/examples/scripts/scrollbar b/examples/scripts/scrollbar new file mode 100755 index 0000000..388bea8 --- /dev/null +++ b/examples/scripts/scrollbar @@ -0,0 +1,25 @@ +#!/bin/bash +# +# scrollbar - display scrolling text +# +# usage: scrollbar args +# +# A cute hack originally from Heiner Steven <hs@bintec.de> +# +# converted from ksh syntax to bash v2 syntax by Chet Ramey + +WIDTH=${COLUMNS:-80} +WMINUS=$(( $WIDTH - 1 )) + +[ $# -lt 1 ] && set -- TESTING + +# use the bash-2.02 printf builtin +Text=$(printf "%-${WIDTH}s" "$*") +Text=${Text// /_} + +while : +do + printf "%-.${WIDTH}s\r" "$Text" + LastC=${Text:${WMINUS}:1} + Text="$LastC""${Text%?}" +done diff --git a/examples/scripts/scrollbar2 b/examples/scripts/scrollbar2 new file mode 100755 index 0000000..0e53634 --- /dev/null +++ b/examples/scripts/scrollbar2 @@ -0,0 +1,24 @@ +#!/bin/bash +# +# scrollbar - display scrolling text +# +# usage: scrollbar args +# +# A cute hack originally from Heiner Steven <hs@bintec.de> +# +# converted from ksh syntax to bash v2 syntax by Chet Ramey + +WIDTH=${COLUMNS:-80} +WMINUS=$(( $WIDTH - 1 )) + +[ $# -lt 1 ] && set -- TESTING + +# use the bash-2.02 printf builtin +Text=$(printf "%-${WIDTH}s" "$*") + +while : +do + printf "%-.${WIDTH}s\r" "$Text" + LastC=${Text:${WMINUS}:1} + Text="$LastC""${Text%?}" +done diff --git a/examples/scripts/self-repro b/examples/scripts/self-repro new file mode 100644 index 0000000..951d4e4 --- /dev/null +++ b/examples/scripts/self-repro @@ -0,0 +1,9 @@ +# self-reproducing script (except for these comment lines -- remove them) +# i got this from the ksh93 faq: +# http://www.kornshell.com/doc/faq.html +# +n=" +" q="'" x="cat <<-!" y=! z='n="$n" q="$q" x="$x" y=$y z=$q$z$q$n$x$n$z$n$y' +cat <<-! +n="$n" q="$q" x="$x" y=$y z=$q$z$q$n$x$n$z$n$yb +! diff --git a/examples/scripts/showperm.bash b/examples/scripts/showperm.bash new file mode 100644 index 0000000..2e06c0b --- /dev/null +++ b/examples/scripts/showperm.bash @@ -0,0 +1,53 @@ +#Newsgroups: comp.unix.shell +#From: gwc@root.co.uk (Geoff Clare) +#Subject: Re: Determining permissions on a file +#Message-ID: <Dr79nw.DtL@root.co.uk> +#Date: Fri, 10 May 1996 17:23:56 GMT + +#Here's a bit of Korn shell that converts the symbolic permissions produced +#by "ls -l" into octal, using only shell builtins. How to create a script +#combining this with an "ls -l" is left as an exercise... +# +# +# Converted to Bash v2 syntax by Chet Ramey <chet@po.cwru.edu> +# +# usage: showperm modestring +# +# example: showperm '-rwsr-x--x' +# + +[ -z "$1" ] && { + echo "showperm: usage: showperm modestring" >&2 + exit 2 +} + +tmode="$1" + +typeset -i omode sbits +typeset pmode + +# check for set-uid, etc. bits +sbits=0 +case $tmode in +???[sS]*) (( sbits += 8#4000 )) ;; # set-uid +??????[sSl]*) (( sbits += 8#2000 )) ;; # set-gid or mand. lock +?????????[tT]*) (( sbits += 8#1000 )) ;; # sticky +esac + +omode=0 +while : +do + tmode=${tmode#?} + case $tmode in + "") break ;; + [-STl]*) (( omode *= 2 )) ;; + [rwxst]*) (( omode = omode*2 + 1 )) ;; + *) echo "$0: first letter of \"$tmode\" is unrecognized" >&2 + (( omode *= 2 )) + ;; + esac +done + +(( omode += sbits )) + +printf "0%o\n" $omode diff --git a/examples/scripts/shprompt b/examples/scripts/shprompt new file mode 100755 index 0000000..ec8b997 --- /dev/null +++ b/examples/scripts/shprompt @@ -0,0 +1,137 @@ +# +# shprompt -- give a prompt and get an answer satisfying certain criteria +# +# shprompt [-dDfFsy] prompt +# s = prompt for string +# f = prompt for filename +# F = prompt for full pathname to a file or directory +# d = prompt for a directory name +# D = prompt for a full pathname to a directory +# y = prompt for y or n answer +# +# Chet Ramey +# chet@ins.CWRU.Edu + +type=file + +OPTS=dDfFsy + +succeed() +{ + echo "$1" + exit 0 +} + +while getopts "$OPTS" c +do + case "$c" in + s) type=string + ;; + f) type=file + ;; + F) type=path + ;; + d) type=dir + ;; + D) type=dirpath + ;; + y) type=yesno + ;; + ?) echo "usage: $0 [-$OPTS] prompt" 1>&2 + exit 2 + ;; + esac +done + +if [ "$OPTIND" -gt 1 ] ; then + shift $[$OPTIND - 1] +fi + +while : +do + case "$type" in + string) + echo -n "$1" 1>&2 + read ans || exit 1 + if [ -n "$ans" ] ; then + succeed "$ans" + fi + ;; + file|path) + echo -n "$1" 1>&2 + read ans || exit 1 + # + # use `fn' and eval so that bash will do tilde expansion for + # me + # + eval fn="$ans" + case "$fn" in + /*) if test -e "$fn" ; then + succeed "$fn" + else + echo "$0: '$fn' does not exist" 1>&2 + fi + ;; + *) if [ "$type" = "path" ] ; then + echo "$0: must give full pathname to file" 1>&2 + else + if test -e "$fn" ; then + succeed "$fn" + else + echo "$0: '$fn' does not exist" 1>&2 + fi + fi + ;; + esac + ;; + dir|dirpath) + echo -n "$1" 1>&2 + read ans || exit 1 + # + # use `fn' and eval so that bash will do tilde expansion for + # me + # + eval fn="$ans" + case "$fn" in + /*) if test -d "$fn" ; then + succeed "$fn" + elif test -e "$fn" ; then + echo "$0 '$fn' is not a directory" 1>&2 + else + echo "$0: '$fn' does not exist" 1>&2 + fi + ;; + *) if [ "$type" = "dirpath" ] ; then + echo "$0: must give full pathname to directory" 1>&2 + else + if test -d "$fn" ; then + succeed "$fn" + elif test -e "$fn" ; then + echo "$0 '$fn' is not a directory" 1>&2 + else + echo "$0: '$fn' does not exist" 1>&2 + fi + fi + ;; + esac + ;; + yesno) + echo -n "$1" 1>&2 + read ans || exit 1 + case "$ans" in + y|Y|[yY][eE][sS]) + succeed "yes" + ;; + n|N|[nN][oO]) + succeed "no" + exit 0 + ;; + *) + echo "$0: yes or no required" 1>&2 + ;; + esac + ;; + esac +done + +exit 1 diff --git a/examples/scripts/spin.bash b/examples/scripts/spin.bash new file mode 100644 index 0000000..dc6a66a --- /dev/null +++ b/examples/scripts/spin.bash @@ -0,0 +1,21 @@ +#!/bin/bash +# +# spin.bash -- provide a `spinning wheel' to show progress +# +# Chet Ramey +# chet@po.cwru.edu +# +bs=$'\b' + +chars="|${bs} \\${bs} -${bs} /${bs}" + +# Infinite loop for demo. purposes +while : +do + for letter in $chars + do + echo -n ${letter} + done +done + +exit 0 diff --git a/examples/scripts/timeout b/examples/scripts/timeout new file mode 100644 index 0000000..ac8d88f --- /dev/null +++ b/examples/scripts/timeout @@ -0,0 +1,53 @@ +#Newsgroups: comp.unix.admin,comp.unix.solaris,comp.unix.shell +#From: gwc@root.co.uk (Geoff Clare) +#Subject: Re: timeout -t <sec> <unix command> (Re: How to give rsh a shorter timeout?) +#Message-ID: <EoBxrs.223@root.co.uk> +#Date: Fri, 13 Feb 1998 18:23:52 GMT + +# +# Conversion to bash v2 syntax done by Chet Ramey <chet@po.cwru.edu +# UNTESTED +# + +prog=${0##*/} +usage="usage: $prog [-signal] [timeout] [:interval] [+delay] [--] <command>" + +SIG=-TERM # default signal sent to the process when the timer expires +timeout=60 # default timeout +interval=15 # default interval between checks if the process is still alive +delay=2 # default delay between posting the given signal and + # destroying the process (kill -KILL) + +while : +do + case $1 in + --) shift; break ;; + -*) SIG=$1 ;; + [0-9]*) timeout=$1 ;; + :*) EXPR='..\(.*\)' ; interval=`expr x"$1" : "$EXPR"` ;; + +*) EXPR='..\(.*\)' ; delay=`expr x"$1" : "$EXPR"` ;; + *) break ;; + esac + shift +done + +case $# in +0) echo "$prog: $usage" >&2 ; exit 2 ;; +esac + +( + for t in $timeout $delay + do + while (( $t > $interval )) + do + sleep $interval + kill -0 $$ || exit + t=$(( $t - $interval )) + done + sleep $t + kill $SIG $$ && kill -0 $$ || exit + SIG=-KILL + done +) 2> /dev/null & + +exec "$@" diff --git a/examples/scripts/vtree2 b/examples/scripts/vtree2 new file mode 100755 index 0000000..878cbab --- /dev/null +++ b/examples/scripts/vtree2 @@ -0,0 +1,43 @@ +#!/bin/bash +# +# vtree - make a tree printout of the specified directory, with disk usage +# in 1k blocks +# +# usage: vtree [-a] [dir] +# +# Original posted to Usenet sometime in February, 1996 +# I believe that the original author is Brian S. Hiles <bsh29256@atc.fhda.edu> +# +usage() +{ + echo "vtree: usage: vtree [-a] [dir]" >&2 +} + +while getopts a opt +do + case "$opt" in + a) andfiles=-a ;; + *) usage ; exit 2 ;; + esac +done + +shift $((OPTIND - 1)) + +export BLOCKSIZE=1k # 4.4 BSD systems need this + +[ $# -eq 0 ] && set . + +while [ $# -gt 0 ] +do + cd "$1" || { shift; [ $# -ge 1 ] && echo >&2; continue; } + echo -n "$PWD" + + du $andfiles | sort -k 2f | sed \ + -e 's/\([^ ]*\) \(.*\)/\2 (\1)/' \ + -e "s#^$1##" \ + -e 's#[^/]*/\([^/]*\)$#|____\1#' \ + -e 's#[^/]*/#| #g' + + [ $# -gt 1 ] && echo + shift +done diff --git a/examples/scripts/vtree3 b/examples/scripts/vtree3 new file mode 100644 index 0000000..2059b9f --- /dev/null +++ b/examples/scripts/vtree3 @@ -0,0 +1,99 @@ +#!/bin/ksh +# +# Name: dirtree +# Programmer: +# Hemant T. Shah +# Life Insurance Data Processing +# July 12 1994 +# +# Description: +# Print directory tree structure as follows: +# |___Mail +# |___scheduler +# |___cics_scripts +# |___tar_msdos +# |___awk +# |___attributes +# |___tmp +# |___News +# |___dosscsi +# |___FAQ_xterminal +# |___shell_history.Z +# |___FAQ_AIX +# |___aix_ftp_site +# |___hp_software +# |___dnload +# |___telnet.h +# |___msdos +# |___tnetd.tar.Z +# |___aix +# |___hp +# |___xkey.c +# +# Conversion to bash v2 syntax done by Chet Ramey +# - removed command substitutions calling `basename' +# + +ProgramName=${0##*/} +Path="." +ShowAll=1 +ShowDir=0 + + +ExpandDirectory() +{ +local object # Local variable + +cd "$1" + +for object in $PWD/.??* $PWD/* +do + if [ -d $object ]; # It is a directory + then + echo "${indent}|___${object##*/}/" + indent="${indent}! " # Add to indentation + if [ -x $object ]; + then + ExpandDirectory $object + fi + indent=${indent%????} # Remove from indentation + elif [ -e $object ]; then + if (( ShowAll == 1 )); + then + echo "${indent}|___${object##*/}" + fi + fi +done + +} + +usage() +{ + echo -e "Usage: $ProgramName [-h] [-f] [-d] [path] " + echo -e "\t-h ... display this help message." + echo -e "\t-f path ... shows all files and directories below path (default)." + echo -e "\t-d path ... shows all directories only below path." +} + +while getopts "fd" opt +do + case $opt in + f) ShowAll=1 ;; + d) ShowDir=1 ;; + *) usage ; exit 2;; + esac +done + +shift $(( $OPTIND - 1 )) + +Path=${1:-.} + +if [ ! -d "$Path" ]; then + echo "$0: error: specified path is not a directory." >&2 + exit 1 +fi + + + +echo "!$Path/" +ExpandDirectory $Path diff --git a/examples/scripts/vtree3a b/examples/scripts/vtree3a new file mode 100644 index 0000000..0678764 --- /dev/null +++ b/examples/scripts/vtree3a @@ -0,0 +1,100 @@ +#!/bin/bash +# +# Name: dirtree +# Programmer: +# Hemant T. Shah +# Life Insurance Data Processing +# July 12 1994 +# +# Description: +# Print directory tree structure as follows: +# |___Mail +# |___scheduler +# |___cics_scripts +# |___tar_msdos +# |___awk +# |___attributes +# |___tmp +# |___News +# |___dosscsi +# |___FAQ_xterminal +# |___shell_history.Z +# |___FAQ_AIX +# |___aix_ftp_site +# |___hp_software +# |___dnload +# |___telnet.h +# |___msdos +# |___tnetd.tar.Z +# |___aix +# |___hp +# |___xkey.c +# +# Conversion to bash v2 syntax done by Chet Ramey +# - removed command substitutions calling `basename' +# + +ProgramName=${0##*/} +Path="." +ShowAll=1 +ShowDir=0 + + +ExpandDirectory() +{ +local object # Local variable + +cd "$1" + +for object in $PWD/.??* $PWD/* +do + if [ -d $object ]; # It is a directory + then + echo "${indent}|___${object##*/}/" + indent="${indent}! " # Add to indentation + if [ -x $object ]; + then + ExpandDirectory $object + fi + indent=${indent%????} # Remove from indentation + elif [ -e $object ]; then + if (( ShowAll == 1 )); + then + echo "${indent}|___${object##*/}" + fi + fi +done + +} + +usage() +{ + echo -e "Usage: $ProgramName [-h] [-f] [-d] [path] " + echo -e "\t-h ... display this help message." + echo -e "\t-f path ... shows all files and directories below path (default)." + echo -e "\t-d path ... shows all directories only below path." +} + +while getopts "fd" opt +do + case $opt in + f) ShowAll=1 ;; + #d) ShowDir=1 ;; + d) ShowAll=0 ;; + *) usage ; exit 2;; + esac +done + +shift $(( $OPTIND - 1 )) + +Path=${1:-.} + +if [ ! -d "$Path" ]; then + echo "$0: error: specified path is not a directory." >&2 + exit 1 +fi + + + +echo "!$Path/" +ExpandDirectory $Path diff --git a/examples/scripts/websrv.sh b/examples/scripts/websrv.sh new file mode 100644 index 0000000..e07746b --- /dev/null +++ b/examples/scripts/websrv.sh @@ -0,0 +1,230 @@ +#!/bin/sh +#for instructions or updates go to: +#<A HREF="http://math.ucr.edu:8889/">This script's home page</A> +#email me questions or comments at: +#<A HREF="mailto:insom@math.ucr.edu">insom@math.ucr.edu</A> +#copyright chris ulrich; This software may be used or modified +#in any way so long as this notice remains intact. +# +# WWW server in sh +# Author: Chris Ulrich <chris@tinker.ucr.edu> +# + +INDEX=index.html +date=`date` +DOCHOME=/home/insom/web-docs +BINHOME=/home/insom/web-bin +LOGHOME=/home/insom/web-logs +LOGFILE=$LOGHOME/access_log +#verbose=: +verbose=echo +exec 2>> $LOGHOME/error_log + +hheader() { +echo "HTTP/1.0 200 OK +Server: WebSH/2.00 +Connection: close +Date: $date" +} + +header() { +echo "Content-type: $1 +" +} + +no_url() { + header "text/plain" + echo "No such url $1" +} + +send() { + #case "$#" in 2) ;; *) echo eep! | mailx insom@math.ucr.edu ; exit 3 ;; esac + if test -f "$DOCHOME/$2" + then + header "$1" + cat "$DOCHOME/$2" + else + no_url "$2" + fi +} + +LsToHTML() { + if test -f "$DOCHOME/$url/.title" + then + header "text/html; charset=US-ASCII" + echo "<pre>" + cat "$DOCHOME/$url/.title" + echo "</pre>" + elif test -f "$DOCHOME/$url/.title.html" + then + header "text/html; charset=US-ASCII" + cat "$DOCHOME/$url/.title.html" + else + header "text/html; charset=US-ASCII" + fi + + case "$url" in + /) ;; + *) url="$url/" + esac + + while read link + do + case $link in + *.cgi) ;; + *) + echo "<A HREF=\"$url$link\">$link</A> <BR>" + ;; + esac + done +} + +read method data + +$verbose " +$date access from ${TCPREMOTEINFO:=NO-IDENT}@${TCPREMOTEHOST:=$TCPREMOTEIP} + on local machine $TCPLOCALHOST + $method $data " >> $LOGFILE + +for hopeurl in $data +do + url="${url}${url:+ }$second" + second="$hopeurl" +done + +case "$second" in + *[1-9].*) + read inheader + while + case "$inheader" in + ?|'') false + ;; + *) + read inheader + ;; + esac + do + : + done + hheader + ;; +esac + +case "$url" in + *..*) + no_url "$url" + exit 1 + ;; + *.txt|*.[ch]) + send "text/plain; charset=US-ASCII" "$url" + ;; + *.html) + send "text/html; charset=US-ASCII" "$url" + ;; + *.cgi) + if test -x "$DOCHOME/$url" + then + read message + echo "$message" | "$DOCHOME/$url" + else + no_url "$url" + fi + ;; + *".cgi?"*) + oIFS="$IFS" + echo "$url" | { + IFS='?' read url QUERY_STRING + if test -x "$DOCHOME/$url" + then + IFS="$oIFS" + export QUERY_STRING + "$DOCHOME/$url" + else + no_url "$url" + fi + } + ;; + *.[Gg][Ii][Ff]) + send "image/gif" "$url" + ;; + *.[Jj][Pp][Gg]|*.[Jj][Pp][Ee][Gg]) + send "image/jpeg" "$url" + ;; + *.tbl) + header "text/html; charset=US-ASCII" + echo "<pre>" + test -f "$DOCHOME/$url" && + tbl < "$DOCHOME/$url" | nroff || + no_url "$url" + echo "</pre>" + ;; + *.nroff) + header "text/html; charset=US-ASCII" + echo "<pre>" + test -f "$DOCHOME/$url" && + nroff < "$DOCHOME/$url" || + no_url "$url" + echo "</pre>" + ;; + *mp[23]) + if test -f "$DOCHOME/$url" + then + header "application/mpstream" + echo "+$TCPLOCALIP:${MPSERVPORT:=9001}/$url" + else + no_url "$url" + fi + ;; + *.[0-9]|*.[0-9][a-z]) + header "text/html; charset=US-ASCII" + echo "<pre>" + if test -f "$DOCHOME/$url" + then + #nroff -man "$DOCHOME/$url" | $BINHOME/man2html + echo "perl at the moment is broken, so man2html doesn't work. Sorry." + echo "</pre>" + else + no_url "$url" + fi + ;; + *.???|*.??) + send "unknown/data" "$url" + ;; + */) + if test -d "$DOCHOME/$url" + then + ls "$DOCHOME/$url" | LsToHTML + fi + ;; + *) + if test -f "$DOCHOME/$url" + then + read filetype < "$DOCHOME/$url" + case "$filetype" in + \#!/*/*|\#!?/*/*) + header "text/plain; charset=US-ASCII" + cat "$DOCHOME/$url" + ;; + '<!*>') + header "text/html; charset=US-ASCII" + cat "$DOCHOME/$url" + ;; + *) + header "text/html; charset=US-ASCII" + echo "<pre>" + cat "$DOCHOME/$url" + echo "</pre>" + ;; + esac + elif test -f "$DOCHOME/$url/$INDEX" + then + header "text/html; charset=US-ASCII" + cat "$DOCHOME/$url/$INDEX" + elif test -d "$DOCHOME/$url" + then + ls "$DOCHOME/$url" | LsToHTML + else + no_url "$url" + fi + ;; +esac diff --git a/examples/scripts/xterm_title b/examples/scripts/xterm_title new file mode 100755 index 0000000..72ba099 --- /dev/null +++ b/examples/scripts/xterm_title @@ -0,0 +1,27 @@ +#! /bin/bash +# +# xterm_title - print the contents of the xterm title bar +# +# Derived from http://www.clark.net/pub/dickey/xterm/xterm.faq.html#how2_title +# +P=${0##*/} +[ -z "$DISPLAY" ] && { + echo "${P}: not running X" >&2 + exit 1 +} + +if [ -z "$TERM" ] || [ "$TERM" != "xterm" ]; then + echo "${P}: not running in an xterm" >&2 + exit 1 +fi + +exec </dev/tty +old=$(stty -g) +stty raw -echo min 0 time ${1-10} +echo -e "\033[21t\c" > /dev/tty +IFS='' read -r a +stty $old +b=${a#???} +echo "${b%??}" + +exit 0 diff --git a/examples/scripts/zprintf b/examples/scripts/zprintf new file mode 100755 index 0000000..5e2e3ad --- /dev/null +++ b/examples/scripts/zprintf @@ -0,0 +1,26 @@ +#! /bin/bash +# +# zprintf - function that calls gawk to do printf for those systems that +# don't have a printf executable +# +# The format and arguments can have trailing commas, just like gawk +# +# example: +# zprintf 'Eat %x %x and suck %x!\n' 57005 48879 64206 +# +# Chet Ramey +# chet@po.cwru.edu + +[ $# -lt 1 ] && { + echo "zprintf: usage: zprintf format [args ...]" >&2 + exit 2 +} + +fmt="${1%,}" +shift + +for a in "$@"; do + args="$args,\"${a%,}\"" +done + +gawk "BEGIN { printf \"$fmt\" $args }" |