SHELLdorado Newsletter 1/2001 - January 12, 2001 ================================================================ The "SHELLdorado Newsletter" covers UNIX shell script related topics. To subscribe to this newsletter, leave your e-mail address at the SHELLdorado home page: http://www.shelldorado.com/ "Heiner's SHELLdorado" is a place for UNIX shell script programmers providing Many shell script examples, shell scripting tips & tricks + more... ================================================================ Main Topic: Shell Functions Contents o What's new at the SHELLdorado? o Shell Tip: Progress indicator o Shell Tip: Use function "echon()" to "echo" without newline o Shell Tip: Shell function "ask()" for yes/no questions o Q&A: How can I search for text in all subdirectories? o Q&A: How can I use arguments with KornShell aliases? o Programmer's Playground: KornShell FTP Library ----------------------------------------------------------------- >> What's new at the SHELLdorado? ----------------------------------------------------------------- o The "SHELLdorado Links" section has been extended. Five new categories with more than 40 new links to interesting shell scripting resources have been added: http://www.shelldorado.com/links/ Thanks to Brian Hiles <brian_hiles@rocketmail.com> for providing his extensive collection of shell scripting links, which turned out to contain some real gems. o The "AWK Compatibility List" has been extended and now compares the AWK features for more than 20 operating systems and versions: http://www.shelldorado.com/articles/awkcompat.html If you run an operating system not listed there, you may help complete the list. Just run the feature test script from the page, and send the resulting output to mailto:heiner.steven@odn.de . ----------------------------------------------------------------- >> Shell Tip: Progress indicator ----------------------------------------------------------------- Sometimes we have a script taking a long time to complete. We would like to provide some kind of progress indicator for the impatiently waiting user threading to terminate our script. This is the important use of "busy"! The script writes a character to the screen once a second, so the user knows the script is still running: #! /bin/ksh # busy - print status indicator # # usage: # busy& busypid=$! # somelongrunningcommand # kill $busypid >/dev/null 2>&1 set -A Indicators -- "|" "/" "-" "\\" integer n=${#Indicators[@]} integer i=0 while true do print -n -- "${Indicators[i]" ((i=(i+1)%n)) sleep 1 done Note that this script uses KornShell specific features and requires a KornShell or BASH. ----------------------------------------------------------------- >> Shell Tip: Use function "echon()" to echo without newline ----------------------------------------------------------------- The "echo" command prints a string to standard output, and positions the cursor at the start of the next line. Sometimes this is not desired. Consider the following code fragment: echo "Are you sure?" read answer This would result in the following output: Are you sure? X ('X' is the position of the cursor). On BSD systems, we could use "echo -n" to suppress the newline character at the end of the string, and System V systems use "echo ...\c" for the same purpose. The following shell function "echon()" ("echo, no newline") automatically detects the required "echo" arguments, hence we can simply write: echon "Are you sure? " to get Are you sure? X The function echon() is defined as follows: echon () { if [ X"$ECHON" = X ] then # Determine how to "echo" without newline: "echo -n" # or "echo ...\c" if [ X`echo -n` = X-n ] then ECHON=echo; NNL="\c" else ECHON="echo -n"; NNL="" fi fi $ECHON "$*$NNL" } ----------------------------------------------------------------- >> Shell Tip: Use function "ask()" for yes/no questions ----------------------------------------------------------------- Script programmers often have to prompt the user for yes/no answers, e.g. echo "Do you really want to remove 1230 files (yes/no)?" The script "ask()" allows for easy prompting, e.g. if ask -y "really remove $n files" then rm -f "$allfiles" fi Just copy the following code into your script to use the function: ask() { # # ask a question with a yes/no answer; # Usage: ask [-y|-n] "question" # Set default: -y = yes; -n = no; otherwise no default; # Returns: 0 (true) = yes; 1 (false) = no; # Note: changing the default does not effect the return value; # ASK_DFLT= # process options/args for ASK_OPT do case "$ASK_OPT" in -[yY]*) ASK_DFLT='y' ;; -[nN]*) ASK_DFLT='n' ;; --) shift; break ;; -*) ;; *) break esac shift done ASK_PROMPT="$*" # get the response while : ; do echon "$ASK_PROMPT (y/n)?${ASK_DFLT:+ [$ASK_DFLT]} " >&2 read ASK_ANSWER ASK_JUNK : ${ASK_ANSWER:=$ASK_DFLT} case "$ASK_ANSWER" in [yY]*) return 0 ;; [nN]*) return 1 ;; *) echo "" >&2 esac done return } [Thanks to Daniel E. Singer <des@cs.duke.edu> for providing the "ask()" function] ----------------------------------------------------------------- >> Q&A: How can I search for text in all subdirectories? ----------------------------------------------------------------- The "grep" command searches for a pattern in all specified files, e.g. grep "unix" *.txt But how can we search the string in all files of the current directory and all subdirectories? The "find" command can be used to recurse into all subdirectories, and the following line shows us how to invoke the "grep" command for each file: find . -type f -exec grep "unix" {} \; The curly braces {} will be replaced with the current file name. Unfortunately this will cause one "grep" invocation for each file, resulting in a very slow execution. Most systems have the "xargs" command we can use to speed up the command considerably: find . -type f -print | xargs grep "unix" ----------------------------------------------------------------- >> Q&A: How can I use arguments with KornShell aliases? ----------------------------------------------------------------- CSH users often write small one-line "aliases" for frequently entered commands, e.g. "alias ll ls -l". The KornShell knows aliases, too, but there is no way to access the arguments to the alias. The following alias will not work as expected: $ alias makebackup="cp $1 $1.bak" $ makebackup testfile To solve this problem, shell functions should be used instead of aliases: makebackup () { cp "$1" "$1".bak; } function makebackup { cp "$1" "$1".bak; } The first version works with Bourne Shell, KornShell, and BASH. The second syntax requires KornShell or BASH. If you include one of these lines in the file $HOME/.profile, the command "mkbackup" can be used like a standard command. ----------------------------------------------------------------- >> Programmer's Playground: KornShell FTP Library ----------------------------------------------------------------- The "Programmer's Playground" features advanced shell scripting topics. Scripts are frequently used to automate file transfers, e.g. using the external "ftp" program: ftp -i -v targethost <<EOF bin put file.tar /tmp quit EOF (This example assumes a $HOME/.netrc file with authentication information for the host "targethost"). One disadvantage of this solution is the error checking: only the exit code of the "ftp" command (or the analysis of a log file) can tell us if the transfer was successful. The KornShell FTP library (ftplib.ksh) provides FTP functions like ftp_login, ftp_put, ftp_get, ... and allows for a better errors handling: #! /usr/bin/ksh . ftplib.ksh # include library ftplib # initialize it if ftp_open targethost then ftp_login anonymous geheim || echo "$ftp_response" ftp_put file.tar /tmp || echo "$ftp_response" ftp_quit ftp_close fi Note that the library is only a case study and cannot be used for serious programming (yet). Interested readers can download it from the following location: http://www.shelldorado.com/misc/ ---------------------------------------------------------------- If you want to comment on the newsletter, or even want to submit an article of your own, send a mail to mailto:heiner.steven@shelldorado.com ================================================================ To unsubscribe send a mail with the body "unsubscribe" to newsletter@shelldorado.com ================================================================