diff options
author | An-Cheng Huang <ancheng@vyatta.com> | 2007-11-12 13:06:02 -0800 |
---|---|---|
committer | An-Cheng Huang <ancheng@vyatta.com> | 2007-11-12 13:06:02 -0800 |
commit | b7fc9e0f6d6105ba2203f219743d4b269415e84b (patch) | |
tree | ef6586dfc62798c2b17487b443864699aca55f31 /examples/functions | |
download | vyatta-bash-b7fc9e0f6d6105ba2203f219743d4b269415e84b.tar.gz vyatta-bash-b7fc9e0f6d6105ba2203f219743d4b269415e84b.zip |
initial import from bash_3.1dfsg.orig.tar.gz
Diffstat (limited to 'examples/functions')
54 files changed, 3233 insertions, 0 deletions
diff --git a/examples/functions/array-stuff b/examples/functions/array-stuff new file mode 100644 index 0000000..97ed512 --- /dev/null +++ b/examples/functions/array-stuff @@ -0,0 +1,103 @@ +# usage: reverse arrayname +reverse() +{ + local -a R + local -i i + local rlen temp + + # make r a copy of the array whose name is passed as an arg + eval R=\( \"\$\{$1\[@\]\}\" \) + + # reverse R + rlen=${#R[@]} + + for ((i=0; i < rlen/2; i++ )) + do + temp=${R[i]} + R[i]=${R[rlen-i-1]} + R[rlen-i-1]=$temp + done + + # and assign R back to array whose name is passed as an arg + eval $1=\( \"\$\{R\[@\]\}\" \) +} + +A=(1 2 3 4 5 6 7) +echo "${A[@]}" +reverse A +echo "${A[@]}" +reverse A +echo "${A[@]}" + +# unset last element of A +alen=${#A[@]} +unset A[$alen-1] +echo "${A[@]}" + +# ashift -- like shift, but for arrays + +ashift() +{ + local -a R + local n + + case $# in + 1) n=1 ;; + 2) n=$2 ;; + *) echo "$FUNCNAME: usage: $FUNCNAME array [count]" >&2 + exit 2;; + esac + + # make r a copy of the array whose name is passed as an arg + eval R=\( \"\$\{$1\[@\]\}\" \) + + # shift R + R=( "${R[@]:$n}" ) + + # and assign R back to array whose name is passed as an arg + eval $1=\( \"\$\{R\[@\]\}\" \) +} + +ashift A 2 +echo "${A[@]}" + +ashift A +echo "${A[@]}" + +ashift A 7 +echo "${A[@]}" + +# Sort the members of the array whose name is passed as the first non-option +# arg. If -u is the first arg, remove duplicate array members. +array_sort() +{ + local -a R + local u + + case "$1" in + -u) u=-u ; shift ;; + esac + + if [ $# -eq 0 ]; then + echo "array_sort: argument expected" >&2 + return 1 + fi + + # make r a copy of the array whose name is passed as an arg + eval R=\( \"\$\{$1\[@\]\}\" \) + + # sort R + R=( $( printf "%s\n" "${A[@]}" | sort $u) ) + + # and assign R back to array whose name is passed as an arg + eval $1=\( \"\$\{R\[@\]\}\" \) + return 0 +} + +A=(3 1 4 1 5 9 2 6 5 3 2) +array_sort A +echo "${A[@]}" + +A=(3 1 4 1 5 9 2 6 5 3 2) +array_sort -u A +echo "${A[@]}" diff --git a/examples/functions/array-to-string b/examples/functions/array-to-string new file mode 100644 index 0000000..0d2fbe5 --- /dev/null +++ b/examples/functions/array-to-string @@ -0,0 +1,15 @@ +#! /bin/bash + +# Format: array_to_string vname_of_array vname_of_string separator +array_to_string() +{ + (( ($# < 2) || ($# > 3) )) && { + "$FUNCNAME: usage: $FUNCNAME arrayname stringname [separator]" + return 2 + } + + local array=$1 string=$2 + ((3==$#)) && [[ $3 = ? ]] && local IFS="${3}${IFS}" + eval $string="\"\${$array[*]}\"" + return 0 +} diff --git a/examples/functions/autoload b/examples/functions/autoload new file mode 100644 index 0000000..a563a77 --- /dev/null +++ b/examples/functions/autoload @@ -0,0 +1,111 @@ +# +# An almost ksh-compatible `autoload'. A function declared as `autoload' will +# be read in from a file the same name as the function found by searching the +# $FPATH (which works the same as $PATH), then that definition will be run. +# +# To do this without source support, we define a dummy function that, when +# executed, will load the file (thereby re-defining the function), then +# execute that newly-redefined function with the original arguments. +# +# It's not identical to ksh because ksh apparently does lazy evaluation +# and looks for the file to load from only when the function is referenced. +# This one requires that the file exist when the function is declared as +# `autoload'. +# +# usage: autoload func [func...] +# +# The first cut of this was by Bill Trost, trost@reed.bitnet +# +# Chet Ramey +# chet@ins.CWRU.Edu + +# +# Declare a function ($1) to be autoloaded from a file ($2) when it is first +# called. This defines a `temporary' function that will `.' the file +# containg the real function definition, then execute that new definition with +# the arguments given to this `fake' function. The autoload function defined +# by the file and the file itself *must* be named identically. +# + +aload() +{ + eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }' +} + +# +# Search $FPATH for a file the same name as the function given as $1, and +# autoload the function from that file. There is no default $FPATH. +# + +autoload() +{ + # + # Save the list of functions; we're going to blow away the arguments + # in a second. If any of the names contain white space, TFB. + # + + local args="$*" + + # + # This should, I think, list the functions marked as autoload and not + # yet defined, but we don't have enough information to do that here. + # + if [ $# -eq 0 ] ; then + echo "usage: autoload function [function...]" >&2 + return 1 + fi + + # + # If there is no $FPATH, there is no work to be done + # + + if [ -z "$FPATH" ] ; then + echo autoload: FPATH not set or null >&2 + return 1 + fi + + # + # This treats FPATH exactly like PATH: a null field anywhere in the + # FPATH is treated the same as the current directory. + # + # The path splitting command is taken from Kernighan and Pike + # + +# fp=$(echo $FPATH | sed 's/^:/.:/ +# s/::/:.:/g +# s/:$/:./ +# s/:/ /g') + + # replaced with builtin mechanisms 2001 Oct 10 + + fp=${FPATH/#:/.:} + fp=${fp//::/:.:} + fp=${fp/%:/:.} + fp=${fp//:/ } + + for FUNC in $args ; do + # + # We're blowing away the arguments to autoload here... + # We have to; there are no arrays (well, there are, but + # this doesn't use them yet). + # + set -- $fp + + while [ $# -ne 0 ] ; do + if [ -f $1/$FUNC ] ; then + break # found it! + fi + shift + done + + if [ $# -eq 0 ] ; then + echo "$FUNC: autoload function not found" >&2 + continue + fi + +# echo auto-loading $FUNC from $1/$FUNC + aload $FUNC $1/$FUNC + done + + return 0 +} diff --git a/examples/functions/autoload.v2 b/examples/functions/autoload.v2 new file mode 100644 index 0000000..e8f3433 --- /dev/null +++ b/examples/functions/autoload.v2 @@ -0,0 +1,192 @@ +# +# An almost ksh-compatible `autoload'. A function declared as `autoload' will +# be read in from a file the same name as the function found by searching the +# $FPATH (which works the same as $PATH), then that definition will be run. +# +# To do this without source support, we define a dummy function that, when +# executed, will load the file (thereby re-defining the function), then +# execute that newly-redefined function with the original arguments. +# +# It's not identical to ksh because ksh apparently does lazy evaluation +# and looks for the file to load from only when the function is referenced. +# This one requires that the file exist when the function is declared as +# `autoload'. +# +# usage: autoload [-pu] [func ...] +# +# options: +# -p print in a format that can be reused as input +# -u unset each function and remove it from the autoload list +# +# The first cut of this was by Bill Trost, trost@reed.edu +# +# Chet Ramey +# chet@ins.CWRU.Edu + +unset _AUTOLOADS +_aindex=0 + +# +# Declare a function ($1) to be autoloaded from a file ($2) when it is first +# called. This defines a `temporary' function that will `.' the file +# containg the real function definition, then execute that new definition with +# the arguments given to this `fake' function. The autoload function defined +# by the file and the file itself *must* be named identically. +# + +_aload() +{ + eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }' + _autoload_addlist "$1" +} + +_autoload_addlist() +{ + local i=0 + + while (( i < $_aindex )); do + case "${_AUTOLOADS[i]}" in + "$1") return 1 ;; + esac + (( i += 1 )) + done + _AUTOLOADS[_aindex]="$1" + (( _aindex += 1 )) + return 0 +} + +_autoload_dump() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [ -n "$1" ] && echo -n "autoload " + echo "$func" + done +} + +# Remove $1 from the list of autoloaded functions +_autoload_remove_one() +{ + local i=0 nnl=0 + local -a nlist + + while (( i < _aindex )); do + case "${_AUTOLOADS[i]}" in + "$1") ;; + *) nlist[nnl]="${_AUTOLOADS[i]}" ; (( nnl += 1 ));; + esac + (( i += 1 )) + done + unset _AUTOLOADS _aindex + eval _AUTOLOADS=( ${nlist[@]} ) + _aindex=$nnl +} + +# Remove all function arguments from the list of autoloaded functions +_autoload_remove() +{ + local func i es=0 + + # first unset the autoloaded functions + for func; do + i=0 + while (( i < _aindex )); do + case "${_AUTOLOADS[i]}" in + "$func") unset -f $func ; break ;; + esac + (( i += 1 )) + done + if (( i == _aindex )); then + echo "autoload: $func: not an autoloaded function" >&2 + es=1 + fi + done + + # then rebuild the list of autoloaded functions + for func ; do + _autoload_remove_one "$func" + done + + return $es +} + +# +# Search $FPATH for a file the same name as the function given as $1, and +# autoload the function from that file. There is no default $FPATH. +# + +autoload() +{ + local -a fp + local _autoload_unset nfp i + + if (( $# == 0 )) ; then + _autoload_dump + return 0 + fi + + OPTIND=1 + while getopts pu opt + do + case "$opt" in + p) _autoload_dump printable; return 0;; + u) _autoload_unset=y ;; + *) echo "autoload: usage: autoload [-pu] [function ...]" >&2 + return 1 ;; + esac + done + + shift $(( $OPTIND - 1 )) + + if [ -n "$_autoload_unset" ]; then + _autoload_remove "$@" + return $? + fi + + # + # If there is no $FPATH, there is no work to be done + # + + if [ -z "$FPATH" ] ; then + echo "autoload: FPATH not set or null" >&2 + return 1 + fi + + # + # This treats FPATH exactly like PATH: a null field anywhere in the + # FPATH is treated the same as the current directory. + # + # This turns $FPATH into an array, substituting `.' for `' + # + eval fp=( $( + IFS=':' + set -- ${FPATH} + for p in "$@" ; do echo -n "${p:-.} "; done + ) + ) + + nfp=${#fp[@]} + + for FUNC ; do + i=0; + while (( i < nfp )) ; do + if [ -f ${fp[i]}/$FUNC ] ; then + break # found it! + fi + (( i += 1 )) + done + + if (( i == nfp )) ; then + echo "autoload: $FUNC: autoload function not found" >&2 + es=1 + continue + fi + +# echo auto-loading $FUNC from ${fp[i]}/$FUNC + _aload $FUNC ${fp[i]}/$FUNC + es=0 + done + + return $es +} diff --git a/examples/functions/autoload.v3 b/examples/functions/autoload.v3 new file mode 100644 index 0000000..a82ffe9 --- /dev/null +++ b/examples/functions/autoload.v3 @@ -0,0 +1,125 @@ +#From: Mark Kennedy <mtk@ny.ubs.com> +#Message-ID: <35E2B899.63A02DF5@ny.ubs.com> +#Date: Tue, 25 Aug 1998 09:14:01 -0400 +#To: chet@nike.ins.cwru.edu +#Subject: a newer version of the ksh-style 'autoload' + +#enclosed you'll find 'autoload.v3', a version of the autoloader +#that emulates the ksh semantics of delaying the resolution (and loading) of the function +#until its first use. i took the liberty of simplifying the code a bit although it still uses the +#same functional breakdown. i recently went through the exercise of converting +#my ksh-based environment to bash (a very, very pleasant experience) +#and this popped out. + +# the psuedo-ksh autoloader. + +# The first cut of this was by Bill Trost, trost@reed.bitnet. +# The second cut came from Chet Ramey, chet@ins.CWRU.Edu +# The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25 + +unset _AUTOLOADS + +_aload() +{ + local func + for func; do + eval $func '() + { + local f=$(_autoload_resolve '$func') + if [[ $f ]]; then + . $f + '$func' "$@" + return $? + else + return 1 + fi + }' + _autoload_addlist $func + done +} + +_autoload_addlist() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [[ $func = "$1" ]] && return + done + + _AUTOLOADS[${#_AUTOLOADS[@]}]=$1 +} + +_autoload_dump() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [[ $1 ]] && echo -n "autoload " + echo $func + done +} + +_autoload_remove_one() +{ + local func + local -a NEW_AUTOLOADS + + for func in ${_AUTOLOADS[@]}; do + [[ $func != "$1" ]] && NEW_AUTOLOADS[${#NEW_AUTOLOADS[@]}]=$func + done + + _AUTOLOADS=( ${NEW_AUTOLOADS[@]} ) +} + +_autoload_remove() +{ + local victim func + + for victim; do + for func in ${_AUTOLOADS[@]}; do + [[ $victim = "$func" ]] && unset -f $func && continue 2 + done + echo "autoload: $func: not an autoloaded function" >&2 + done + + for func; do + _autoload_remove_one $func + done +} + +_autoload_resolve() +{ + if [[ ! "$FPATH" ]]; then + echo "autoload: FPATH not set or null" >&2 + return + fi + + local p + + for p in $( (IFS=':'; set -- ${FPATH}; echo "$@") ); do + p=${p:-.} + if [ -f $p/$1 ]; then echo $p/$1; return; fi + done + + echo "autoload: $1: function source file not found" >&2 +} + +autoload() +{ + if (( $# == 0 )) ; then _autoload_dump; return; fi + + local opt OPTIND + + while getopts pu opt + do + case $opt in + p) _autoload_dump printable; return;; + u) shift $((OPTIND-1)); _autoload_remove "$@"; return;; + *) echo "autoload: usage: autoload [-pu] [function ...]" >&2; return;; + esac + done + + shift $(($OPTIND-1)) + + _aload "$@" +} diff --git a/examples/functions/basename b/examples/functions/basename new file mode 100644 index 0000000..a541349 --- /dev/null +++ b/examples/functions/basename @@ -0,0 +1,23 @@ +# Date: Fri, 11 Oct 91 11:22:36 edt +# From: friedman@gnu.ai.mit.edu +# To: bfox@gnu.ai.mit.edu + +# A replacement for basename(1). Not all the systems I use have this +# program. Usage: basename [path] {extension} +function basename () +{ + local path="$1" + local suffix="$2" + local tpath="${path%/}" + + # Strip trailing '/' characters from path (unusual that this should + # ever occur, but basename(1) seems to deal with it.) + while [ "${tpath}" != "${path}" ]; do + tpath="${path}" + path="${tpath%/}" + done + + path="${path##*/}" # Strip off pathname + echo ${path%${suffix}} # Also strip off extension, if any. +} + diff --git a/examples/functions/basename2 b/examples/functions/basename2 new file mode 100644 index 0000000..a42231c --- /dev/null +++ b/examples/functions/basename2 @@ -0,0 +1,43 @@ +#From: "Grigoriy Strokin" <grg@philol.msu.ru> +#Newsgroups: comp.unix.shell +#Subject: fast basename and dirname functions for BASH/SH +#Date: Sat, 27 Dec 1997 21:18:40 +0300 +# +#Please send your comments to grg@philol.msu.ru + +function basename() +{ + local name="${1##*/}" + echo "${name%$2}" +} + +function dirname() +{ + local dir="${1%${1##*/}}" + [ "${dir:=./}" != "/" ] && dir="${dir%?}" + echo "$dir" +} + +# Two additional functions: +# 1) namename prints the basename without extension +# 2) ext prints extension of a file, including "." + +function namename() +{ + local name=${1##*/} + local name0="${name%.*}" + echo "${name0:-$name}" +} +function ext() +{ + local name=${1##*/} + local name0="${name%.*}" + local ext=${name0:+${name#$name0}} + echo "${ext:-.}" +} + + + + + + diff --git a/examples/functions/coproc.bash b/examples/functions/coproc.bash new file mode 100644 index 0000000..61dc8d7 --- /dev/null +++ b/examples/functions/coproc.bash @@ -0,0 +1,108 @@ +# coprocess.bash +# +# vi:set sts=2 sw=2 ai: +# + +coprocess_pid= + +# +# coprocess - Start, control, and end coprocesses. +# +function coprocess () +{ + while (( $# > 0 )) ; do + case "$1" in + # + # coprocess close + # + c|cl|clo|clos|close) + shift + exec 61>&- 62<&- + coprocess_pid= + if [ "$1" = "-SIGPIPE" ] ; then + # Only print message in an interactive shell + case "$-" in + *i*) + echo 'SIGPIPE' >&2 + ;; + esac + return 1 + fi + return 0 + ;; + + # + # coprocess open + # + o|op|ope|open) + shift + local fifo="/var/tmp/coprocess.$$.$RANDOM" + + local cmd="/bin/bash" + if (( $# > 0 )) ; then + cmd="$@" + fi + + mkfifo "$fifo.in" || return $? + mkfifo "$fifo.out" || { + ret=$? + rm -f "$fifo.in" + return $? + } + + ( "$@" <$fifo.in >$fifo.out ; rm -f "$fifo.in" "$fifo.out" ) & + coprocess_pid=$! + exec 61>$fifo.in 62<$fifo.out + return 0 + ;; + + # + # coprocess print - write to the coprocess + # + p|pr|pri|prin|print) + shift + local old_trap=$(trap -p SIGPIPE) + trap 'coprocess close -SIGPIPE' SIGPIPE + if [ $# -eq 1 ] && [ "$1" = "--stdin" ] ; then + cat >&61 + else + echo "$@" >&61 + fi + local ret=$? + eval "$old_trap" + return $ret + ;; + + # + # coprocess read - read from the coprocess + # + r|re|rea|read) + shift + local old_trap=$(trap -p SIGPIPE) + trap '_coprocess_close -SIGPIPE' SIGPIPE + builtin read "$@" <&62 + local ret=$? + eval "$old_trap" + return $ret + ;; + + s|st|sta|stat|statu|status) + if [ -z "$coprocess_pid" ] ; then + echo 'no active coprocess' + return 1 + else + echo " coprocess is active [$coprocess_pid]" + return 0 + fi + ;; + + *) + coprocess print "$@" + return $? + ;; + esac + shift + done + coprocess status + return $? +} diff --git a/examples/functions/coshell.README b/examples/functions/coshell.README new file mode 100644 index 0000000..9675cda --- /dev/null +++ b/examples/functions/coshell.README @@ -0,0 +1,53 @@ +Date: Fri, 21 Sep 2001 14:50:29 -0400 +From: "Jason M. Felice" <jfelice@cronosys.com> +To: bash-maintainers@gnu.org, chet@po.cwru.edu +Subject: Bash co-processes functions +Message-ID: <20010921145029.A6093@argo.eraserhead.net> +Mime-Version: 1.0 + +Attached to this message you will find coprocess.bash and coshell.bash. +Here's a brief synopsis of use: + +coprocess open telnet localhost +while coprocess read il ; do + echo "$il" + case "$il" in + *ogin:*) + coprocess print 'user' + ;; + *ord:*) + echo 'pass' |coprocess print --stdin + ;; + *$ *) + coprocess print 'exit' + break + ;; + esac +done +coprocess close + +And here's an example of the coshell function: + +coshell open ssh -l root otherbox +coshell eval hostname +coshell ls -l +if coshell test -d /tmp ; then echo 'otherbox has a /tmp!' ; fi + +coshell sendfile /var/lib/upgrade.rpm /tmp/test.rpm || exit $? +coshell eval rpm -ivh /tmp/test.rpm || exit $? +coshell eval rm -f /tmp/test.rpm || exit $? +coshell close +exit 0 + +There are a few minor issues that I'd like to work out, but it works well +enough for me ;-) The issues are: + +- Shell quoting issue with 'coshell eval' commands - need to somehow + re-quote words. +- Interactive commands hang 'coshell eval', tried redirecting in </dev/null + to executed command, but it caused strange shell exit problems. +- Some way to copy stdin from local coshell eval to remote shell. Probably + logically impossible, but would be wonderfully useful. + +I'm using it for writing scripts to publish websites and other scripts to +co-located servers. diff --git a/examples/functions/coshell.bash b/examples/functions/coshell.bash new file mode 100644 index 0000000..dc177b3 --- /dev/null +++ b/examples/functions/coshell.bash @@ -0,0 +1,127 @@ +# vi:set sts=2 sw=2 ai: +# +# coshell.bash - Control shell coprocesses (see coprocess.bash). +# + +function coshell () +{ + while (( $# > 0 )) ; do + case "$1" in + # + # coshell open + # + o|op|ope|open) + shift + coprocess open "$@" + local ret=$? + + # This should eat any ssh error messages or what not. + coshell eval : >/dev/null 2>&1 + return $ret + ;; + + # + # coshell close + # + c|cl|clo|close) + shift + coprocess close "$@" + return $? + ;; + + # + # coshell eval + # + e|ev|eva|eval) + shift + local cookie=$RANDOM + if (( $# == 0 )) ; then + echo "coshell eval: no argumentsl" >&2 + return 1 + fi + if [ x$coprocess_pid = x ] ; then + echo "coshell eval: no active coshell" >&2 + return 1 + fi + + coprocess print "$@" + coprocess print "coprocess_rc=\$?" + coprocess print "printf 'coprocess-$cookie----\n%d\n' \$coprocess_rc" + if [ x$coprocess_pid = x ] ; then + return 0 + fi + + local ol + while coprocess read ol ; do + case "$ol" in + *coprocess-$cookie----*) + ol="${ol%coprocess-$cookie----}" + echo -n "$ol" + break + ;; + esac + echo "$ol" + done + coprocess read ol + return $ol + ;; + + # + # coshell sendfile + # + s|se|sen|send|sendf|sendfi|sendfil|sendfile) + shift + if (( $# != 2 )) ; then + echo "coshell sendfile: syntax is 'coshell sendfile SRC TARGET'" >&2 + return 1 + fi + if [ x$coprocess_pid = x ] ; then + echo "coshell sendfile: no active coshell" >&2 + return 1 + fi + + local target=$2 + if coshell test -d "$target" ; then + target="$target/${1##*/}" + fi + + coprocess print "uudecode <<END_OF_FILE" + uuencode -m "$target" <$1 |coprocess print --stdin + coshell eval "END_OF_FILE" + return $? + ;; + + # + # coshell getfile + # + g|ge|get|getf|getfi|getfil|getfile) + shift + if (( $# != 2 )) ; then + echo "coshell getfile: syntax is 'coshell getfile SRC TARGET'" >&2 + return 1 + fi + if [ x$coprocess_pid = x ] ; then + echo "coshell getfile: no active coshell" >&2 + return 1 + fi + + local target=$2 + if test -d "$target" ; then + target="$target/${1##*/}" + fi + + coshell eval uuencode -m "$target" "<" "$1" |uudecode + return $? + ;; + + *) + coshell eval "$@" + return $? + ;; + esac + shift + done + coprocess status + return $? +} + diff --git a/examples/functions/csh-compat b/examples/functions/csh-compat new file mode 100644 index 0000000..b8dcf8f --- /dev/null +++ b/examples/functions/csh-compat @@ -0,0 +1,48 @@ +# C-shell compatabilty package. +# setenv VAR VALUE +function setenv () +{ + export $1="$2" +} + +function unsetenv () +{ + unset $1 +} + +# Can't write foreach yet. Need pattern matching, and a few extras. +function foreach () { +echo 'Can'\''t do `foreach'\'' yet. Type "help for".' +} + +# Make this work like csh's. Special case "term" and "path". +#set () { +#} + +chdir () +{ + builtin cd "$@" +} + +# alias - convert csh alias commands to bash functions +# from Mohit Aron <aron@cs.rice.edu> +# posted to usenet as <4i5p17$bnu@larry.rice.edu> +function alias () +{ + if [ "x$2" = "x" ] + then + declare -f $1 + else + case $2 in + *[#\!]*) + comm=$(echo $2 | sed 's/\\!\*/\"$\@\"/g + s/\\!:\([1-9]\)/\"$\1\"/g + s/#/\\#/g') + ;; + *) + comm="$2 \"\$@\"" ;; + esac + + eval function $1 \(\) "{" command "$comm" "; }" + fi +} diff --git a/examples/functions/dirfuncs b/examples/functions/dirfuncs new file mode 100644 index 0000000..3958bbe --- /dev/null +++ b/examples/functions/dirfuncs @@ -0,0 +1,142 @@ +# +# Directory manipulation functions from the book 'The Korn Shell' +# Modified for use with bash Mon Apr 18 08:37 1994 by +# Ken Konecki (kenk@wfg.com) +# +# Modified by Chet Ramey +# +# This could stand to have calls to `select' added back in +# + +alias integer="declare -i" + +integer _push_max=${CDSTACK-31} _push_top=${CDSTACK-31} + +unalias cd +# alias cd=_cd + +# Display directory stack -- $HOME display as ~ +dirs() +{ + dir="${PWD#$HOME/}" + case $dir in + $HOME) dir=\~ ;; + /*) ;; + *) dir=\~/$dir ;; + esac + + integer i=_push_top + integer n=1 + + echo "$n) $dir" + while let "i < $_push_max" + do + n=n+1 + eval "echo \$n\) \$_push_stack_$i" + i=i+1 + done +} + +# Change directory and put directory on front of stack +cd() +{ + typeset dir= + integer n=0 type=4 i + case $1 in + -|-1|2) # cd - + n=_push_top type=1 + ;; + -[1-9]|-[1-9][0-9]) # cd -n + n=_push_top+${1#-}-1 type=2 + ;; + + 1) # keep present directory + echo "$PWD" + return + ;; + + [2-9]|[1-9][0-9]) # cd n + n=_push_top+${1}-2 type=2 + ;; + + *) + if let "_push_top <= 0"; then + type=3 n=_push_max + fi + ;; + esac + + if let "type < 3"; then + if let "n >= _push_max"; then + echo cd: Directory stack not that deep + return 1 + else + eval dir=\${_push_stack_$n} + fi + fi + + case $dir in + ~*) dir=$HOME${dir#\~} ;; + esac + + cd2 ${dir:-$@} > /dev/null || return 1 + dir=${OLDPWD#$HOME/} + case $dir in + $HOME) dir=\~ ;; + /*) ;; + *) dir=\~/$dir ;; + esac + + case $type in + 1) # swap first two elements + eval _push_stack_$_push_top=\$dir ;; + + 2|3) # put $dir on top and shift down by one until top + i=_push_top + unset _dirlist + while let "i < $_push_max" ; do + eval _dirlist=\"\$_dirlist \$_push_stack_$i\" + i=i+1 + done + + i=_push_top + for dir in "$dir" ${_dirlist} ; do + let "i > n" && break + eval _push_stack_$i=\$dir + i=i+1 + done + ;; + 4) # push name + _push_top=_push_top-1; + eval _push_stack_$_push_top=\$dir + ;; + esac + + echo "$PWD" + +} + +# Menu-driven change directory command +function mcd +{ + dirs + echo -n "Select by number or enter a name: " + read + cd $REPLY +} + + +# Emulate ksh cd substitution +cd2() +{ + case "$#" in + 0) builtin cd "$HOME" ;; + 1) builtin cd "$1" ;; + 2) newDir=$(echo $PWD | sed -e "s:$1:$2:g") + case "$newDir" in + $PWD) echo "bash:: cd: bad substitution" >&2 ; return 1 ;; + *) builtin cd "$newDir" ;; + esac ;; + *) echo "bash: cd: wrong arg count" 1>&2 ; return 1 ;; + esac +} diff --git a/examples/functions/dirname b/examples/functions/dirname new file mode 100644 index 0000000..ccb8c84 --- /dev/null +++ b/examples/functions/dirname @@ -0,0 +1,21 @@ +# Date: Fri, 11 Oct 91 11:22:36 edt +# From: friedman@gnu.ai.mit.edu +# To: bfox@gnu.ai.mit.edu + +# A replacement for dirname(1). This one appears less often on some +# systems I use than basename(1), and I really depend on it for some +# things. Usage: dirname [path] +function dirname () +{ + local dir="$1" + local tdir="${dir%/}" + + # Strip trailing '/' characters from dir (unusual that this should + # ever occur, but dirname(1) seems to deal with it.) + while [ "${tdir}" != "${dir}" ]; do + tdir="${dir}" + dir="${tdir%/}" + done + + echo "${dir%/*}" +} diff --git a/examples/functions/emptydir b/examples/functions/emptydir new file mode 100644 index 0000000..412af5b --- /dev/null +++ b/examples/functions/emptydir @@ -0,0 +1,28 @@ +#! /bin/bash +# +#Derived from: +# +#From: damercer@mmm.com (Dan Mercer) +#Newsgroups: comp.unix.admin,comp.unix.shell,comp.unix.programmer,comp.sys.sun.admin +#Subject: Re: Command to find out if a directory is empty +#Date: 17 Aug 2000 14:35:56 GMT +#Message-ID: <8ngt8c$fmr$1@magnum.mmm.com> + +# usage: emptydir [dirname] ; default dirname is "." + +emptydir() +{ + typeset file dir=${1:-.} + [[ -d $dir ]] || { + echo "$FUNCNAME: $dir is not a directory" >&2 + return 2 + } + for file in $dir/.* $dir/* + do + case ${file#$dir/} in + .|..) ;; + \*) [[ -e $file ]];let $?;return;; + *) return 1;; + esac + done +} diff --git a/examples/functions/exitstat b/examples/functions/exitstat new file mode 100644 index 0000000..f49ebf5 --- /dev/null +++ b/examples/functions/exitstat @@ -0,0 +1,22 @@ +# Contributed by Noah Friedman and Roland McGrath. + +# To be run by the PROMPT_COMMAND variable, so that one can see what +# the exit status of processes are. + +function check_exit_status () +{ + local status="$?" + local signal="" + + if [ ${status} -ne 0 ] && [ ${status} != 128 ]; then + # If process exited by a signal, determine name of signal. + if [ ${status} -gt 128 ]; then + signal="$(builtin kill -l $((${status} - 128)) 2>/dev/null)" + if [ "$signal" ]; then signal="($signal)"; fi + fi + echo "[Exit ${status} ${signal}]" 1>&2 + fi + return 0 +} + +PROMPT_COMMAND=check_exit_status diff --git a/examples/functions/external b/examples/functions/external new file mode 100644 index 0000000..c2e52cd --- /dev/null +++ b/examples/functions/external @@ -0,0 +1,50 @@ +# Contributed by Noah Friedman. + +# To avoid using a function in bash, you can use the `builtin' or +# `command' builtins, but neither guarantees that you use an external +# program instead of a bash builtin if there's a builtin by that name. So +# this function can be used like `command' except that it guarantees the +# program is external by first disabling any builtin by that name. After +# the command is done executing, the state of the builtin is restored. +function external () +{ + local state="" + local exit_status + + if builtin_p "$1"; then + state="builtin" + enable -n "$1" + fi + + command "$@" + exit_status=$? + + if [ "$state" = "builtin" ]; then + enable "$1" + fi + + return ${exit_status} +} + +# What is does is tell you if a particular keyword is currently enabled as +# a shell builtin. It does NOT tell you if invoking that keyword will +# necessarily run the builtin. For that, do something like +# +# test "$(builtin type -type [keyword])" = "builtin" +# +# Note also, that disabling a builtin with "enable -n" will make builtin_p +# return false, since the builtin is no longer available. +function builtin_p () +{ + local word + + set $(builtin type -all -type "$1") + + for word in "$@" ; do + if [ "${word}" = "builtin" ]; then + return 0 + fi + done + + return 1 +} diff --git a/examples/functions/fact b/examples/functions/fact new file mode 100644 index 0000000..97efd49 --- /dev/null +++ b/examples/functions/fact @@ -0,0 +1,13 @@ +# Who said shells can't use recursion? Here is a factorial function. +# You call it with a number as an argument, and it returns the factorial +# of that number. + +fact () +{ + local num=$1; + if [ "$num" = 1 ] ; then + echo 1 + return ; + fi; + echo $(( $num * $(fact $(( $num - 1 )) ) )) +} diff --git a/examples/functions/fstty b/examples/functions/fstty new file mode 100644 index 0000000..a770d84 --- /dev/null +++ b/examples/functions/fstty @@ -0,0 +1,59 @@ +# +# A function that works as a front end for both stty and the `bind' +# builtin, so the tty driver and readline see the same changes +# + +# +# Convert between the stty ^H control character form and the readline \C-H +# form +# +cvt() +{ + echo "$@" | cat -v | sed 's/\^/\\C-/' +} + +# +# stty front-end. Parses the argument list and creates two command strings, +# one for stty, another for bind. +# +fstty() +{ + local cmd="" bargs="" + local e + + while [ $# -gt 0 ] + do + case "$1" in + -a) cmd="$cmd everything" + ;; + erase) shift; + e=$(cvt "$1") + cmd="$cmd erase $1" + bargs="$bargs '\"$e\": backward-delete-char'" + ;; + kill) shift + e=$(cvt "$1") + cmd="$cmd kill $1" + bargs="$bargs '\"$e\": unix-line-discard'" + ;; + werase) shift; + e=$(cvt "$1") + cmd="$cmd erase $1" + bargs="$bargs '\"$e\": backward-kill-word'" + ;; + lnext) shift; + e=$(cvt "$1") + cmd="$cmd erase $1" + bargs="$bargs '\"$e\": quoted-insert'" + ;; + *) cmd="$cmd $1" + ;; + esac + shift + done + + command stty $cmd + if [ -n "$bargs" ]; then + builtin bind $bargs + fi +} diff --git a/examples/functions/func b/examples/functions/func new file mode 100644 index 0000000..710f643 --- /dev/null +++ b/examples/functions/func @@ -0,0 +1,27 @@ +# +# func -- print out definitions for functions named by arguments +# +# usage: func name [name ...] +# +# Chet Ramey +# chet@ins.CWRU.Edu +func() +{ + local status=0 + + if [ $# -eq 0 ] ; then + echo "usage: func name [name...]" 1>&2 + return 1 + fi + + for f + do + if [ "$(builtin type -type $f)" != "function" ] ; then + echo "func: $f: not a function" 1>&2 + status=1 # one failed + continue + fi + builtin type $f | sed 1d + done + return $status +} diff --git a/examples/functions/gethtml b/examples/functions/gethtml new file mode 100644 index 0000000..2eec1d8 --- /dev/null +++ b/examples/functions/gethtml @@ -0,0 +1,35 @@ +# +# get_html -- get a web page from a remote server +# +# Original Author: Jeff Korn <jlk@cs.princeton.edu> +# Modified for bash by Chet Ramey <chet@po.cwru.edu> +# +# Example: get_html cnswww.cns.cwru.edu /~chet/ | more + +get_html() +{ + local host port + + (($# < 2)) && { + echo "usage: $FUNCNAME hostname path [port]" >&2 + return 1 + } + + host="$1" + port="${3:-80}" + + exec 3<> /dev/tcp/$host/$port || { + echo "$FUNCNAME: $host/$port: cannot connect" >&2 + exit 1 + } + + echo -e "GET $2 HTTP/1.0\n" >&3 + + cat <&3 + + exec 3<&- + + return 0 +} + +get_html "$@" diff --git a/examples/functions/getoptx.bash b/examples/functions/getoptx.bash new file mode 100644 index 0000000..d402c7d --- /dev/null +++ b/examples/functions/getoptx.bash @@ -0,0 +1,301 @@ +#From: "Grigoriy Strokin" <grg@philol.msu.ru> +#Newsgroups: comp.unix.shell +#Subject: BASH: getopt function that parses long-named options +#Date: Mon, 22 Dec 1997 20:35:18 +0300 + +#Hi, I have written a BASH function named getoptex, that is like bash builtin +#"getopts", but does parse long-named options and optional arguments. It only +#uses builtin bash commands, so it is very fast. In order to use it in your +#bash scripts, include a command ". getopt.sh" (<dot> getopt.sh) to the file +#containing your script, and that will define functions getopt, getoptex, and +#optlistex (the file getopt.sh with its detailed description is listed +#below). + +#*** file getopt.sh *** + +#! /bin/bash +# +# getopt.sh: +# functions like getopts but do long-named options parsing +# and support optional arguments +# +# Version 1.0 1997 by Grigoriy Strokin (grg@philol.msu.ru), Public Domain +# Date created: December 21, 1997 +# Date modified: December 21, 1997 +# +# IMPORTANT FEATURES +# +# 1) Parses both short and long-named options +# 2) Supports optional arguments +# 3) Only uses bash builtins, thus no calls to external +# utilities such as expr or sed is done. Therefore, +# parsing speed is high enough +# +# +# DESCRIPTION +# +# FUNCTION getopt +# Usage: getopt OPTLIST {"$@"|ALTERNATIVE_PARAMETERS} +# +# like getopts, but parse options with both required and optional arguments, +# Options with optional arguments must have "." instead of ":" after them. +# Furthemore, a variable name to place option name cannot be specified +# and is always placed in OPTOPT variable +# +# This function is provided for compatibility with getopts() +# OPTLIST style, and it actually calls getoptex (see bellow) +# +# NOTE that a list of parameters is required and must be either "$@", +# if processing command line arguments, or some alternative parameters. +# +# FUNCTION getoptex +# Usage: getoptex OPTION_LIST {"$@"|ALTERNATIVE_PARAMETERS} +# +# like getopts, but parse long-named options. +# +# Both getopt and getoptex return 0 if an option has been parsed, +# and 1 if all options are already parsed or an error occured +# +# Both getopt and getoptex set or test the following variables: +# +# OPTERR -- tested for whether error messages must be given for invalid +options +# +# OPTOPT -- set to the name of an option parsed, +# or to "?" if no more options or error +# OPTARG -- set to the option argument, if any; +# unset if ther is no argument; +# on error, set to the erroneous option name +# +# OPTIND -- Initialized to 1. +# Then set to the number of the next parameter to be parsed +# when getopt or getoptex will be called next time. +# When all options are parsed, contains a number of +# the first non-option argument. +# +# +# OPTOFS -- If a parameter number $OPTIND containg an option parsed +# does not contain any more options, OPTOFS is unset; +# otherwise, OPTOFS is set to such a number of "?" signs +# which is equal to the number of options parsed +# +# You might not set variables OPTIND and OPTOFS yourself +# unless you want to parse a list of parameters more than once. +# Otherwise, you whould unset OPTIND (or set it to 1) +# and unset OPTOFS each time you want to parse a new parameters +list +# +# Option list format is DIFFERENT from one for getopts or getopt. +getopts-style +# option list can be converted to getoptex-style using a function optlistex +# (see bellow) +# +# DESCRIPTION of option list used with getoptex: +# Option names are separated by whitespace. Options consiting of +# more than one character are treated as long-named (--option) +# +# Special characters can appear at the and of option names specifying +# whether an argument is required (default is ";"): +# ";" (default) -- no argument +# ":" -- required argument +# "," -- optional argument +# +# For example, an option list "a b c help version f: file: separator." +# defines the following options: +# -a, -b, -c, --help, --version -- no argument +# -f, --file -- argument required +# --separator -- optional argument +# +# FUNCTION optlistex +# Usage new_style_optlist=`optlistex OLD_STYLE_OPTLIST` +# +# Converts getopts-style option list in a format suitable for use with getoptex +# Namely, it inserts spaces after each option name. +# +# +# HOW TO USE +# +# In order o use in your bash scripts the functions described, +# include a command ". getopt.sh" to the file containing the script, +# which will define functions getopt, getoptex, and optlistex +# +# EXAMPLES +# +# See files 'getopt1' and 'getopt2' that contain sample scripts that use +# getopt and getoptex functions respectively +# +# +# Please send your comments to grg@philol.msu.ru + +function getoptex() +{ + let $# || return 1 + local optlist="${1#;}" + let OPTIND || OPTIND=1 + [ $OPTIND -lt $# ] || return 1 + shift $OPTIND + if [ "$1" != "-" ] && [ "$1" != "${1#-}" ] + then OPTIND=$[OPTIND+1]; if [ "$1" != "--" ] + then + local o + o="-${1#-$OPTOFS}" + for opt in ${optlist#;} + do + OPTOPT="${opt%[;.:]}" + unset OPTARG + local opttype="${opt##*[^;:.]}" + [ -z "$opttype" ] && opttype=";" + if [ ${#OPTOPT} -gt 1 ] + then # long-named option + case $o in + "--$OPTOPT") + if [ "$opttype" != ":" ]; then return 0; fi + OPTARG="$2" + if [ -z "$OPTARG" ]; + then # error: must have an agrument + let OPTERR && echo "$0: error: $OPTOPT must have an argument" >&2 + OPTARG="$OPTOPT"; + OPTOPT="?" + return 1; + fi + OPTIND=$[OPTIND+1] # skip option's argument + return 0 + ;; + "--$OPTOPT="*) + if [ "$opttype" = ";" ]; + then # error: must not have arguments + let OPTERR && echo "$0: error: $OPTOPT must not have arguments" >&2 + OPTARG="$OPTOPT" + OPTOPT="?" + return 1 + fi + OPTARG=${o#"--$OPTOPT="} + return 0 + ;; + esac + else # short-named option + case "$o" in + "-$OPTOPT") + unset OPTOFS + [ "$opttype" != ":" ] && return 0 + OPTARG="$2" + if [ -z "$OPTARG" ] + then + echo "$0: error: -$OPTOPT must have an argument" >&2 + OPTARG="$OPTOPT" + OPTOPT="?" + return 1 + fi + OPTIND=$[OPTIND+1] # skip option's argument + return 0 + ;; + "-$OPTOPT"*) + if [ $opttype = ";" ] + then # an option with no argument is in a chain of options + OPTOFS="$OPTOFS?" # move to the next option in the chain + OPTIND=$[OPTIND-1] # the chain still has other options + return 0 + else + unset OPTOFS + OPTARG="${o#-$OPTOPT}" + return 0 + fi + ;; + esac + fi + done + echo "$0: error: invalid option: $o" + fi; fi + OPTOPT="?" + unset OPTARG + return 1 +} +function optlistex +{ + local l="$1" + local m # mask + local r # to store result + while [ ${#m} -lt $[${#l}-1] ]; do m="$m?"; done # create a "???..." mask + while [ -n "$l" ] + do + r="${r:+"$r "}${l%$m}" # append the first character of $l to $r + l="${l#?}" # cut the first charecter from $l + m="${m#?}" # cut one "?" sign from m + if [ -n "${l%%[^:.;]*}" ] + then # a special character (";", ".", or ":") was found + r="$r${l%$m}" # append it to $r + l="${l#?}" # cut the special character from l + m="${m#?}" # cut one more "?" sign + fi + done + echo $r +} +function getopt() +{ + local optlist=`optlistex "$1"` + shift + getoptex "$optlist" "$@" + return $? +} + +#************************************** +# cut here +#************************************** +#*** (end of getopt.sh) *** + + +#*** file getopt1 *** + +#! /bin/bash +# getopt1: +# Sample script using the function getopt +# +# Type something like "getopt1 -ab -d 10 -e20 text1 text2" +# on the command line to see how it works +# +# See getopt.sh for more information +#. getopt.sh +#echo Using getopt to parse arguments: +#while getopt "abcd:e." "$@" +#do +# echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}" +#done +#shift $[OPTIND-1] +#for arg in "$@" +#do +# echo "Non option argument <$arg>" +#done +# +#************************************** +# cut here +#************************************** +#*** (end of getopt1) *** +# +# +#*** file getopt2 *** +# +#! /bin/bash +# getopt2: +# Sample script using the function getoptex +# +# Type something like "getopt2 -ab -d 10 -e20 --opt1 --opt4=100 text1 text2" +# to see how it works +# +# See getopt.sh for more information +. getopt.sh +#echo Using getoptex to parse arguments: +#while getoptex "a; b; c; d: e. opt1 opt2 opt3 opt4: opt5." "$@" +#do +# echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}" +#done +#shift $[OPTIND-1] +#for arg in "$@" +#do +# echo "Non option argument <$arg>" +#done +# +#************************************** +# cut here +#************************************** +#*** (end of getopt2) *** + diff --git a/examples/functions/inetaddr b/examples/functions/inetaddr new file mode 100644 index 0000000..f3e228f --- /dev/null +++ b/examples/functions/inetaddr @@ -0,0 +1,60 @@ +# +# inet2hex - Internet address conversion, dotted-decimal to hex +# +inet2hex () +{ + local IFS + + IFS=. + set -- $1 + + if (( $# != 4 )); then + echo "inet2hex: incorrect input format: $1" >&2 + echo "inet2hex: usage: inet2hex XX.XX.XX.XX" >&2 + return 2 + fi + + printf "0x%02x%02x%02x%02x\n" $1 $2 $3 $4 +} + +# +# hex2inet - Internet address conversion, hex to dotted-decimal +# +hex2inet () +{ + local x1 x2 x3 x4 + local rev + + OPTIND=1 + while getopts "r" o + do + case "$o" in + r) rev=true;; + *) echo "hex2inet: usage: hex2inet [-r] [0x]XXXXXXXX" >&2 ; exit 2;; + esac + done + shift $(( $OPTIND - 1 )) + + case "$1" in + 0x*) h=${1#??} ;; + *) h=$1 ;; + esac + + if (( ${#h} != 8 )); then + echo "hex2inet: $h not in inet format" >&2 + echo "hex2inet: usage: hex2inet [0x]XXXXXXXX" >&2 + return 2 + fi + + x1=$(( 0x${h:0:2} )) + x2=$(( 0x${h:2:2} )) + x3=$(( 0x${h:4:2} )) + x4=$(( 0x${h:6:2} )) + + if [ -z "$rev" ] ; then + printf "%d.%d.%d.%d\n" $x1 $x2 $x3 $x4 + else + printf "%d.%d.%d.%d\n" $x4 $x3 $x2 $x1 + fi + return 0 +} diff --git a/examples/functions/inpath b/examples/functions/inpath new file mode 100644 index 0000000..cb4c93d --- /dev/null +++ b/examples/functions/inpath @@ -0,0 +1,14 @@ +inpath() +{ + local PROG + path=$(echo $PATH | sed 's/^:/.:/ + s/::/:.:/g + s/:$/:./ + s/:/ /g') + + for x in $path + do + [ -x $x/$1 ] && { PROG=$x/$1; break; } + done + [ -n "$PROG" ] +} diff --git a/examples/functions/isnum.bash b/examples/functions/isnum.bash new file mode 100644 index 0000000..b733965 --- /dev/null +++ b/examples/functions/isnum.bash @@ -0,0 +1,52 @@ +#From: jrmartin@rainey.blueneptune.com (James R. Martin) +#Newsgroups: comp.unix.shell +#Subject: Re: testing user input on numeric or character value +#Date: 26 Nov 1997 01:28:43 GMT + +# isnum returns True if its argument is a valid number, +# and False (retval=1) if it is any other string. +# The first pattern requires a digit before the decimal +# point, and the second after the decimal point. + +# BASH NOTE: make sure you have executed `shopt -s extglob' before +# trying to use this function, or it will not work + +isnum() # string +{ + case $1 in + ?([-+])+([0-9])?(.)*([0-9])?([Ee]?([-+])+([0-9])) ) + return 0;; + ?([-+])*([0-9])?(.)+([0-9])?([Ee]?([-+])+([0-9])) ) + return 0;; + *) return 1;; + esac +} + +isnum2() # string +{ + case $1 in + ?([-+])+([[:digit:]])?(.)*([[:digit:]])?([Ee]?([-+])+([[:digit:]])) ) + return 0;; + ?([-+])*([[:digit:]])?(.)+([[:digit:]])?([Ee]?([-+])+([[:digit:]])) ) + return 0;; + *) return 1;; + esac +} + +isint() # string +{ + case $1 in + ?([-+])+([0-9]) ) + return 0;; + *) return 1;; + esac +} + +isint2() # string +{ + case $1 in + ?([-+])+([[:digit:]]) ) + return 0;; + *) return 1;; + esac +} diff --git a/examples/functions/isnum2 b/examples/functions/isnum2 new file mode 100644 index 0000000..e2e7a5f --- /dev/null +++ b/examples/functions/isnum2 @@ -0,0 +1,22 @@ +isnum2() +{ + case "$1" in + '[-+]' | '') return 1;; # empty or bare `-' or `+' + [-+]*[!0-9]*) return 1;; # non-digit with leading sign + [-+]*) return 0;; # OK + *[!0-9]*) return 1;; # non-digit + *) return 0;; # OK + esac +} + +# this one handles floating point +isnum3() +{ + case "$1" in + '') return 1;; # empty + *[!0-9.+-]*) return 1;; # non-digit, +, -, or . + *?[-+]*) return 1;; # sign as second or later char + *.*.*) return 1;; # multiple decimal points + *) return 0;; # OK + esac +} diff --git a/examples/functions/isvalidip b/examples/functions/isvalidip new file mode 100644 index 0000000..0b2dafe --- /dev/null +++ b/examples/functions/isvalidip @@ -0,0 +1,14 @@ +# Thanks to Chris F. A. Johnson <c.f.a.johnson@rogers.com> for this one +is_validip() +{ + case "$*" in + ""|*[!0-9.]*|*[!0-9]) return 1 ;; + esac + + local IFS=. + set -- $* + + [ $# -eq 4 ] && + [ ${1:-666} -le 255 ] && [ ${2:-666} -le 255 ] && + [ ${3:-666} -le 255 ] && [ ${4:-666} -le 254 ] +} diff --git a/examples/functions/jdate.bash b/examples/functions/jdate.bash new file mode 100644 index 0000000..9488ed9 --- /dev/null +++ b/examples/functions/jdate.bash @@ -0,0 +1,78 @@ +#From: damatex@CAM.ORG (Mario Boudreault) +#Newsgroups: comp.unix.shell +#Subject: JULIAN DATE CONVERSION SUB +#Date: 4 Aug 1995 10:23:28 -0400 +#Message-ID: <3vtah0$jb3@ocean.CAM.ORG> + +#For those using shells and who want to convert dates to a julian number +#here is a shell script (wihtout validation) that can be used as a base +#program for your shell scripts. + +#Special thanks to Ed Ferguson@ti.com who sent me the algorithm to compute +#that date. + +# +# MODIFIED BY CHET RAMEY TO CONVERT TO bash v2 SYNTAX +# + +# cnvdate - Conversion de dates en julienne et vice et versa... +# +# Par : Mario Boudreault Damatex Inc Montreal, Canada +# Date: 2 Aout 1995 +# Rev.: 2 Aout 1995 +# +# Usage: +# cvdate [-j] YYYMMDD pour convertir en nbre de jours +# cvdate -d {julian number} pour convertir en AAAAMMJJ +# + +jul_date() +{ + # + # Separe ANNEE, MOIS et JOUR... + # + YEAR=`echo $DATE | awk ' { print substr($0,1,4) } '` + MONTH=`echo $DATE | awk ' { print substr($0,5,2) } '` + DAY=`echo $DATE | awk ' { print substr($0,7,2) } '` + # + # Execute la formule magique... + # + A=$(( $DAY - 32075 + 1461 * ( $YEAR + 4800 - ( 14 - $MONTH ) / 12 ) \ + / 4 + 367 * ( $MONTH - 2 + ( 14 - $MONTH ) / 12 * 12 ) / 12 - \ + 3 * ( ( $YEAR + 4900 - ( 14 - $MONTH ) / 12 ) / 100 ) / 4 )) + echo $A +} + +day_date() +{ + TEMP1=$(( $DATE + 68569 )) + TEMP2=$(( 4 * $TEMP1 / 146097 )) + TEMP1=$(( $TEMP1 - ( 146097 * $TEMP2 + 3 ) / 4 )) + Y=$(( 4000 * ( $TEMP1 + 1 ) / 1461001 )) + TEMP1=$(( $TEMP1 - 1461 * $Y / 4 + 31 )) + M=$(( 80 * $TEMP1 / 2447 )) + D=$(( $TEMP1 - 2447 * $M / 80 )) + TEMP1=$(( $M / 11 )) + M=$(( $M + 2 - 12 * $TEMP1 )) + Y=$(( 100 * ( $TEMP2 - 49 ) + $Y + $TEMP1 )) + M=`echo $M | awk ' { M=$0 ; if ( length($0) == 1 ) M="0"$0 } END { print M } '` + D=`echo $D | awk ' { D=$0 ; if ( length($0) == 1 ) D="0"$0 } END { print D } '` + echo $Y$M$D +} + +# main() + +if [ $# -eq 1 ]; then + DATE=$1 + jul_date +elif [ "$1" = '-j' ]; then + DATE=$2 + jul_date +elif [ "$1" = '-d' ]; then + DATE=$2 + day_date +fi +# +# Termine +# +exit 0 diff --git a/examples/functions/jj.bash b/examples/functions/jj.bash new file mode 100644 index 0000000..212c9ce --- /dev/null +++ b/examples/functions/jj.bash @@ -0,0 +1,12 @@ +jj () +{ + p=$(jobs $1); + echo $p + + case "$p" in + [*) echo matches '[*' + ;; + *) echo not a match\? + ;; + esac +} diff --git a/examples/functions/keep b/examples/functions/keep new file mode 100644 index 0000000..4433b35 --- /dev/null +++ b/examples/functions/keep @@ -0,0 +1,62 @@ +# From: Seth Chaiklin <psykseth@aau.dk> +# To: chet@ins.CWRU.Edu +# Subject: bash functions (sorta) + +# +# keep: +# usage: keep program +# declare the a program should be "kept". i.e. try to fg a stopped one +# and only when that fails start a fresh program. +# + +keep() +{ + case $# in + 1|2) ;; + *) echo "usage: keep [alias] program" 1>&2 ; return 1;; + esac + + # progname + pn=${1##*/} + + # set up an alias for the kept program + if [ $# = 1 ]; then + alias "$pn=fg $1 2>/dev/null || $1" + else + alias "$1=fg $2 2>/dev/null || $2" + fi +} + +# +# unkeep: +# usage: unkeep program +# unset the alias set up by the keep function +# + +unkeep() +{ + if [ $# != 1 ]; then + echo "usage: unkeep program" + return 2 + fi + + # unset the alias for the kept program + unalias "${1##*/}" +} + +# +# kept: +# lists all kept programs in 'alias: program' form +# + +kept() +{ + alias | grep "fg.*2>" | sed "s/alias \(.*\)='fg.*||\(.*\)'$/\1:\2/" +} + + +# some things that should be kept +#keep /usr/local/bin/emacs +#keep e ${EDITOR:-/usr/local/bin/emacs} +#keep edit ${EDITOR:-/usr/local/bin/emacs} +#keep /usr/local/bin/emm diff --git a/examples/functions/ksh-cd b/examples/functions/ksh-cd new file mode 100644 index 0000000..801a490 --- /dev/null +++ b/examples/functions/ksh-cd @@ -0,0 +1,35 @@ +# +# ksh-like `cd': cd [-LP] [dir [change]] +# +cd() +{ + OPTIND=1 + while getopts "LP" opt + do + case $opt in + L|P) CDOPTS="$CDOPTS -$opt" ;; + *) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2;; + esac + done + + shift $(( $OPTIND - 1 )) + + case $# in + 0) builtin cd $CDOPTS "$HOME" ;; + 1) builtin cd $CDOPTS "$@" ;; + 2) old="$1" new="$2" + case "$PWD" in + *$old*) ;; + *) echo "${0##*/}: $FUNCNAME: bad substitution" >&2 ; return 1 ;; + esac + + dir=${PWD//$old/$new} + + builtin cd $CDOPTS "$dir" && echo "$PWD" + + ;; + *) echo "${0##*/}: $FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2 ;; + esac +} diff --git a/examples/functions/ksh-compat-test b/examples/functions/ksh-compat-test new file mode 100644 index 0000000..feee965 --- /dev/null +++ b/examples/functions/ksh-compat-test @@ -0,0 +1,40 @@ +# +# replacements for test/[ that do arithmetic expansion on the operands to +# the arithmetic operators, like ksh. +# +function test() +{ + local -i n1 n3 + case "$#" in + 3) case "$2" in + -lt|-gt|-eq|-ne|-le|-ge) n1=$(( $1 )) + n3=$(( $3 )) + builtin test "$n1" $2 "$n3" + return $?;; + *) builtin test "$@" ;; + esac;; + *) builtin test "$@" ;; + esac +} + +function [() +{ + local -i n1 n3 + case "$#" in + 4) case "$2" in + -lt|-gt|-eq|-ne|-le|-ge) n1=$(( $1 )) + n3=$(( $3 )) + builtin [ "$n1" $2 "$n3" ] + return $?;; + *) builtin [ "$@" ;; + esac;; + *) builtin [ "$@" ;; + esac +} + +q=7 + +[ q -lt 10 ] +echo $? +[ $q -lt 10 ] +echo $? diff --git a/examples/functions/kshenv b/examples/functions/kshenv new file mode 100644 index 0000000..7594f2d --- /dev/null +++ b/examples/functions/kshenv @@ -0,0 +1,228 @@ +# +# .kshenv -- functions and aliases to provide the beginnings of a ksh +# environment for bash. +# +# Chet Ramey +# chet@ins.CWRU.Edu +# +# +# These are definitions for the ksh compiled-in `exported aliases'. There +# are others, but we already have substitutes for them: "history", "type", +# and "hash". +# +alias r="fc -s" +alias functions="typeset -f" +alias integer="typeset -i" +alias nohup="nohup " +alias command="command " +alias stop="kill -s STOP" +alias redirect="command exec" +alias hist="fc" + +# +# An almost-ksh compatible `whence' command. This is as hairy as it is +# because of the desire to exactly mimic ksh (whose behavior was determined +# empirically). +# +# This depends somewhat on knowing the format of the output of the bash +# `builtin type' command. +# + +whence() +{ + local vflag pflag fflag defarg c + local path + + vflag= aflag= pflag= fflag= + path= + if [ "$#" = "0" ] ; then + echo "whence: usage: whence [-afpv] name..." >&2 + return 2 + fi + + OPTIND=1 + while getopts "avfp" c + do + case "$c" in + a) defarg=-a ;; + f) fflag=1 ;; # no-op + p) pflag=1 ;; + v) vflag=1 ;; + ?) echo "whence: $1: unknown option" >&2 + echo "whence: usage: whence [-afpv] name..." >&2 + return 2 ;; + esac + done + + shift $(( $OPTIND - 1 )) + + if [ "$#" = "0" ] ; then + echo "whence: usage: whence [-afpv] name..." >&2 + return 2 + fi + + for cmd + do + if [ "$vflag" ] ; then + if [ -z "$defarg" ]; then + builtin type $cmd | sed 1q + else + if builtin type $defarg -t $cmd | grep 'function$' >/dev/null 2>&1; then + # HAIRY awk script to suppress + # printing of function body -- could + # do it with sed, but I don't have + # that kind of time + builtin type $defarg $cmd | awk ' +BEGIN {printit = 1;} +$1 == "'$cmd'" && $2 == "()" {printit=0; next; } +/^}$/ { if (printit == 0) printit=1 ; else print $0; next ; } +/.*/ { if (printit) print $0; }' + else + builtin type $defarg $cmd + fi + fi + else + path=$(builtin type $defarg -p $cmd) + if [ "$path" ] ; then + echo $path + else + case "$cmd" in + /*) echo "" ;; + *) case "$(builtin type -t $cmd)" in + "") echo "" ;; + *) echo "$cmd" ;; + esac + ;; + esac + fi + fi + done + return 0 +} + +# +# For real ksh homeboy fanatics, redefine the `type' builtin with a ksh +# version. +# +#type() +#{ +# whence -v "$*" +#} + +# +# ksh-like `cd': cd [-LP] [dir [change]] +# +cd() +{ + OPTIND=1 + while getopts "LP" opt + do + case $opt in + L|P) CDOPTS="$CDOPTS -$opt" ;; + *) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2;; + esac + done + + shift $(( $OPTIND - 1 )) + + case $# in + 0) builtin cd $CDOPTS "$HOME" ;; + 1) builtin cd $CDOPTS "$@" ;; + 2) old="$1" new="$2" + case "$PWD" in + *$old*) ;; + *) echo "${0##*/}: $FUNCNAME: bad substitution" >&2 ; return 1 ;; + esac + + dir=${PWD//$old/$new} + + builtin cd $CDOPTS "$dir" && echo "$PWD" + + ;; + *) echo "${0##*/}: $FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2 ;; + esac +} + +# +# ksh print emulation +# +# print [-Rnprsu[n]] [-f format] [arg ...] +# +# - end of options +# -R BSD-style -- only accept -n, no escapes +# -n do not add trailing newline +# -p no-op (no coprocesses) +# -r no escapes +# -s print to the history file +# -u n redirect output to fd n +# -f format printf "$format" "$@" +# + +print() +{ + local eflag=-e + local nflag= fflag= c + local fd=1 + + OPTIND=1 + while getopts "fRnprsu:" c + do + case $c in + R) eflag= ;; + r) eflag= ;; + n) nflag=-n ;; + s) sflag=y ;; + f) fflag=y ;; + u) fd=$OPTARG ;; + p) ;; + esac + done + shift $(( $OPTIND - 1 )) + + if [ -n "$fflag" ]; then + builtin printf "$@" >&$fd + return + fi + + case "$sflag" in + y) builtin history -s "$*" ;; + *) builtin echo $eflag $nflag "$@" >&$fd + esac +} + +# substring function +# this function should be equivalent to the substring built-in which was +# eliminated after the 06/29/84 version +substring () +{ + local lpat flag str #local variables + set -f + case $1 in + -l|-L) + flag=$1 + lpat=$2 + shift 2 + ;; + esac + # test for too few or too many arguments + if [ x"$1" = x ] || [ $# -gt 2 ]; then + print -u2 'substring: bad argument count' + return 1 + fi + str=$1 + if [ x"$flag" = x-l ]; then #substring -l lpat + str=${str#$lpat} + elif [ x"$flag" = x-L ]; then + str=${str##$lpat} #substring -L lpat + fi + + if [ x"$2" != x ]; then + echo ${str%$2} + else + echo $str + fi + + return 0 +} diff --git a/examples/functions/login b/examples/functions/login new file mode 100644 index 0000000..3d59683 --- /dev/null +++ b/examples/functions/login @@ -0,0 +1,11 @@ +# replace the `login' and `newgrp' builtins in old bourne shells + +login() +{ + exec login "$@" +} + +newgrp() +{ + exec newgrp "$@" +} diff --git a/examples/functions/lowercase b/examples/functions/lowercase new file mode 100644 index 0000000..3cf6bde --- /dev/null +++ b/examples/functions/lowercase @@ -0,0 +1,27 @@ +#! /bin/bash +# +# original from +# @(#) lowercase.ksh 1.0 92/10/08 +# 92/10/08 john h. dubois iii (john@armory.com) +# +# conversion to bash v2 syntax done by Chet Ramey + +lowercase() +{ + for file; do + [ -f "$file" ] || continue + filename=${file##*/} + case "$file" in + */*) dirname=${file%/*} ;; + *) dirname=.;; + esac + nf=$(echo $filename | tr A-Z a-z) + newname="${dirname}/${nf}" + if [ "$nf" != "$filename" ]; then + mv "$file" "$newname" + echo "lowercase: $file -> $newname" + else + echo "lowercase: $file not changed." + fi + done +} diff --git a/examples/functions/manpage b/examples/functions/manpage new file mode 100644 index 0000000..60f9aed --- /dev/null +++ b/examples/functions/manpage @@ -0,0 +1,129 @@ +# Written from scratch by Tom Tromey (tromey@cns.caltech.edu) +# +# manpage -- find and print a manual page. +# usage: manpage section name [printing] +# +function manpage () +{ + local i h cmd zot sec + local num="$1" + local page="$2" + local printing="$3" + local mp + + mp="${MANPATH:-/usr/man}" + if [ "$#" -lt 2 ]; then return 1; fi # should print usage + if [ "$num" != "" ]; then + sec="${num%%[a-zA-Z]*}" + else + sec='[168234571lnpo]' + num="$sec" + fi + for i in $(echo "$mp" | tr : ' '); do + if [ ! -d "$i" ]; then continue; fi + file="$i"/man"$sec"/"$page"."$num"* + set $file + file="$1" + if [ -f "$file" ]; then + zot=$(sed 1q "$file") + cmd=${MANROFF:-"nroff -man - | col | cat -s"} + h=${zot##"'"'\"'} + if [ "$h" != "$zot" ]; then + while [ "$h" != "" ]; do + case "$h" in + *e) cmd="${MANEQN:-neqn} | $cmd";; + *r) cmd="refer | $cmd";; + *t) cmd="tbl | $cmd";; + *v) cmd="vgrind | $cmd";; + *) ;; # should print error + esac + h=${h%?} + done + fi + if [ "$printing" != "" ]; then + (cd "$i"; eval "$cmd") < "$file" | ${PAGER:-more} + else + (cd "$i"; eval "$cmd") < "$file" > /tmp/manpage-$$ + ${PAGER:-more} /tmp/manpage-$$ + rm -f /tmp/manpage-$$ + fi + break + fi + done +} + +function whatis_internal () +{ + local j + for j in $(echo "$MANPATH" | tr : ' '); do + if [ -f "$j/whatis" ]; then + eval $2 -i -e "$1" $j/whatis + fi + done +} + +function whatis () +{ + local name=$(basename "$1") + whatis_internal "$name" "grep -w" +} + +function apropos () +{ + whatis_internal "$1" "grep -F" +} + +# Note: "-" and "-t" together not supported. This man could be +# made a lot better, but it does everything I want. +function man () +{ + local PAGER printing mpath MANROFF num + mpath="${MANPATH:-/usr/man}" + while true; do + case "$1" in + -) PAGER=cat + printing= ;; + -t) + MANROFF=${TROFF:-"ptroff -man -t"} + PAGER="${TCAT:-lpr}" + printing=yes ;; + -M) + mpath="$2" + shift;; + *) break;; + esac + shift + done + local MANPATH="$mpath" + case "$1" in + -f | -k) + local g a + if [ "$1" = "-f" ]; then + g="grep -w" + a=$(basename "$2") + else + g="grep -F" + a="$2" + fi + whatis_internal "$a" "$g" + ;; + [0-9npol] | [0-9][a-z]* | new | public | old | local) + if [ "$1" = "new" ]; then + num=n + elif [ "$1" = "public" ]; then + num=p + elif [ "$1" = "old" ]; then + num=o + elif [ "$1" = "local" ]; then + num=l + else + num="$1" + fi + shift + manpage "$num" "$1" "$printing" + ;; + *) + manpage "$num" "$1" "$printing" + ;; + esac +} diff --git a/examples/functions/mhfold b/examples/functions/mhfold new file mode 100644 index 0000000..3c0c743 --- /dev/null +++ b/examples/functions/mhfold @@ -0,0 +1,16 @@ +# To: chet@ins.CWRU.Edu +# Subject: Bash functions +# From: Sandeep Mehta <sxm@philabs.Philips.Com> + +# print MH folders, useful only because folders(1) doesn't print +# mod date/times + +mhfold() +{ + list=`folders | awk '{if (1 < NR) print $1}'` + /bin/ls -lag ~/Mail > /tmp/fold$$ + for i in $list; do + grep $i /tmp/fold$$ + done + /bin/rm -f /tmp/fold$$ +} diff --git a/examples/functions/notify.bash b/examples/functions/notify.bash new file mode 100644 index 0000000..dafbac5 --- /dev/null +++ b/examples/functions/notify.bash @@ -0,0 +1,58 @@ +trap _notify CHLD +NOTIFY_ALL=false +unset NOTIFY_LIST +unalias false + +false() +{ + return 1 +} + +_notify () +{ + local i j + local newlist= + + if $NOTIFY_ALL + then + return # let bash take care of this itself + elif [ -z "$NOTIFY_LIST" ]; then + return + else + set -- $NOTIFY_LIST + for i in "$@" + do + j=$(jobs -n %$i) + if [ -n "$j" ]; then + echo "$j" + jobs -n %$i >/dev/null + else + newlist="newlist $i" + fi + done + NOTIFY_LIST="$newlist" + fi +} + +notify () +{ + local i j + + if [ $# -eq 0 ]; then + NOTIFY_ALL=: + set -b + return + else + for i in "$@" + do + # turn a valid job spec into a job number + j=$(jobs $i) + case "$j" in + [*) j=${j%%]*} + j=${j#[} + NOTIFY_LIST="$NOTIFY_LIST $j" + ;; + esac + done + fi +} diff --git a/examples/functions/pathfuncs b/examples/functions/pathfuncs new file mode 100644 index 0000000..56fdca3 --- /dev/null +++ b/examples/functions/pathfuncs @@ -0,0 +1,45 @@ +#From: "Simon J. Gerraty" <sjg@zen.void.oz.au> +#Message-Id: <199510091130.VAA01188@zen.void.oz.au> +#Subject: Re: a shell idea? +#Date: Mon, 09 Oct 1995 21:30:20 +1000 + + +# NAME: +# add_path.sh - add dir to path +# +# DESCRIPTION: +# These functions originated in /etc/profile and ksh.kshrc, but +# are more useful in a separate file. +# +# SEE ALSO: +# /etc/profile +# +# AUTHOR: +# Simon J. Gerraty <sjg@zen.void.oz.au> + +# @(#)Copyright (c) 1991 Simon J. Gerraty +# +# This file is provided in the hope that it will +# be of use. There is absolutely NO WARRANTY. +# Permission to copy, redistribute or otherwise +# use this file is hereby granted provided that +# the above copyright notice and this notice are +# left intact. + +# is $1 missing from $2 (or PATH) ? +no_path() { + eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac" +} +# if $1 exists and is not in path, append it +add_path () { + [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1" +} +# if $1 exists and is not in path, prepend it +pre_path () { + [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}" +} +# if $1 is in path, remove it +del_path () { + no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: | + sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"` +} diff --git a/examples/functions/recurse b/examples/functions/recurse new file mode 100644 index 0000000..f69cd50 --- /dev/null +++ b/examples/functions/recurse @@ -0,0 +1,63 @@ +#!/bin/bash + +#From: kaz@ashi.footprints.net (Kaz Kylheku) +#Newsgroups: comp.os.linux.misc +#Subject: Re: bash question: subdirectories +#Message-ID: <slrn8a0gu9.v5n.kaz@ashi.FootPrints.net> +#Date: Tue, 08 Feb 2000 16:24:35 GMT + +#Actually it can be made to. That is to say, it is possible to code a recursive +#descender function in the bash language. Here is an example. +# +#What is nice about this is that you can embed the function into your shell +#script. The function changes the current working directory as it descends. +#So it can handle arbitrarily deep paths. Whereas paths generated by the +#find command can cause a problem when they get too long; the kernel has a +#hard limit on the length of the string passed to the open() and other +#system calls. + +#There are races; what if the directory tree is blown away during the traversal? +#The function won't be able to crawl back up using the .. link and will just +#bail. + +# Recursive Directory Traverser +# Author: Kaz Kylheku +# Date: Feb 27, 1999 +# Copyright 1999 + +# Function parameter usage: +# $1 directory to search +# $2 pattern to search for +# $3 command to execute +# $4 secret argument for passing down path + +function recurse +{ + local file + local path + + if [ "$4" = "" ] ; then + path="${1%/}/" + else + path="$4$1/" + fi + + if cd "$1" ; then + for file in $2; do + if [ -f "$file" ] || [ -d "$file" ]; then + eval "$3" + fi + done + for file in .* * ; do + if [ "$file" = "." ] || [ "$file" = ".." ] ; then + continue + fi + if [ -d "$file" ] && [ ! -L "$file" ]; then + recurse "$file" "$2" "$3" "$path" + fi + done + cd .. + fi +} + +recurse "$1" "$2" 'echo "$path$file"' diff --git a/examples/functions/repeat2 b/examples/functions/repeat2 new file mode 100644 index 0000000..2e2dc7a --- /dev/null +++ b/examples/functions/repeat2 @@ -0,0 +1,43 @@ +# To: chet@ins.CWRU.Edu +# Subject: Bash functions +# From: Sandeep Mehta <sxm@philabs.Philips.Com> + +########################################## +# +# repeat - clone of C shell builtin `repeat' +# +# usage: repeat <count> <command> +# +# It has been tested inside other functions and in conditionals like +# if [ "`repeat <count> <command>`" ]; then COMMANDS [ else COMMANDS ] fi +# Please send me fixes/enhancements. +# +# Sandeep Mehta <sxm@philabs.Philips.Com> +########################################## +repeat() +{ + local rcount=$1 + + if [ $# -le 1 ] || [ -z "$rcount" ]; then + echo "usage: repeat <count> <command>" 1>&2 + return 2 + fi + + shift + + local acmd=("$@") + + if [ $rcount -le 0 ]; then + echo "count must be greater than 0" + echo "usage: repeat <count> <command>" 1>&2 + return 2 + fi + + st=0 + while [ $rcount -gt 0 ]; do + eval "${acmd[@]}" + st=$? + rcount=$((rcount - 1)) + done + return $st +} diff --git a/examples/functions/repeat3 b/examples/functions/repeat3 new file mode 100644 index 0000000..65048bf --- /dev/null +++ b/examples/functions/repeat3 @@ -0,0 +1,12 @@ +# From psamuels@jake.niar.twsu.edu (Peter Samuelson) +# posted to usenet, Message-ID: <6rtp8j$2a0$1@jake.niar.twsu.edu> + +repeat () +{ + local i max; # note that you can use \$i in the command string + max=$1; shift; + + i=1; while ((i <= max)); do + eval "$@"; ((i = i + 1)); + done; +} diff --git a/examples/functions/seq b/examples/functions/seq new file mode 100644 index 0000000..87c8a2c --- /dev/null +++ b/examples/functions/seq @@ -0,0 +1,29 @@ +# Generate a sequence from m to n, m defaults to 1. + +seq () +{ + declare -i lo hi i # makes local + local _SEQ + + case $# in + 1) seq 1 "$1" ; return $? ;; + 2) lo=$1 hi=$2 + i=$lo _SEQ="" + while let "i <= hi"; do + _SEQ="${_SEQ}$i " + let i+=1 + done + echo "${_SEQ# }" + return 0 ;; + *) echo seq: usage: seq [low] high 1>&2 ; return 2 ;; + esac +} + +# like the APL `iota' function (or at least how I remember it :-) +iota() +{ + case $# in + 1) seq 1 "$1"; return $?;; + *) echo "iota: usage: iota high" 1>&2; return 2;; + esac +} diff --git a/examples/functions/seq2 b/examples/functions/seq2 new file mode 100644 index 0000000..c3ad95c --- /dev/null +++ b/examples/functions/seq2 @@ -0,0 +1,37 @@ +# Generate a sequence from m to n, m defaults to 1. + +seq () +{ + declare -i lo hi i # makes local + local _SEQ INIT COMPARE STEP + + case "$1" in + -r) INIT='i=$hi _SEQ=""' COMPARE='let "i >= $lo"' STEP='let i-=1' ; shift ;; + *) INIT='i=$lo _SEQ=""' COMPARE='let "i <= $hi"' STEP='let i+=1' ;; + esac + + case $# in + 1) lo=1 hi="$1" ;; + 2) lo=$1 hi=$2 ;; + *) echo seq: usage: seq [-r] [low] high 1>&2 ; return 2 ;; + esac + + # equivalent to the as-yet-unimplemented + # for (( "$INIT" ; "$COMPARE" ; "$STEP" )); do _SEQ="${_SEQ}$i "; done + eval "$INIT" + while eval "$COMPARE"; do + _SEQ="${_SEQ}$i " + eval "$STEP" + done + echo "${_SEQ# }" + return 0 +} + +# like the APL `iota' function (or at least how I remember it :-) +iota() +{ + case $# in + 1) seq 1 "$1"; return $?;; + *) echo "iota: usage: iota high" 1>&2; return 2;; + esac +} diff --git a/examples/functions/shcat b/examples/functions/shcat new file mode 100644 index 0000000..c5d3d63 --- /dev/null +++ b/examples/functions/shcat @@ -0,0 +1,7 @@ +shcat() +{ + while read -r line + do + echo "$line" + done +} diff --git a/examples/functions/shcat2 b/examples/functions/shcat2 new file mode 100644 index 0000000..6fe90f4 --- /dev/null +++ b/examples/functions/shcat2 @@ -0,0 +1,19 @@ +shcat() +{ + while read -r line + do + echo "$line" + done +} + +shcat2() +{ + while [ $# -ge 1 ]; do + case "$1" in + -) shcat ;; + *) shcat < "$1" ;; + esac + shift + done + exit 0 +} diff --git a/examples/functions/sort-pos-params b/examples/functions/sort-pos-params new file mode 100644 index 0000000..0052b46 --- /dev/null +++ b/examples/functions/sort-pos-params @@ -0,0 +1,50 @@ +# Sort the positional paramters. +# Make sure the positional parameters are passed as arguments to the function. +# If -u is the first arg, remove duplicate array members. +sort_posparams() +{ + local -a R + local u + + case "$1" in + -u) u=-u ; shift ;; + esac + + # if you want the case of no positional parameters to return success, + # remove the error message and return 0 + if [ $# -eq 0 ]; then + echo "$FUNCNAME: argument expected" >&2 + return 1 + fi + + # make R a copy of the positional parameters + R=( "${@}" ) + + # sort R. + R=( $( printf "%s\n" "${R[@]}" | sort $u) ) + + printf "%s\n" "${R[@]}" + return 0 +} + +# will print everything on separate lines +set -- 3 1 4 1 5 9 2 6 5 3 2 +sort_posparams "$@" + +# sets without preserving quoted parameters +set -- $( sort_posparams "$@" ) +echo "$@" +echo $# + +# sets preserving quoted parameters, beware pos params with embedded newlines +set -- 'a b' 'a c' 'x z' + +oifs=$IFS +IFS=$'\n' +set -- $( sort_posparams "$@" ) +IFS="$oifs" + +echo "$@" +echo $# + +sort_posparams diff --git a/examples/functions/substr b/examples/functions/substr new file mode 100644 index 0000000..a80b3b4 --- /dev/null +++ b/examples/functions/substr @@ -0,0 +1,79 @@ +# +# substr -- a function to emulate the ancient ksh builtin +# + +# +# -l == shortest from left +# -L == longest from left +# -r == shortest from right (the default) +# -R == longest from right + +substr() +{ + local flag pat str + local usage="usage: substr -lLrR pat string or substr string pat" + + case "$1" in + -l | -L | -r | -R) + flag="$1" + pat="$2" + shift 2 + ;; + -*) + echo "substr: unknown option: $1" + echo "$usage" + return 1 + ;; + *) + flag="-r" + pat="$2" + ;; + esac + + if [ "$#" -eq 0 ] || [ "$#" -gt 2 ] ; then + echo "substr: bad argument count" + return 2 + fi + + str="$1" + + # + # We don't want -f, but we don't want to turn it back on if + # we didn't have it already + # + case "$-" in + "*f*") + ;; + *) + fng=1 + set -f + ;; + esac + + case "$flag" in + -l) + str="${str#$pat}" # substr -l pat string + ;; + -L) + str="${str##$pat}" # substr -L pat string + ;; + -r) + str="${str%$pat}" # substr -r pat string + ;; + -R) + str="${str%%$pat}" # substr -R pat string + ;; + *) + str="${str%$2}" # substr string pat + ;; + esac + + echo "$str" + + # + # If we had file name generation when we started, re-enable it + # + if [ "$fng" = "1" ] ; then + set +f + fi +} diff --git a/examples/functions/substr2 b/examples/functions/substr2 new file mode 100644 index 0000000..2bb8d36 --- /dev/null +++ b/examples/functions/substr2 @@ -0,0 +1,81 @@ +# +# substr -- a function to emulate the ancient ksh builtin +# + +# -l == remove shortest from left +# -L == remove longest from left +# -r == remove shortest from right (the default) +# -R == remove longest from right + +substr() +{ + local flag pat str + local usage="usage: substr -lLrR pat string or substr string pat" + local options="l:L:r:R:" + + OPTIND=1 + while getopts "$options" c + do + case "$c" in + l | L | r | R) + flag="-$c" + pat="$OPTARG" + ;; + '?') + echo "$usage" + return 1 + ;; + esac + done + + if [ "$OPTIND" -gt 1 ] ; then + shift $[ $OPTIND -1 ] + fi + + if [ "$#" -eq 0 ] || [ "$#" -gt 2 ] ; then + echo "substr: bad argument count" + return 2 + fi + + str="$1" + + # + # We don't want -f, but we don't want to turn it back on if + # we didn't have it already + # + case "$-" in + "*f*") + ;; + *) + fng=1 + set -f + ;; + esac + + case "$flag" in + -l) + str="${str#$pat}" # substr -l pat string + ;; + -L) + str="${str##$pat}" # substr -L pat string + ;; + -r) + str="${str%$pat}" # substr -r pat string + ;; + -R) + str="${str%%$pat}" # substr -R pat string + ;; + *) + str="${str%$2}" # substr string pat + ;; + esac + + echo "$str" + + # + # If we had file name generation when we started, re-enable it + # + if [ "$fng" = "1" ] ; then + set +f + fi +} diff --git a/examples/functions/term b/examples/functions/term new file mode 100644 index 0000000..fbe99f1 --- /dev/null +++ b/examples/functions/term @@ -0,0 +1,35 @@ +# +# term -- a shell function to set the terminal type interactively or not. +# + +term() +{ + local t + + if [ $# != 0 ] ; then + eval $(tset -sQ $1) + else # interactive + if [ -z "$TERM" ] ; then + TERM="unknown" + fi + + case "$TERM" in + network|dialup|unknown|lat) + TERM=unknown + ;; + *) + eval $(tset -sQ) + ;; + esac + + while [ "$TERM" = "unknown" ] ; do + echo -n "Terminal type: " + read t + if [ -n "$t" ] ; then + eval $(tset -sQ $t) + fi + done + fi +} + + diff --git a/examples/functions/whatis b/examples/functions/whatis new file mode 100644 index 0000000..56c5a58 --- /dev/null +++ b/examples/functions/whatis @@ -0,0 +1,52 @@ +# +# whatis -- and implementation of the 10th Edition Unix sh builtin `whatis' +# command. +# +# usage: whatis arg [...] +# +# For each argument, whatis prints the associated value as a parameter, +# builtin, function, alias, or executable file as appropriate. In each +# case, the value is printed in a form which would yield the same value +# if typed as input to the shell itself. +# + +whatis() +{ + local wusage='usage: whatis arg [arg...]' + local fail=0 + + if [ $# -eq 0 ] ; then + echo "$wusage" + return 1 + fi + + for arg + do + case $(builtin type -type $arg 2>/dev/null) in + "alias") + builtin alias "$arg" + ;; + "function") + builtin type "$arg" | sed 1d + ;; + "builtin") + echo builtin "$arg" + ;; + "file") + builtin type -path "$arg" + ;; + *) + # OK, we could have a variable, or we could have nada + if [ "$(eval echo \${$arg+set})" = "set" ] ; then + # It is a variable, and it is set + echo -n "$arg=" + eval echo '\"'\$$arg'\"' + else + echo whatis: $arg: not found + fail=1 + fi + ;; + esac + done + return $fail +} diff --git a/examples/functions/whence b/examples/functions/whence new file mode 100644 index 0000000..70b2322 --- /dev/null +++ b/examples/functions/whence @@ -0,0 +1,59 @@ +# +# An almost-ksh compatible `whence' command. This is as hairy as it is +# because of the desire to exactly mimic ksh. +# +# This depends somewhat on knowing the format of the output of the bash +# `builtin type' command. +# +# Chet Ramey +# chet@ins.CWRU.Edu +# +whence() +{ + local vflag= path= + + if [ "$#" = "0" ] ; then + echo "whence: argument expected" + return 1 + fi + case "$1" in + -v) vflag=1 + shift 1 + ;; + -*) echo "whence: bad option: $1" + return 1 + ;; + *) ;; + esac + + if [ "$#" = "0" ] ; then + echo "whence: bad argument count" + return 1 + fi + + for cmd + do + if [ "$vflag" ] ; then + echo $(builtin type $cmd | sed 1q) + else + path=$(builtin type -path $cmd) + if [ "$path" ] ; then + echo $path + else + case "$cmd" in + /*) if [ -x "$cmd" ]; then + echo "$cmd" + fi + ;; + *) case "$(builtin type -type $cmd)" in + "") ;; + *) echo "$cmd" + ;; + esac + ;; + esac + fi + fi + done + return 0 +} diff --git a/examples/functions/which b/examples/functions/which new file mode 100644 index 0000000..ca33703 --- /dev/null +++ b/examples/functions/which @@ -0,0 +1,44 @@ +# +# which - emulation of `which' as it appears in FreeBSD +# +# usage: which [-as] command [command...] +# + +which() +{ + local aflag sflag ES a opt + + OPTIND=1 + while builtin getopts as opt ; do + case "$opt" in + a) aflag=-a ;; + s) sflag=1 ;; + ?) echo "which: usage: which [-as] command [command ...]" >&2 + exit 2 ;; + esac + done + + (( $OPTIND > 1 )) && shift $(( $OPTIND - 1 )) + + # without command arguments, exit with status 1 + ES=1 + + # exit status is 0 if all commands are found, 1 if any are not found + for command; do + # if $command is a function, make sure we add -a so type + # will look in $PATH after finding the function + a=$aflag + case "$(builtin type -t $command)" in + "function") a=-a;; + esac + + if [ -n "$sflag" ]; then + builtin type -p $a $command >/dev/null 2>&1 + else + builtin type -p $a $command + fi + ES=$? + done + + return $ES +} diff --git a/examples/functions/xalias.bash b/examples/functions/xalias.bash new file mode 100644 index 0000000..88a00dc --- /dev/null +++ b/examples/functions/xalias.bash @@ -0,0 +1,22 @@ +# xalias - convert csh alias commands to bash functions +# from Mohit Aron <aron@cs.rice.edu> +# posted to usenet as <4i5p17$bnu@larry.rice.edu> +function xalias () +{ + if [ "x$2" = "x" ] + then + declare -f $1 + else + case $2 in + *[#\!]*) + comm=$(echo $2 | sed 's/\\!\*/\"$\@\"/g + s/\\!:\([1-9]\)/\"$\1\"/g + s/#/\\#/g') + ;; + *) + comm="$2 \"\$@\"" ;; + esac + + eval function $1 \(\) "{" command "$comm" "; }" + fi +} diff --git a/examples/functions/xfind.bash b/examples/functions/xfind.bash new file mode 100644 index 0000000..6d29038 --- /dev/null +++ b/examples/functions/xfind.bash @@ -0,0 +1,52 @@ +#! /bin/bash +#From: kaz@cafe.net (Kaz Kylheku) +#Newsgroups: comp.unix.shell +#Subject: Why not roll your own @#$% find! (was: splitting directory off from filename) +#Message-ID: <6n1117$tp1@espresso.cafe.net> +#Date: Fri, 26 Jun 1998 20:47:34 GMT + +# $1 = dirname, $2 = pattern, optional $3 = action +xfind() +{ + local x + local dir="$1" + + # descend into specified directory + + builtin cd -L "$1" || { + echo "${FUNCNAME}: cannot change dir to $1" >&2 + return 1 + } + + # + # default action is to print the filename + # + if [ -n "$3" ]; then + action="$3" + else + action='printf -- "%s\n"' + fi + + # process ordinary files that match pattern + + for x in $2 ; do + if [ -f "$x" ] ; then + eval "$action" "$x" + fi + done + + # now descend into subdirectories, avoiding symbolic links + # and directories that start with a period. + + for x in * ; do + if [ -d "$x" ] && [ ! -L "$x" ] ; then + $FUNCNAME "$x" "$2" "$action" + fi + done + + # finally, pop back up + + builtin cd -L .. +} + +#xfind "$@" |