Sunteți pe pagina 1din 200

UNIX Shell Programming Featuring KornShell

Course Guide
EY–G994E-SG-0002
UNIX Shell Programming Featuring KornShell

Course Guide
EY–G994E-SG-0002
Notice

The information in this publication is subject to change without notice.

COMPAQ COMPUTER CORPORATION SHALL NOT BE LIABLE FOR TECHNICAL OR


EDITORIAL ERRORS OR OMISSIONS CONTAINED HEREIN, NOR FOR INCIDENTAL OR
CONSEQUENTIAL DAMAGES RESULTING FROM THE FURNISHING, PERFORMANCE, OR
USE OF THIS MATERIAL.

This guide contains information protected by copyright. No part of this guide may be photocopied or
reproduced in any form without prior written consent from Compaq Computer Corporation.

The software described in this guide is furnished under a license agreement or nondisclosure agreement.
The software may be used or copied only in accordance with the terms of this agreement.

Other product names mentioned herein may be trademarks and/or registered trademarks of their
respective companies.

©1999 Compaq Computer Corporation. All rights reserved. Printed in the USA.

Aero, ALPHA, ALPHA AXP, AlphaServer, AlphaStation, Armada, BackPaq, COMPAQ, Compaq
Insight Manager, CompaqCare logo, Counselor, DECterm, Deskpro, DIGITAL, DIGITAL logo,
DIGITAL Alpha Systems, Digital Equipment Corporation, DIGITAL UNIX, DirectPlus, FASTART,
Himalaya, HSZ, InfoPaq, Integrity, LAT, LicensePaq, Ministation, NetFlex, NonStop, OpenVMS,
PaqFax, Presario, ProLiant, ProLinea, ProSignia, QuickBack, QuickFind, Qvision, RDF, RemotePaq,
RomPaq, ServerNet, SERVICenter, SmartQ, SmartStart, SmartStation, SolutionPaq, SpeedPaq,
StorageWorks, Systempro/LT, Tandem, TechPaq, TruCluster, Tru64 UNIX are registered in United
States Patent and Trademark Office.

AdvantageCluster, DECevent, DECladebug, DECnsr, DECsafe, DEC VET, DECwindows, OpenVMS,


RZ, TURBOchannel, VAX Notes, are trademarks of Compaq Computer Corporation.

AIX and IBM are registered trademarks of International Business Machines Corp. BSD is a trademark
of the University of California, Berkeley, CA. Memory Channel is a trademark of Encore Computer
Corporation. Global Knowledge Network and the Global Knowledge Network logo are trademarks of
Global Nowledge Network, Inc. Microsoft and Windows NT are registered trademarks of Microsoft
Corporation. MIPS is a trademark of MIPS Computer Systems. Motif, OSF and OSF/1 are registered
trademarks of the Open Software Foundation. NetWorker is a trademark of Legato. NFS is a registered
trademark of Sun Microsystems, Inc. Oracle is a registered trademark and Oracle7 is a trademark of
Oracle Corporation. POSIX and IEEE are registered trademarks of the Institute of Electrical and
Electronics Engineers, Inc. PostScript is a registered trademark of Adobe Systems, Inc. UNIX is a
registered trademark licensed exclusively through X/Open Company Ltd. X Window System is a
trademark of the Massachusetts Institute of Technology.

UNIX Shell Programming Featuring KornShell


Course Guide
November 1999
Contents

About This Course


About This Course .................................................................................................xix
Introduction ...................................................................................................xix
Course Description ........................................................................................xix
Place in Curriculum.......................................................................................xix
Target Audience ............................................................................................xix
Prerequisites ...................................................................................................xx
Course Goals ..................................................................................................xx
Nongoals........................................................................................................xxi
Taking This Course...............................................................................................xxii
Course Organization.....................................................................................xxii
Course Map ..................................................................................................xxii
Chapter Descriptions .................................................................................. xxiii
Time Schedule .............................................................................................xxiv
Course Conventions .....................................................................................xxv
Resources......................................................................................................xxv
1 Review
About This Chapter............................................................................................... 1-3
Introduction .................................................................................................. 1-3
Objectives ..................................................................................................... 1-3
Resources...................................................................................................... 1-3
Standard Files ....................................................................................................... 1-4
Standard Input .............................................................................................. 1-4
Standard Output............................................................................................ 1-4
Standard Error (Diagnostic Output) ............................................................. 1-4
Status of the Standard Files at Login............................................................ 1-4
Redirecting Standard Files.................................................................................... 1-5
Redirecting Output to a New File................................................................. 1-5
Redirecting Output to an Existing File......................................................... 1-5
Appending Redirected Output to an Existing Output File ........................... 1-5
Redirecting Input .......................................................................................... 1-6
Redirecting Standard Error........................................................................... 1-6
Redirecting Both Standard Error and Standard Output................................ 1-6
Eliminating Any Output ............................................................................... 1-6
The /dev/null File ......................................................................................... 1-6
The Pipe Operator................................................................................................. 1-7
Definition...................................................................................................... 1-7
Using the Pipe Operator (|)........................................................................... 1-7

EY-G994E-SG-0002 iii
Using the Pipe Operator to List All Block-Structured Devices ................... 1-7
Using the Pipe Operator to List All Files in /dev on the Printer .................. 1-7
Wildcard Metacharacters ...................................................................................... 1-8
Wildcards...................................................................................................... 1-8
Using Wildcards ........................................................................................... 1-8
Command History and Command Line Editing ................................................... 1-9
The history Command .................................................................................. 1-9
Recalling a Specific Command .................................................................... 1-9
Recalling a Command ................................................................................ 1-10
Using vi Commands ................................................................................... 1-10
Filename Completion ................................................................................. 1-10
Shell Variables .................................................................................................... 1-12
Guidelines................................................................................................... 1-12
Using Variables .......................................................................................... 1-12
Shell Variables............................................................................................ 1-12
Exporting Variables.................................................................................... 1-13
Listing Exported Variables......................................................................... 1-13
Foreground and Background Processes .............................................................. 1-14
Introduction ................................................................................................ 1-14
Running a Program in the Background ...................................................... 1-14
Running Multiple Commands in the Background...................................... 1-14
Running a Pipeline in the Background....................................................... 1-15
Obtaining a Long Listing of Process Status ............................................... 1-15
Moving a Foreground Job to the Background............................................ 1-16
Moving a Background Job into the Foreground......................................... 1-16
Waiting for a Background Process to Complete ........................................ 1-16
Terminating a Background Job .................................................................. 1-16
Stopping a Background Job........................................................................ 1-17
Job References............................................................................................ 1-17
vi Refresher......................................................................................................... 1-18
Sample vi Commands................................................................................. 1-18
Summary .......................................................................................................... 1-19
Defining and Redirecting Standard Files ................................................... 1-19
Using the Pipe Operator to Connect Two Commands ............................... 1-19
Using Wildcard Metacharacters ................................................................. 1-19
Using the History Command to Recall Commands ................................... 1-19
Using Shell Variables ................................................................................. 1-19
Defining and Using Foreground and Background Processes ..................... 1-19
Reviewing Some vi Commands ................................................................. 1-19
2 Getting Started with Scripting
About This Chapter............................................................................................... 2-3
Introduction .................................................................................................. 2-3
Objectives ..................................................................................................... 2-3
Resources...................................................................................................... 2-3
Processes ............................................................................................................ 2-4
iv EY-G994E-SG-0002
Definition...................................................................................................... 2-4
Process as Program Wrapper........................................................................ 2-4
fork() and exec() ........................................................................................... 2-4
Login Process Diagram ................................................................................ 2-4
Executing Shell Scripts......................................................................................... 2-5
Introduction .................................................................................................. 2-5
Executing a Shell Script as an Argument to ksh .......................................... 2-5
Permission Requirements for Executing a Shell Script as an Argument ..... 2-5
Executing Shell Scripts by Name ................................................................. 2-5
Permission Requirements for Executing Shell Scripts by Name ................. 2-5
The #! Operator ............................................................................................ 2-5
The print Statement ...................................................................................... 2-5
print Statement Syntax ................................................................................. 2-6
Using the print Statement to Write Standard Output.................................... 2-6
Using Positional Parameters ................................................................................. 2-7
Positional Parameters ................................................................................... 2-7
Arguments .................................................................................................... 2-7
Using Positional Parameters......................................................................... 2-8
Using the shift Statement.............................................................................. 2-8
shift Statement Syntax.................................................................................. 2-8
Default Parameter Value .............................................................................. 2-8
Using Parameter Values ............................................................................... 2-9
Escape Sequences ............................................................................................... 2-10
Using Wildcards as Literals........................................................................ 2-10
How the Escape Sequences Influence Output ............................................ 2-10
Using Escape Sequences Properly.............................................................. 2-11
Passing Arguments to Shell Scripts .................................................................... 2-12
Introduction ................................................................................................ 2-12
Environmental Variables ............................................................................ 2-12
Environmental Variables and Positional Parameters ................................. 2-12
How the read Command Works ................................................................. 2-13
Using the read Command ........................................................................... 2-13
Including a Prompt ..................................................................................... 2-13
Using read with a Prompt ........................................................................... 2-14
Testing Arguments.............................................................................................. 2-15
Techniques for Testing Arguments ............................................................ 2-15
General Syntax ........................................................................................... 2-15
Using the (( )) Command............................................................................ 2-15
Using the test Command ............................................................................ 2-15
Using the [ ] Command .............................................................................. 2-15
The [[ ]] Command..................................................................................... 2-16
Using Operators .................................................................................................. 2-17
Introduction ................................................................................................ 2-17
Integer Comparison Operators ................................................................... 2-17
Using Integer Comparison Operators......................................................... 2-17
String Comparison Operators ..................................................................... 2-18
EY-G994E-SG-0002 v
Using String Comparison Operators .......................................................... 2-18
File Enquiry Operators ............................................................................... 2-19
Using File Enquiry Operators..................................................................... 2-19
Testing for Permissions .............................................................................. 2-19
Logic Operators .......................................................................................... 2-20
Using Logic Operators ............................................................................... 2-20
Review........................................................................................................ 2-20
Making Decisions and Looping.......................................................................... 2-21
Introduction ................................................................................................ 2-21
The if/then/fi Commands............................................................................ 2-21
Using the if Command to Test the Status of the [[ Command ................... 2-21
Using the if Command to Test the Status of the who | grep Command ..... 2-22
Exact Syntax............................................................................................... 2-22
Commands in then or else Blocks .............................................................. 2-22
Using the while Loop ................................................................................. 2-22
Infinite Loops ............................................................................................. 2-23
The for Loop in KornShell ......................................................................... 2-23
Using the for Loop...................................................................................... 2-23
A Special Case............................................................................................ 2-23
Using Execute Trace and Verbose Trace to Debug Shell Scripts....................... 2-24
Introduction ................................................................................................ 2-24
Execute Trace ............................................................................................. 2-24
Using Execute Trace................................................................................... 2-24
Verbose Trace............................................................................................. 2-25
Using Verbose Trace .................................................................................. 2-25
Using Execute Trace Inside a Shell Script ................................................. 2-25
Command Substitution ....................................................................................... 2-26
Introduction ................................................................................................ 2-26
Procedure.................................................................................................... 2-26
Assigning Output of a Command to a Variable ......................................... 2-26
Using the Grave Accent.............................................................................. 2-26
Shell Script Examples......................................................................................... 2-27
Example 1................................................................................................... 2-27
Example 2................................................................................................... 2-28
Summary .......................................................................................................... 2-29
Defining and Using Processes .................................................................... 2-29
Executing a Shell Script ............................................................................. 2-29
Using Positional Parameters....................................................................... 2-29
Using Escape Sequences ............................................................................ 2-29
Passing Arguments to Shell Scripts............................................................ 2-29
Testing Arguments ..................................................................................... 2-29
Using Operators.......................................................................................... 2-29
Using Conditions, Control Statements and Loops ..................................... 2-29
Debugging Scripts Using Execute Trace and Verbose Trace .................... 2-29
Using Command Substitution .................................................................... 2-30
Exercises .......................................................................................................... 2-31
vi EY-G994E-SG-0002
Instructions ................................................................................................. 2-31
3 Intermediate Shell Scripting
About This Chapter............................................................................................... 3-3
Introduction .................................................................................................. 3-3
Objectives ..................................................................................................... 3-3
Resources...................................................................................................... 3-3
Executing a Script as a Sibling ............................................................................. 3-4
Normal Execution of a Shell Script.............................................................. 3-4
Parent and Child Scripts ............................................................................... 3-4
Running a Sibling......................................................................................... 3-4
Using the Dot Command .............................................................................. 3-5
Class Discussion........................................................................................... 3-5
The exec Command...................................................................................... 3-5
Grouping Shell Commands................................................................................... 3-7
Introduction .................................................................................................. 3-7
Using the Semicolon..................................................................................... 3-7
Using Parentheses......................................................................................... 3-7
Using Braces................................................................................................. 3-8
Control Statements................................................................................................ 3-9
Command Execution Dependent on Previous Command (&&, | |).............. 3-9
The && Operator ......................................................................................... 3-9
The | | Operator ............................................................................................. 3-9
The case Statement ....................................................................................... 3-9
Using the case Statement............................................................................ 3-10
The until Statement .................................................................................... 3-10
Using the until Statement ........................................................................... 3-10
Syntax for break and continue.................................................................... 3-11
Using break and continue ........................................................................... 3-11
Advanced Loop Processing ................................................................................ 3-12
Redirecting Input/Output in while Loops................................................... 3-12
Using the while read Statement.................................................................. 3-12
KornShell Aliases ............................................................................................... 3-13
Alias Definition .......................................................................................... 3-13
Preset Aliases.............................................................................................. 3-13
Defined Aliases .......................................................................................... 3-13
Tracked Aliases .......................................................................................... 3-13
Using Aliases in a Script ............................................................................ 3-14
Using $ set -o trackall................................................................................. 3-14
Exporting Aliases ....................................................................................... 3-15
KornShell Functions ........................................................................................... 3-16
Function Definition .................................................................................... 3-16
Invoking a Function.................................................................................... 3-16
Debugging Functions.................................................................................. 3-17
Exporting Functions ................................................................................... 3-17
The ENV Environment Variable ........................................................................ 3-18
EY-G994E-SG-0002 vii
The ENV Environment Variable ................................................................ 3-18
Default Execution....................................................................................... 3-18
Execution on Creating a New Kornshell .................................................... 3-18
Executing a Shell Script as an Argument to ksh ........................................ 3-18
Summary of Differences between $ myscript and $ ksh myscript............. 3-19
Executing ~/.kshrc...................................................................................... 3-19
Exception for Tru64 UNIX and Some Other Implementations.................. 3-19
Autoloading Functions ............................................................................... 3-19
Delaying Function Definition..................................................................... 3-20
Using Directory Information .............................................................................. 3-21
Introduction ................................................................................................ 3-21
Example Using the ma Function ................................................................ 3-22
Example Using the ga Function ................................................................. 3-23
Notes on Using the ga Function ................................................................. 3-24
Example Using the la Function .................................................................. 3-25
Another Example........................................................................................ 3-26
Running This Script.................................................................................... 3-26
Summary .......................................................................................................... 3-27
Executing a Script as a Sibling................................................................... 3-27
Grouping Shell Commands ........................................................................ 3-27
Using Control Statements........................................................................... 3-27
Enhancing Loop Processing ....................................................................... 3-27
Creating and Using Aliases ........................................................................ 3-27
Creating and Using Functions .................................................................... 3-27
Using the ENV Variable............................................................................. 3-27
Using Directory Information ...................................................................... 3-27
Exercises .......................................................................................................... 3-28
Instructions ................................................................................................. 3-28
4 Advanced Shell Scripting
About This Chapter............................................................................................... 4-3
Introduction .................................................................................................. 4-3
Objectives ..................................................................................................... 4-3
Resources...................................................................................................... 4-3
Menu Processing................................................................................................... 4-4
List Processing (select)................................................................................. 4-4
User Prompts ................................................................................................ 4-5
Prompt Reminder ......................................................................................... 4-5
Signals….. ............................................................................................................ 4-6
Introduction .................................................................................................. 4-6
Assigned Signals in Tru64 UNIX................................................................. 4-6
Obtaining a List of Signals ........................................................................... 4-7
Signal Actions .............................................................................................. 4-7
User Programming Example ........................................................................ 4-7
Common Signals .......................................................................................... 4-8
Notes about the kill Signal ........................................................................... 4-8
viii EY-G994E-SG-0002
Using kill to Send a Signal to a Process ....................................................... 4-9
Handling Signals in a Shell Script ...................................................................... 4-10
The trap Command ..................................................................................... 4-10
Using trap to Clean up Temporary Files .................................................... 4-10
The /tmp Directory ..................................................................................... 4-10
Using trap to Catch, Ignore, and Reset Signals .......................................... 4-11
Ignoring Other Signals ............................................................................... 4-11
Executing Commands................................................................................. 4-11
Displaying trap Settings ............................................................................. 4-11
Using the trap Command............................................................................ 4-12
date Formats ............................................................................................... 4-12
Integer Arithmetic in the KornShell ................................................................... 4-13
The Integer Data Type................................................................................ 4-13
Declaring Integers ...................................................................................... 4-13
The (( )) Operator ....................................................................................... 4-13
Supported Operators................................................................................... 4-13
Using Math Operations in a while Loop .................................................... 4-14
Declaring Integers of Different Bases ........................................................ 4-14
Converting from Decimal to Hexadecimal................................................. 4-14
Floating-Point Arithmetic in the KornShell........................................................ 4-15
Not Supported in ksh88.............................................................................. 4-15
Utilities Operators ...................................................................................... 4-15
Using Operators from Utilities ................................................................... 4-15
Supported in ksh93..................................................................................... 4-16
Variable Arrays................................................................................................... 4-17
Declaring Arrays ........................................................................................ 4-17
Assigning Values to an Array..................................................................... 4-17
Accessing the Values of an Array .............................................................. 4-17
Using Variable Arrays................................................................................ 4-18
Loading an Array from a Disk File ............................................................ 4-19
The set –A Command................................................................................. 4-19
Assigning Values to an Array Using the set -A Command........................ 4-20
Changing IFS (Internal Field Separator) ............................................................ 4-21
Introduction ................................................................................................ 4-21
Using IFS.................................................................................................... 4-22
Command Evaluation ......................................................................................... 4-23
Order of Evaluation (eval).......................................................................... 4-23
Common Errors in Evaluation.................................................................... 4-23
Repairing Common Errors in Evaluation – Part 1...................................... 4-24
Repairing Common Errors in Evaluation – Part 2...................................... 4-24
Tools for Shell Scripts ........................................................................................ 4-25
The df Command........................................................................................ 4-25
The sed and cut Commands with df ........................................................... 4-25
Setting Positional Parameters ............................................................................. 4-26
Setting and Changing Positional Parameters.............................................. 4-26
Using Positional Parameters....................................................................... 4-26
EY-G994E-SG-0002 ix
Using the ls Command ............................................................................... 4-27
Making Nice Neat Columns ............................................................................... 4-28
Slicing off the Leftmost Characters............................................................ 4-28
Using Wildcards ......................................................................................... 4-29
Slicing off Portions of a Pathname............................................................. 4-29
The ## Operator.......................................................................................... 4-29
Slicing off the Rightmost Characters ......................................................... 4-30
Slicing off the Suffix from a Filename....................................................... 4-30
Renaming Files........................................................................................... 4-31
Finding the Length of a String.................................................................... 4-31
Using ${#str} ............................................................................................. 4-32
Summary .......................................................................................................... 4-33
Creating and Using Menu Processors......................................................... 4-33
Identifying and Using Signals .................................................................... 4-33
Handling Signals in a Shell Script.............................................................. 4-33
Using Integer Arithmetic............................................................................ 4-33
Using Floating-Point Arithmetic ................................................................ 4-33
Creating and Using Variable Arrays .......................................................... 4-33
Changing the Internal Field Separator........................................................ 4-33
Performing Command Evaluation Using eval............................................ 4-33
Using Tools for Shell Scripts ..................................................................... 4-33
Setting Positional Parameters ..................................................................... 4-34
Exercises .......................................................................................................... 4-35
Instructions ................................................................................................. 4-35
5 Tips and Tricks
About This Chapter............................................................................................... 5-3
Introduction .................................................................................................. 5-3
Objectives ..................................................................................................... 5-3
Resources...................................................................................................... 5-3
Here Documents ................................................................................................... 5-4
Sending Mail from a Shell Script ................................................................. 5-4
Using the mail Command............................................................................. 5-4
Redirection Characters ................................................................................. 5-4
Invoking the ed Utility.................................................................................. 5-5
Substituting Arguments Inside a Here Document ........................................ 5-5
Using a Here Document to Spawn a Child Shell Script............................... 5-6
File Input and Output Operations ......................................................................... 5-7
Opening a Stream for Reading ..................................................................... 5-7
Opening a Stream for Writing ...................................................................... 5-8
Command Options Processing.............................................................................. 5-9
Introduction .................................................................................................. 5-9
Using Command Line Options..................................................................... 5-9
Error Checking ........................................................................................... 5-10
Detecting Invalid Command Line Options................................................. 5-10
+ Switches and - Switches.......................................................................... 5-10
x EY-G994E-SG-0002
Using + and - Switches............................................................................... 5-11
The OPTIND Variable ............................................................................... 5-11
Using the OPTIND Variable ...................................................................... 5-12
Pattern Matching Operators ................................................................................ 5-13
Review........................................................................................................ 5-13
Matching..................................................................................................... 5-13
Special KornShell-Only Wildcards ............................................................ 5-14
Using a Lock File to Synchronize Access .......................................................... 5-15
Using a Lock File ....................................................................................... 5-15
Testing for Superuser ................................................................................. 5-15
Test 1 .......................................................................................................... 5-15
Test 2 .......................................................................................................... 5-15
setuid Bit for Shell Scripts.......................................................................... 5-16
The setuid Concept ..................................................................................... 5-16
Using setuid ................................................................................................ 5-17
Using sush .................................................................................................. 5-17
KornShell Command Processing ........................................................................ 5-19
KornShell Command Line Parsing............................................................. 5-19
Command Word Precedence ...................................................................... 5-19
Exercise/Discussion.................................................................................... 5-20
Co–processes ...................................................................................................... 5-21
Definition.................................................................................................... 5-21
Running a Script as a Co-process............................................................... 5-21
Co-process Diagram ................................................................................... 5-22
Using Multiple Co-processes ..................................................................... 5-23
Using Co-processes .................................................................................... 5-24
Starting preserve shell From .profile .......................................................... 5-24
Single Quotes.............................................................................................. 5-25
The if Command......................................................................................... 5-25
The print Command.................................................................................... 5-25
The for Loop............................................................................................... 5-25
The read Command .................................................................................... 5-25
Readability and Maintainability.......................................................................... 5-26
Suggestions................................................................................................. 5-26
Include Comments at the Beginning of a Script......................................... 5-26
Organize the Script into Sections of Related Commands .......................... 5-26
Explain Obscure Syntax with Comments................................................... 5-26
Use Blank Lines to Delineate Compound Statements................................ 5-27
Indent the Bodies of Compound Statements .............................................. 5-27
Set up Compound Statements Consistently................................................ 5-27
Avoid Combining Unrelated Commands ................................................... 5-28
Performance ........................................................................................................ 5-29
Improving Performance.............................................................................. 5-29
Shell Script Examples......................................................................................... 5-30
Searching the /etc/passwd File ................................................................... 5-30
Repeatedly Invoking a Command .............................................................. 5-30
EY-G994E-SG-0002 xi
Using replay_opt ........................................................................................ 5-31
The Leading Colon ..................................................................................... 5-32
Checking Commands.................................................................................. 5-32
A Better grep .............................................................................................. 5-33
The sed Command...................................................................................... 5-33
How the sed Command Works................................................................... 5-34
Summary .......................................................................................................... 5-35
Using Here Documents............................................................................... 5-35
Using File I/O Operators ............................................................................ 5-35
Using Command Options Processing......................................................... 5-35
Reviewing Pattern Matching Operators ..................................................... 5-35
Using a Lock File to Synchronize Access.................................................. 5-35
Using Kornshell Command Processing...................................................... 5-35
Creating and Using Co-Processes .............................................................. 5-35
Practice Tips on Code Readability and Maintainability............................. 5-35
Identifying Ways to Improve Performance ................................................ 5-35
Exercises .......................................................................................................... 5-36
Instructions ................................................................................................. 5-36
Appendix A
List of Symbols .....................................................................................................A-3
Symbol Table................................................................................................A-3
Appendix B

xii EY-G994E-SG-0002
Tables

Table 0-1 Course Schedule ................................................................................xxiv


Table 0-2 Course Conventions............................................................................xxv
Table 1-1 Wildcard Metacharacters .................................................................... 1-8
Table 1-2 vi Commands .................................................................................... 1-10
Table 1-3 Shell Variables .................................................................................. 1-12
Table 1-4 Job References .................................................................................. 1-17
Table 1-5 Sample vi Commands ....................................................................... 1-18
Table 2-1 Positional Parameters.......................................................................... 2-7
Table 2-2 Escape Sequences ............................................................................. 2-10
Table 2-3 Integer Comparison Operators.......................................................... 2-17
Table 2-4 String Comparison ............................................................................ 2-18
Table 2-5 File Enquiry Operators...................................................................... 2-19
Table 4-1 Assigned Signals in Tru64 UNIX....................................................... 4-6
Table 4-2 Common Signals................................................................................. 4-8
Table 5-1 Redirection Characters........................................................................ 5-4
Table 5-2 UNIX Wildcard Patterns................................................................... 5-13
Table 5-3 Special Wildcards ............................................................................. 5-14
Table 5-4 print Command Co-process .............................................................. 5-25
Table A-1 Common Symbols...............................................................................A-3

EY-G994E-SG-0002 xiii
xiv EY-G994E-SG-0002
Figures

Figure 0-1 Course Map....................................................................................... xxiii


Figure 2-1 Login Process Diagram....................................................................... 2-4
Figure 2-2 Positional Parameters Assignment...................................................... 2-7

EY-G994E-SG-0002 xv
xvi EY-G994E-SG-0002
About This Course

EY-G994E-SG-0002 xvii
UNIX Shell Programming Featuring KornShell

xviii EY-G994E-SG-0002
About This Course

About This Course


Introduction
This section describes the contents of the course, suggests ways in which you can
most effectively use the materials, and sets up the conventions for the use of terms
in the course. It includes:
! Course description — a brief overview of the course contents
! Target audience — who should take this course
! Prerequisites — the skills and knowledge needed to ensure your success in
this course
! Course goals and nongoals — what skills or knowledge the course will and
will not provide
! Course organization — the structure of the course
! Course map — the sequence in which you should take each chapter
! Chapter descriptions — brief descriptions of each chapter
! Time schedule — an estimate of the amount of time needed to cover the
chapter material and lab exercises
! Course conventions — explanation of symbols and signs used throughout this
course
! Resources — manuals and books to help you successfully complete this
course

Course Description
KornShell scripts are powerful tools for administering systems, manipulating text,
or managing large collections of files. In this course, you will learn how to write
scripts for the newest and most important of the standard UNIX shells, the
KornShell. If you already know the C shell, you can build on that knowledge and
learn to write more advanced scripts with the KornShell.

Place in Curriculum
This course is a basic user course. It is also the prerequisite for the UNIX
programming curriculum.

Target Audience
This course will be of particular benefit to system analysts, application
programmers, system administrators and technical personnel who want to learn
how to write KornShell scripts.

EY-G994E-SG-0002 xix
UNIX Shell Programming Featuring KornShell

Prerequisites
It is expected that the student will have attended a UNIX Utilities & Commands
course or have the equivalent knowledge.

Course Goals
To program the KornShell, you should be able to:
! Define and redirect standard files
! Use the pipe operator to connect two commands
! Use wildcard metacharacters
! Use the history command to recall commands
! Use shell variables
! Define and use foreground and background processes
! Define and use processes and co-processes
! Use directory information in scripts
! Set and use positional parameters
! Use escape sequences
! Pass arguments to shell scripts and test arguments
! Use conditions and control statements
! Use the if command in a decision
! Use while and for loops
! Debug scripts using execute trace and verbose trace
! Use command substitution
! Group shell commands
! Create and use aliases and functions
! Identify and use signals and handle signals in a shell script
! Use integer and floating-point arithmetic
! Create and use variable arrays
! Perform command evaluation using eval
! Use Here documents and file I/O operators
! Use command options processing
! Use a lock file to synchronize access

xx EY-G994E-SG-0002
About This Course

Nongoals
This course does not cover the following topics:
! Managing the software process
! Transferring files and executing commands across TCP/IP networks
! System administration

EY-G994E-SG-0002 xxi
UNIX Shell Programming Featuring KornShell

Taking This Course


Course Organization
This Course Guide is divided into chapters designed to cover a skill or related
group of skills required to fulfill the course goals. Illustrations are used to present
conceptual material. Examples are provided to demonstrate concepts and
commands.
In this course, each chapter consists of:
! An introduction to the subject matter of the chapter
! One or more objectives that describe the goals of the chapter
! The text of each chapter, which includes outlines, tables, diagrams, and
examples
! The summary, which highlights the main points presented in the chapter
! The exercises, which enable you to practice your skills and measure your
mastery of the information learned during the course

Course Map
The Course Map shows how each chapter is related to other chapters and to the
course as a whole. Before studying a chapter, you should master all of its
prerequisite chapters. The prerequisite chapters are depicted before the following
chapters on the Course Map. The direction of the arrows determines the order in
which the chapters should be covered.

xxii EY-G994E-SG-0002
About This Course

Figure 0-1 Course Map

Review

Getting Started
with Scripting

Intermediate
Shell Scripting

Advanced
Shell Scripting

Tips and Tricks

Chapter Descriptions
A brief description of each chapter is listed below.
! Review — Reviews of basic shell concepts, including redirecting standard
files, using the pipe operator and wildcard metacharacters, and the history
command. Using shell variables and understanding foreground and
background processes, vi commands is also discussed.
! Getting Started with Scripting — Describes how to execute a shell script,
pass arguments to scripts, test arguments, and debug scripts. Also, how to use
the if command, and while and for loops; use positional parameters,
escape sequences, condition and control statements, and command
substitution.

EY-G994E-SG-0002 xxiii
UNIX Shell Programming Featuring KornShell

! Intermediate Shell Scripting — Introduces more shell control features such


as grouping shell commands, more advanced looping and branching
statements, and functions. It compares different techniques for executing
shell scripts and introduces aliases.
! Advanced Shell Scripting — Provides more advanced scripting techniques.
Shows how to create a menu, handle signals in a shell script, perform integer
and floating-point arithmetic, and use variable arrays. Also reviews the order
of command evaluation and shows some common programming errors. It
demonstrates how to use some commands and utilities in your scripts.
! Tips and Tricks — Provides some final scripting tips, such as Here
documents, file I/O, processing command line options, and using a lock file
to control access to data files. Reviews pattern matching with wildcards and
KornShell command line processing. Shows how to communicate with a co-
process, and identifies ways to improve readability, maintainability, and
performance of scripts.

Time Schedule
The amount of time required for this course depends on each student’s background
knowledge, experience, and interest in the various topics. Use the following table
as a guideline.

Table 0-1 Course Schedule


Day Course Chapter
1 Review
1 Getting Started with Scripting
2 Intermediate Shell Scripting
2 Advanced Shell Scripting
3 Tips and Tricks

xxiv EY-G994E-SG-0002
About This Course

Course Conventions
This book uses the following course conventions.

Table 0-2 Course Conventions


Convention Description
keyword Keywords and new concepts are displayed in this typeface.
example Examples, commands, options, and pathnames are
displayed in this typeface.
command(n) Cross-references to command documentation include the
section number in the reference pages. For example,
fstab(5) means fstab is referenced in Section 5.
$ A dollar sign represents the user prompt for the Bourne shell
and KornShell.
% A percent sign represents the user prompt for the C shell.
# A number sign represents the superuser prompt.
<key> This symbol indicates that the named key on the keyboard is
pressed.
. In examples, an ellipsis indicates that not all lines in the
. example are shown.
.
[ ] In syntax descriptions, brackets indicate items that are
optional.
variable In syntax descriptions, italics indicate items that are variable.

Resources
For more information on the topics in this module, refer to the following:
! Tru64 UNIX Reference Pages
! Command and Shell User’s Guide
! Kornshell Programming Tutorial, Barry Rosenberg, Reading, MA: Addison-
Wesley Publishing Co., 1991 (ISBN 0-201-56324-X)
! The Kornshell Command and Programming Language, Morris I. Bolsky and
David G. Korn, Englewood Cliffs, NJ: Prentice Hall, 1989 (ISBN 0-13-
516972-0)

EY-G994E-SG-0002 xxv
UNIX Shell Programming Featuring KornShell

xxvi EY-G994E-SG-0002
Review
Module 1

EY-G994E-SG-0002 1–1
UNIX Shell Programming Featuring KornShell

1–2 EY-G994E-SG-0002
Review

About This Chapter


Introduction
This chapter reviews basic shell concepts, including redirecting standard files,
using the pipe operator and wildcard metacharacters, shell variables, foreground
and background processes, and the vi editor.

Objectives
When you complete this module, you will be able to:
! Define and redirect standard files
! Use the pipe operator to connect two commands
! Use wildcard metacharacters
! Use the history command to recall commands
! Use shell variables
! Define and use foreground and background processes
! Use some vi commands

Resources
For more information on the topics in this module, refer to the following:
! Tru64 UNIX Reference Pages
! Command and Shell User’s Guide
! Kornshell Programming Tutorial, Barry Rosenberg, Reading, MA: Addison-
Wesley Publishing Co., 1991 (ISBN 0-201-56324-X)
! The Kornshell Command and Programming Language, Morris I. Bolsky and
David G. Korn, Englewood Cliffs, NJ: Prentice Hall, 1989 (ISBN 0-13-
516972-0)

EY-G994E-SG-0002 1–3
UNIX Shell Programming Featuring KornShell

Standard Files
Standard Input
Standard input refers to the stream from which commands and scripts gather input.

Standard Output
Standard output refers to the stream to which commands and scripts send their
(nonerror) output.
Example
who, date, ls, ...

Standard Error (Diagnostic Output)


Standard error refers to the stream to which commands and scripts send their error
messages.

Status of the Standard Files at Login


The following table shows the status of the standard files at login.

Status Stream sent to…


Standard input Keyboard
Standard output Terminal
Standard error Terminal

1–4 EY-G994E-SG-0002
Review

Redirecting Standard Files


Redirecting Output to a New File
The following code example shows how to redirect standard output to a new file.
Example
$ who # standard output goes to default standard output
user tty02 Dec 5 11:30
kjd tty04 Dec 5 12:53
sam tty00 Dec 5 13:03
$ who > whofile # redirect standard output to file “whofile”
$ cat whofile
user tty02 Dec 5 11:30
kjd tty04 Dec 5 12:53
sam tty00 Dec 5 13:03

Redirecting Output to an Existing File


By default, the > operator causes the KornShell to empty the target file. If you are
nervous about deleting important files, you can set the noclobber option, as
shown in the following example.
Example
$ set -o noclobber # prevent inadvertent file deletes
$ date > whofile
whofile: file already exists
$ date >| whofile
$ cat whofile
Thu Feb 1 13:38:36 EST 1998
$ set +o noclobber # clear noclobber
$ date > whofile
$ cat whofile
Thu Feb 1 14:22:17 EST 1998

Appending Redirected Output to an Existing Output File


Use the >> operator to append standard output to a file.
For example, the following command writes the output of the ls command to the
end of lsfile. If lsfile does not already exist, the following command will
create it.
$ ls >> lsfile

EY-G994E-SG-0002 1–5
UNIX Shell Programming Featuring KornShell

Redirecting Input
The < operator redirects standard input, telling the KornShell to gather input from
the specified file.
For example, the following command mails the contents of file message to user
john.
$ mail john < message

Redirecting Standard Error


Programs and scripts are supposed to write all error messages to standard error. By
default, standard error goes to the terminal. Use the 2> operator to redirect
standard error to a file.
The following code example redirects the error messages generated by the cat
command to a file named problem.
Example
$ cat nonexistantfile 2> problem
$ cat problem
file1: Permission denied

Redirecting Both Standard Error and Standard Output


You can redirect standard output to one file and standard error to another file. You
can also send standard output and standard error to the same file by using the
2>&1 operator, as shown below.
$ cmd.ksh > out 2> err # send stdout to out; send stderr to err
$ cmd.ksh > combo 2>&1 # send stdout and stderr to combo

Eliminating Any Output


Sometimes, you do not care about the standard output. To eliminate standard
output, redirect standard output to /dev/null as shown below.
$ shell_command > /dev/null

The /dev/null File


The /dev/null file is a special file.
! It is the UNIX data sink (bit bucket).
! When read, it yields EOF.
! When written, it provides an infinite sink.

1–6 EY-G994E-SG-0002
Review

The Pipe Operator


Definition
The pipe (or pipeline) operator (|) connects two commands so that the standard
output of the command to the left of the pipe operator becomes the standard input
to the command on the right of the pipe.

Using the Pipe Operator (|)


The following example shows the use of the pipe operator.
Example
$ who
sam tty10 Jun 30 10:54
mary tty03 Jun 30 08:13
mgr tty12 Jun 28 17:34
$ who | sort # the output of who becomes the input to sort
mary tty03 Jun 30 08:13
mgr tty12 Jun 28 17:34
sam tty10 Jun 30 10:54
$ who | wc -l # the output of who becomes the input to wc 3
$ who | sort > people # the output of who becomes the input
to sort
$ cat people
mary tty03 Jun 30 08:13
mgr tty12 Jun 28 17:34
sam tty10 Jun 30 10:54

Using the Pipe Operator to List All Block-Structured Devices


Use the following command to list all files in /dev which are block structured
devices.
$ ls -l /dev | grep ’^b’
brw------- 1 root system 8, 3072 Apr 13 10:19 rz3a
brw------- 1 root system 8, 3073 Apr 13 10:19 rz3b
brw------- 1 root system 8, 3074 Apr 13 10:19 rz3c
brw------- 1 root system 8, 3075 Apr 13 10:19 rz3d

Using the Pipe Operator to List All Files in /dev on the Printer
Use the following command to write all files in /dev which are block structured
devices and then send that list to the printer.
$ ls -l /dev | grep ’^b’ | lpr

EY-G994E-SG-0002 1–7
UNIX Shell Programming Featuring KornShell

Wildcard Metacharacters
Wildcards
Table 1-1 shows the wildcards available in the Bourne Shell, C Shell, and
KornShell. Note that the KornShell also supports five additional wildcards not
supported by the Bourne Shell and C shell.

Table 1-1 Wildcard Metacharacters


Wildcard Meaning
* Matches any string of characters, including a null string
? Matches exactly ONE of any character
[chars] Matches exactly ONE of any character among those
included between the brackets
~ Matches user's home directory
~sam Matches the home directory for user sam

Using Wildcards
The following code example shows how wildcards are used.
Example
$ ls -a
. .login get.c put.c x x2 xfour z4
.. a msg.c put.o x1 x3 y4sale zed

$ ls x?
x1 x2 x3
$ ls x*
x x1 x2 x3 xfour
$ ls x??
x?? not found
$ ls [xyz]*
x x2 xfour z4
x1 x3 y4sale zed
$ ls [x-z]4*
y4sale z4
$ ls [!a-y]*
z4 zed

$ ls -l ~/srcfile
-rw-r--r-- 1 mary 362 Nov 3 12:13 srcfile
$ cp myfile ~

1–8 EY-G994E-SG-0002
Review

Command History and Command Line Editing


The history Command
The history command, by default, returns the 15 most recently issued
commands, as shown in the following example.
Example
$ history

9 cat letter.mail
10 date
11 rm mmdc.c
12 ls -l /class/user/save
13 chmod a-x mbox
14 date
15 history

$ r 10 # repeat command number 10


date
Fri Feb 2 12:18:17 EST 1998

$ r cat # repeat the most recent cat command


cat letter.mail
Don’t forget lunch on Friday.
Susan

$ r ch # repeat the most recent command that started with ‘ch’


chmod a-x mbox

Recalling a Specific Command


To recall a specific command, include the command number in the prompt string,
as shown below.
$ PS1=’! $ ’
18 $

EY-G994E-SG-0002 1–9
UNIX Shell Programming Featuring KornShell

Recalling a Command
Another way to repeat recently issued commands is to press Esc/K over and over
until you reach the command you want to redo, as shown in the following
example.
Example
$ set -o vi #establish vi mechanism for redoing commands
$ <esc>k #retrieve latest command
$ set -o vi #cursor at beginning of line
k #retrieve next latest command
$ chmod a-x mbox #cursor at beginning of line
^C #terminate history scan

Using vi Commands
When typing commands on the KornShell command line, you can use just about
any vi commands to edit the command line.
Table 1-2 lists vi commands and their meanings.

Table 1-2 vi Commands


Command Meaning
h j k l Move the cursor or arrow keys (left, down, up, right)
x Delete character
Cw Change the current word
Dw Delete the current word
B Move the cursor backwards one word

Filename Completion
If you only know the first letter or first few letters of a command, you can type
<esc>\ and the KornShell will complete the remaining letters for you.
Note that sometimes several files begin with the same initial letters, so you may
need to type in quite a few letters before the KornShell can determine the filename,
as shown in the following code example.

1 – 10 EY-G994E-SG-0002
Review

Example
$ ls
comm edit files make machine shell

$ lpr ed<esc>\
lpr edit

$ chmod 755 m<esc>\


chmod 755 ma
chmod 755 mak<esc>\
chmod 755 make

$ grep sam /etc/pas<esc>\


grep sam /etc/passwd
sam:A34fjU76Fcv5g:203:0:Samuel Adams:/usr/users/sam:/bin/ksh

EY-G994E-SG-0002 1 – 11
UNIX Shell Programming Featuring KornShell

Shell Variables
Guidelines
Variable names MUST begin with a letter or underscore, but may contain digits,
letters, or underscores.

Using Variables
The following example shows how to assign and display shell variables.
Example
$ var1=”Brown” # assign a value to variable var1
$ var2=”stone” # assign a value to variable var2
$ concat=”$var1$var2” # concatenate two variables

$ print "$var1" # write the value of var1 to standard


output
Brown

$ print "$var2"
stone

$ print "$concat"
Brownstone

Shell Variables
Table 1-3 lists commonly used KornShell reserved variables. The complete list of
environment variables available on your system will be a superset of this list.

Table 1-3 Shell Variables


KornShell Meaning
Variable
HISTFILE File in which history is saved: default = $HOME/.sh_history
HISTSIZE Size of the history list: default = 128
HOME Home directory: default argument for the cd command
IFS Internal field separator: default = blank space, tab, and newline
MAIL Users mail file: if set, user is informed by shell of arrival of mail every
$MAILCHECK seconds
OLDPWD Previous working directory set by the cd command
PATH Name
PPID Parent process ID
PS1 Primary prompt string: default = "$ "
PS2 Secondary prompt string: default = "> "

1 – 12 EY-G994E-SG-0002
Review

Table 1-3 Shell Variables (Continued)


KornShell Meaning
Variable
PWD Present working directory set by the cd command
RANDOM A random integer from 1 to 32767
TERM Terminal type
TMOUT A number of seconds: if a command is not issued to the shell in this
time period, the shell exits
TMOUT=0 Inhibits this automatic logoff feature

Exporting Variables
Suppose you declare a variable named var on the shell command line.
If you do export var, then shell scripts can access the value of var.
If you do not export var, then shell scripts cannot access the value of var.
The following example contrasts the results of exporting and not exporting the
variable.
Example
$ cat shscript
print $var

$ var=12345
$ export var
$ shscript
12345
If an exported variable is modified, the new value is exported.

Listing Exported Variables


To get a list of all exported variables in the current environment, call the export
command without any arguments, as shown in the following example.
Example
$ export var=12345 #define & export
$ var=999 #modify value of an exported variable
$ export #list environment
ENV=~/.environ
HOME=/usr/users/sam
LOGNAME=sam
MAIL=/usr/mail/spool/sam
PATH=/usr/users/sam/bin:/bin:/usr/bin/:/etc:.
TERM=vt100
TZ=EST05EDT
var=999

EY-G994E-SG-0002 1 – 13
UNIX Shell Programming Featuring KornShell

Foreground and Background Processes


Introduction
Programs can run in either the foreground or the background. By default, all
programs run in the foreground.

Running a Program in the Background


To run a program in the background, place an ampersand (&) at the end of the
command line, as shown below.
$ payroll # run a command in the foreground
$ payroll & # run the same command in the background

Running Multiple Commands in the Background


Only one command at a time can run in the foreground; however, multiple
commands can simultaneously run in the background. The background is a good
place to run time-consuming commands.
Use the ps x command to display running processes, including background
processes, as shown in the following example.
Example
$ ps x # what processes are currently running?
PID STAT TT TIME CMD
2680 S 01 0:00 - (ksh)
3204 R 01 0:00 ps x

$ grep ‘^B’ * > report & # run a command in the background


[1] 4750

$ ps x # the ps command confirms that the grep command is


running
PID STAT TT TIME CMD
2680 S 01 0:00 - (ksh)
4751 R 01 0:05 ps x
4750 R 01 0.01 grep ^B

1 – 14 EY-G994E-SG-0002
Review

Running a Pipeline in the Background


If you run a pipeline in the background, both processes show up in ps, as shown
in the following example.
Example
$ grep ’^B’ * | lpr & # run a pipeline in the background
[1] 4791

$ ps x
PID STAT TT TIME CMD
2680 S 01 0:00 - (ksh)
4792 R 01 0:07 ps x
4790 R 01 0:07 grep ^B
4791 R 01 0:01 lpr

Obtaining a Long Listing of Process Status


The following code example shows how to obtain a long listing of process status.
Example
$ grep ’^B’ file > report &
[1] 1706

$ ps lx # options to ps vary considerably between UNIX


versions
F UID PID PPID PRI NI STAT TT TIME COMMAND
10808001 10 1704 1 15 0 S 01 0:00 - (ksh)
10008101 10 1706 1704 46 0 R 01 0:00 grep ’^B’
10008101 10 1708 1704 43 0 R 01 0:00 ps lx

$ jobs -l # get a detailed list of running background


processes
[1] + 1706 Running grep ^B file > report

EY-G994E-SG-0002 1 – 15
UNIX Shell Programming Featuring KornShell

Moving a Foreground Job to the Background


The following code example shows how to move a foreground job to the
background.
Example
$ sort abc.dat | lpr
^Z
Stopped
$ jobs
[1] + Stopped sort abc.dat | lpr
$ bg %1
[1] sort abc.dat | lpr &
$ grep ’^B’ file > report &
[2] 264
$ jobs
[2] + Running grep ^B file > report
[1] - Running sort abc.dat | lpr
! + identifies the current job. This job will be the default job for commands
like bg, and so forth.
! - identifies the previous job. This job may have been the current job at one
time, and will be the current job when the current job finishes.

Moving a Background Job into the Foreground


The following code example shows how to move a background job into the
foreground.
Example
$ fg %2
lpr report
$

Waiting for a Background Process to Complete


The following code example shows how to wait for a background process to
complete.
Example
$ wait %1
ls
$ a.tmp cat.tmp file klm.tmp rpt.c xyzv.tmp

Terminating a Background Job


The following code example shows how to terminate a background job.
Example
$ kill %1

1 – 16 EY-G994E-SG-0002
Review

Stopping a Background Job


The following code example shows how to stop a background job.
Example
$ stop %2

Job References
Table 1-4 shows job references for the fg, bg, wait, and kill commands.

Table 1-4 Job References


Job Reference Meaning
%n Job number
%str Job whose command begins with str
%?str Job whose command contains str
%+ Current job
%%
%- Previous job

EY-G994E-SG-0002 1 – 17
UNIX Shell Programming Featuring KornShell

vi Refresher
Sample vi Commands
The vi editor is a powerful text editor.
Sample commands and their meanings are listed in Table 1-5.

Table 1-5 Sample vi Commands


vi Command Meaning
<Esc>:wq Write the file to disk, then end your vi session
<Esc>:w Write the file to disk
i Go into insert text mode
o Open a new line, then go into insert text mode
<Esc> Go into command mode
x Delete the current character
7x Delete the current character and the 6 that follow it
J Join two lines
cw Change this word
2dw Delete this word and the next word
r Replace the current character
p Paste the contents of the paste buffer
4yy Copy this line and the next three lines into the paste
buffer
3dd Delete this line and the next two lines; place the deleted
lines into the paste buffer

1 – 18 EY-G994E-SG-0002
Review

Summary
Defining and Redirecting Standard Files
Standard files are the stream from which commands and scripts gather input.
These streams can be redirected using operators described in this chapter.

Using the Pipe Operator to Connect Two Commands


The pipe (or pipeline) connects commands making the output of the command to
the left of the pipe the input of the command to the right of the pipe.

Using Wildcard Metacharacters


The wildcard metacharacter is used as an ambiguous reference to another
character. Wildcards are used in character matching.

Using the History Command to Recall Commands


The history command returns the last 15 commands used. Commands can also
be recalled using other methods described in this chapter.

Using Shell Variables


Variables are declared and used in scripts and can be exported. KornShell uses
reserved variables.

Defining and Using Foreground and Background Processes


Processes can be run as foreground or background processes. Only one
foreground process can be running, but several background processes may run
concurrently. Use background processes to run time-consuming commands. This
chapter described how to create foreground and background processes and how to
move a foreground process to the background and vice versa.

Reviewing Some vi Commands


The vi editor can be used in KornShell. This chapter reviewed some vi
commands and their meanings.

EY-G994E-SG-0002 1 – 19
UNIX Shell Programming Featuring KornShell

1 – 20 EY-G994E-SG-0002
Getting Started with Scripting
Module 2

EY-G994E-SG-0002 2–1
UNIX Shell Programming Featuring KornShell

2–2 EY-G994E-SG-0002
Getting Started with Scripting

About This Chapter


Introduction
This chapter starts writing and executing shell scripts. It shows how to pass
arguments to scripts, test arguments, and debug scripts. It introduces conditional
and loop statements, positional parameters, escape sequences, operators, and
command substitution.

Objectives
When you complete this module, you will be able to:
! Define and use processes
! Execute a shell script
! Use positional parameters
! Use escape sequences
! Pass arguments to shell scripts
! Test arguments
! Use operators
! Use conditions, control statements and loops
! Debug scripts using execute trace and verbose trace
! Use command substitution

Resources
For more information on the topics in this module, refer to the following:
! Tru64 UNIX Reference Pages
! Command and Shell User’s Guide
! Kornshell Programming Tutorial, Barry Rosenberg, Reading, MA: Addison-
Wesley Publishing Co., 1991 (ISBN 0-201-56324-X)
! The Kornshell Command and Programming Language, Morris I. Bolsky and
David G. Korn, Englewood Cliffs, NJ: Prentice Hall, 1989 (ISBN 0-13-
516972-0)

EY-G994E-SG-0002 2–3
UNIX Shell Programming Featuring KornShell

Processes
Definition
A process is the kernel environment in which a user program executes. The kernel
schedules the process to execute, not programs.

Process as Program Wrapper


Each program runs in a process; the process is a program wrapper. For example, a
process is to a program what a nest is to an egg.

fork() and exec()


The fork() command creates a new process that runs the same program as the
parent (a new nest with the same egg).
The exec() command replaces the program in the process with a new program.

Login Process Diagram


Figure 2-1 shows a ksh login process forking and executing a child process.

Figure 2-1 Login Process Diagram

Command is Shell Parent Parent


Typed ‘forks’ Waits Resumes
ksh ksh ksh ksh

ksh Shell Command

Shell ‘execs’
Command

2–4 EY-G994E-SG-0002
Getting Started with Scripting

Executing Shell Scripts


Introduction
A KornShell script can be executed in either of the following two ways:
! As an argument to ksh
! By name
The way the script is invoked seldom makes any difference in the way it runs.

Executing a Shell Script as an Argument to ksh


To run a KornShell script as an argument to ksh, you simply type ksh and then
the pathname of the KornShell script, for example:
$ ksh myshellscript

Permission Requirements for Executing a Shell Script as an


Argument to ksh
To run a shell script as an argument to ksh, the user invoking the shell script must
have read permission on the shell script file.

Executing Shell Scripts by Name


To run a shell script by name, simply type its pathname:
$ myshellscript

Permission Requirements for Executing Shell Scripts by Name


To run a shell script by name, the user invoking the shell script must have read
and execute permission on the shell script file.

The #! Operator
By default, the KornShell assumes that the scripts you invoke are KornShell
scripts. However, you can run a C shell script from the KornShell command line.
Conversely, you can run a KornShell script from the C shell command line.
If you are writing a KornShell script and want to allow anyone (no matter what
shell they are using) to run it, place the following statement as the first line of your
script:
#!/usr/bin/ksh

The print Statement


Use the KornShell built-in print statement to write strings to standard output.

EY-G994E-SG-0002 2–5
UNIX Shell Programming Featuring KornShell

print Statement Syntax


The syntax of the print command is as follows:
print arg
Write arg to standard output.

Using the print Statement to Write Standard Output


The following examples show how to use the print statement to write standard
output.
Examples
$ print “Hello friends” # write a string to standard output
Hello friends

$ movie=”Casablanca” # assign a value to a variable named movie


$ print “$movie” # write the value of movie to standard output
Casablanca
$ print “movie” # be careful to use the dollar sign
movie

$ cat myscript # here is a simple KornShell script


print “It was the best of times.”
print “It was the worst of times.”

$ myscript # execute myscript


It was the best of times.
It was the worst of times.

2–6 EY-G994E-SG-0002
Getting Started with Scripting

Using Positional Parameters


Positional Parameters
Positional parameters allow you to pick out arguments. Table 2-1 shows the
positional parameters.

Table 2-1 Positional Parameters


Parameter Assignment
$# Total number of positional parameters
$* All positional parameters
$0 The filename of the KornShell script
$1 The first positional parameter
$2 The second positional parameter
$3 The third positional parameter

${10} The tenth positional parameter

Arguments
Figure 2-2 shows how the positional parameters are assigned.

Figure 2-2 Positional Parameters Assignment


$ shell_script arg1 arg2 ... arg9 arg10 arg11 ...

$0 $1 $2 $9 ${10} ${11}

EY-G994E-SG-0002 2–7
UNIX Shell Programming Featuring KornShell

Using Positional Parameters


The following script example uses the positional parameters to analyze the
command line arguments passed by the user.
Example
$ cat myscript
print “You passed a total of $# parameters, which were: “
print “$*”
print “The first parameter was $1”
print “The second parameter was $2”
print “The third parameter was $3”

$ myscript Abc xyz 1234


You passed a total of 3 parameters, which were:
Abc xyz 1234
The first parameter was Abc
The second parameter was xyz
The third parameter was 1234

Using the shift Statement


Use the shift statement to slide positional parameters.
For example, the following script uses the positional parameters to analyze the
command line arguments passed by the user.
Example
$ cat shifty
print “$1”
shift
print “$1”
shift
print “$1”

$ shifty HELLO THERE


HELLO
THERE

shift Statement Syntax


shift [n] Shift the parameter list n positions to the right
n is a positive integer (default = 1)

Default Parameter Value


Provide a default value for a parameter to be used if the parameter is not set, as
shown below.
$ cat myscript
print ${1:-abc}
print ${2:-mouse}

2–8 EY-G994E-SG-0002
Getting Started with Scripting

Using Parameter Values


In the following example, $1 and $2 are not set.
Example
$ myscript # $1 is not set; $2 is not set
abc
mouse
In the following example, $1 is set, and $2 is not set.
Example
$ myscript xyz # $1 is set; $2 is not set
xyz
mouse
In the following example, $1 and $2 are set.
Example
$ myscript dog cat # $1 is set; $2 is set
dog
cat

EY-G994E-SG-0002 2–9
UNIX Shell Programming Featuring KornShell

Escape Sequences
Table 2-2 lists escape sequences and what they do.

Table 2-2 Escape Sequences


Escape Sequence What It Does
" " Turns off the special significance of all enclosed
characters except $ ` " and \
´ ´ Turns off the special significance of all enclosed
characters
\ Escape the character that comes immediately after the \

Using Wildcards as Literals


Ordinarily, you want the KornShell to apply the special meaning of the wildcards
and the special characters.
For example, you typically want the KornShell to expand the * wildcard to do
filename matching or string matching. However, there are other times when you
simply want the * to be treated as a literal * and not as a wildcard.
Example
$ print * # The KornShell treats the * as a wildcard
apples bananas grapes
$ print ‘*’ # The KornShell treats the * as a literal *
*

How the Escape Sequences Influence Output


The following example shows how the escape sequences influence output.
Example
$ s=”hello” # assign a value to a variable
$ print “$s” # output the value of the variable
hello
$ print ‘$s’ # turns off special significance of $
$s

$ print “I’m doin’ fine”


I’m doin’ fine

$ print “He said, \”Be there.\””


He said, “Be there.”

2 – 10 EY-G994E-SG-0002
Getting Started with Scripting

Using Escape Sequences Properly


Without the pair of single quotes, the following prompt will not work properly.
$ PS1=’$PWD - ’
/usr/users/sam -

EY-G994E-SG-0002 2 – 11
UNIX Shell Programming Featuring KornShell

Passing Arguments to Shell Scripts


Introduction
There are three ways to get arguments into a shell script:
1. Before script executes, using environment variables, as shown in the
following example.
$ export ANIMAL=dog
2. On the script execution command line, using a positional parameter, as
shown in the following example.
$ myscript mouse # ksh will assign mouse to $1
3. While script executes, using the read command, as shown in the following
example.
$ read animal # ksh assigns to animal whatever user types

Environmental Variables
Environment variables are commonly used for values that do not vary from
command to command. For example, LANG is used to represent the language of
the user.

Environmental Variables and Positional Parameters


The following example demonstrates all three methods.
Example
$ cat myscript
print "What animal: \c"
read animal
print environment animal = $ANIMAL
print cmd line animal = $1
print read animal = $animal

$ export ANIMAL=dog
$ myscript mouse
What animal: lion
environment animal = dog
cmd line animal = mouse
read animal = lion

2 – 12 EY-G994E-SG-0002
Getting Started with Scripting

How the read Command Works


! Input to shell scripts is accomplished using the read command.
! One line of characters is read from standard input.
! Successive words of the input are assigned to the variables named on the
read command line (in order).
! Leftover words are assigned to the last variable.
! If there are more variables than words, then some variables will become
unset.
! Sample usage of the read command is shown below.
read var1 var2 var3 ...

Using the read Command


The following example shows use of the read command.
Example
$ cat myscript
read arg1 arg2
print arg1 = $arg1
print arg2 = $arg2

$ myscript
sam tony
arg1 = sam
arg2 = tony

$ myscript
mary betty harry
arg1 = mary
arg2 = betty harry

$ myscript
george
arg1 = george
arg2 =

Including a Prompt
You can build the prompt string into the read statement as follows:
read var?prompt
prompt is written to standard error. Line from standard input is assigned to var.

EY-G994E-SG-0002 2 – 13
UNIX Shell Programming Featuring KornShell

Using read with a Prompt


The following example shows use of the built-in prompt.
Double quotes are used with read so that trailing space is included in the prompt
string.
Example
$ read animal?”Enter your animal choice: “
Enter your animal choice: tiger

2 – 14 EY-G994E-SG-0002
Getting Started with Scripting

Testing Arguments
Techniques for Testing Arguments
The KornShell provides four techniques for testing arguments:
1. [[ ]] — new with KornShell (preferred for string tests)
2. (( )) — new with KornShell (preferred for math tests)
3. test command — Bourne shell and KornShell
4. []— Bourne shell & KornShell

General Syntax
All four techniques test arguments and assign the outcome of that test to the
special shell variable $?. The value of $? will be either:
zero SUCCESS
or
nonzero FAILURE

Using the (( )) Command


Sample usage of the (( )) command is shown below.
$ XX=17
$ (( $XX == 17 ))
$ print $?
0

Using the test Command


Sample usage of the test command is shown below.
$ XX=17
$ test $XX -eq 17
$ print $?
0

Using the [ ] Command


Sample usage of the [] command is shown below.
$ XX=17
$ [ $XX -eq 17 ]
$ print $?
0

EY-G994E-SG-0002 2 – 15
UNIX Shell Programming Featuring KornShell

The [[ ]] Command
Sample usage of the [[ ]] command is shown below.
if [[ “$response” = “Yes” ]]
then
print “Okay, we’re going ahead.”
fi
Note: Spaces around [[ and ]] are required, as shown below.
$ [[“$response” = “Yes” ]]
ksh: [[: not found

$ [[ $XX -eq 17]]


(PS2 - secondary prompt)

2 – 16 EY-G994E-SG-0002
Getting Started with Scripting

Using Operators
Introduction
This section discusses the following operators:
! Integer comparison operators
! String comparison operators
! File enquiry operators
! Logic operators

Integer Comparison Operators


Table 2-3 shows integer comparison operators.

Table 2-3 Integer Comparison Operators


Operator Returns
n1 == n2 Success if integers n1 and n2 are equal
n1 != n2 Success if integers n1 and n2 are not equal
n1 > n2 Success if integer n1 is greater than integer n2
n1 >= n2 Success if integer n1 is greater than or equal to integer n2
n1 < n2 Success if integer n1 is less than integer n2
n1 <= n2 Success if integer n1 is less than or equal to integer n2

Using Integer Comparison Operators


The following example script expects the user to pass at least one command line
argument. If the user does not pass any command line arguments, the script exits.
Example
$ cat myscript
if (( $# == 0 )) # compare two integers
then
print "Usage: $0 filename"
exit 2
fi
...
$ myscript # now run myscript, passing the wrong number of
args.
Usage: myscript filename
$ print $? # what is the exit status returned by myscript?
2

EY-G994E-SG-0002 2 – 17
UNIX Shell Programming Featuring KornShell

String Comparison Operators


The KornShell provides a variety of ways of testing strings, as shown in Table 2-4.

Table 2-4 String Comparison


Operator Returns
-z s1 Success if length of string s1 is zero
-n s1 Success if length of string s1 is non-zero
s1 = s2 Success if strings s1 and s2 are identical
s1 != s2 Success if strings s1 and s2 are not identical
s1 < s2 Success if string s1 comes before string s2 based on their
ASCII values
s1 > s2 Success if string s1 comes after string s2 based on their
ASCII values

Using String Comparison Operators


Use the KornShell [[ ]] operator to test two strings, as shown in the following
example.
Example
$ s=”Hello”
$ [[ “$s” = “Hello” ]]
$ print $?
0

$ [[ “$s” = “Bye” ]]
$ print $?
1
You can compare a string to a pattern containing wildcards, for example:
$ s=”Yes”
$ [[ “$s” = Y* ]] # Does string start with ‘Y’
$ print $?
0

2 – 18 EY-G994E-SG-0002
Getting Started with Scripting

File Enquiry Operators


Table 2-5 lists the most common file tests.

Table 2-5 File Enquiry Operators


Operator Returns
-a file Success if file exists
-r file Success if file exists and is readable
-w file Success if file exists and is writable
-x file Success if file exists and is executable
-f file Success if file exists and is a regular file (as opposed to a
directory)
-d file Success if file exists and is a directory
-s file Success if file exists and has a size greater than zero
file1 -nt file2 Success if file1 is newer than file2
file1 -ot file2 Success if file1 is older than file2
file1 -ef file2 Success if file1 is another name for file2

Using File Enquiry Operators


The KornShell provides a variety of tests to determine a file’s category as shown
in the following example.
Example
$ [[ -f myfile ]] # is myfile a regular file
$ print $? # apparently it is
0

$ [[ -f /etc ]] # is /etc a regular file


$ print $? # no, /etc is not a regular file
1

Testing for Permissions


The KornShell also provides tests to determine whether you have certain
permissions on a specified file, as shown in the example below.
Example
$ [[ -r /users/dan/foo ]] # do I have read permission on foo?
$ print $? # apparently I do
0

$ [[ -w /users/dan/foo ]] # can I modify foo?


$ print $? # I cannot modify foo
1

EY-G994E-SG-0002 2 – 19
UNIX Shell Programming Featuring KornShell

Logic Operators
The following logic operators are shown in decreasing order of precedence.
Operator What It Does
! Unary negation operator
&& Logical AND operator
|| Logical OR operator

Using Logic Operators


The following examples demonstrate typical usage of logical operators.
$ [[ -d $HOME && -d /usr2/$HOME ]]
$ [[ -z $HOME && -z $TERM || -z $LOGNAME ]]

Review
Given the following:
$ A="Oak"; B="Elm"; C=hope; D=hope
what value is returned by each of the following?
1. [[ $A = $C ]]
2. [[ ! -n $D ]]
3. [[ $A != $B && $C = $D ]]
4. [[ $A = $B || $C = $D ]]

2 – 20 EY-G994E-SG-0002
Getting Started with Scripting

Making Decisions and Looping


Introduction
The if and case commands were provided in the earliest Bourne shell. The
while, for, and until statements were written into later versions of the
Bourne shell.

The if/then/fi Commands


Use if/then/fi commands to run tests and then take action depending on the
outcome.

Using the if Command to Test the Status of the [[ Command


In the following examples:
! The if command tests the status return of [[.
! If 0, the then command is executed.
Examples
if [[ -d $1 ]]
then
print "$1 is a directory"
else
print "$1 is not a directory"
fi

if [[ -f $1 ]]
then
print "$1 is a plain file"
elif [[ -d $1 ]]
then
print "$1 is a directory file"
else
print "$1 is neither a plain file nor a directory"
fi

EY-G994E-SG-0002 2 – 21
UNIX Shell Programming Featuring KornShell

Using the if Command to Test the Status of the who | grep


Command
In the following example:
! The if command tests the status return of the who | grep command.
! If 0, the then command is executed.
Example
if who | grep -q ’sam’
then
print "sam is logged on"
fi
grep -q
! Silent grep - no output lines written to stdout; the only result is the status
return.
! grep -s in ULTRIX.

Exact Syntax
Note the need for exact syntax. KornShell is not as forgiving as the C language.
! then requires a line by itself
! elif requires a then, else does not
! fi terminates if

Commands in then or else Blocks


Many shell commands could be in then or else blocks.
Only one command is used in each block of the preceding examples.

Using the while Loop


Use the while loop to execute everything between do and done multiple times,
as shown in the following example.
Example
while [[ -n $1 ]] # loop until there are no more arguments
do
if [[ -r $1 ]]
then
cp $1 $NEWDIR
fi

shift # get next command line argument


done

2 – 22 EY-G994E-SG-0002
Getting Started with Scripting

Loop as long as the command following returns success:


Zero SUCCESS
Nonzero FAILURE

Infinite Loops
The following program loops forever.
while true
do
lines executed in an infinite loop
done
true always returns a 0 (success) value.

The for Loop in KornShell


The for loop of KornShell is somewhat different than the for loop of most other
languages.
In most other languages, the for loop initializes a variable to a numerical value
and then increments or decrements that value with each iteration of the loop. In the
KornShell, the for loop iterates through a collection of strings or filenames.

Using the for Loop


Usage of the for loop in KornShell is shown in the following example.
Example
# The following for loop creates fa.new, fb.new, fc.new and
fd.new:
for FILE in fa fb fc fd
do
cp ${FILE} ${FILE}.new
done

A Special Case
The following is a special case of for that uses positional parameters.
# The following for loop iterates one time for each command
# line argument:
for FILE
do
cp ${FILE} ${FILE}new
done
Note:
for FILE
is equivalent to:
for FILE in $*
EY-G994E-SG-0002 2 – 23
UNIX Shell Programming Featuring KornShell

Using Execute Trace and Verbose Trace to Debug Shell


Scripts
Introduction
The KornShell provides several options that allow you to check your script.
Invoking the shell with the -x option provides an execute trace. Invoking the shell
with the -v option provides a verbose trace.

Execute Trace
Execute trace prints shell commands as they are executed.

Using Execute Trace


The following examples show usage of execute trace.
Examples
$ cat -n save.ksh
1 while [[ -a $1 ]]
2 do
3 cp $1 $1.save
4 shift
5 done

$ ksh -x sav.ksh abc.dat xyz # run in trace mode


+ [[ -a abc.dat ]]
+ cp abc.dat abc.dat.save
+ shift
+ [[ -a xyz ]]
+ cp xyz xyz.save
+ shift
+ [[ -a ]]

$ print $PS4
+
$ export PS4=’at $LINENO - ’

$ ksh -x save.ksh abc.dat xyz


at 1 - [[ -a abc.dat ]]
at 3 - cp abc.dat abc.dat.save
at 4 - shift
at 4 - [[ -a xyz ]]
at 3 - cp xyz xyz.save
at 4 - shift
at 4 - [[ -a ]]

2 – 24 EY-G994E-SG-0002
Getting Started with Scripting

Verbose Trace
Verbose trace prints shell commands as they are read, and is useful for syntax
checking.

Using Verbose Trace


The following examples show usage of verbose trace.
Examples
$ cat –n save.ksh
1 while [[ -a $1 ]]
2 do
3 cp $1 $1.save
4 shift
5 done

$ ksh -v save abc.dat xyz


while [[ -a $1 ]]
do
cp $1 $1.save
shift
done

Using Execute Trace Inside a Shell Script


Instead of running an entire shell script in execute trace mode, you can run a
subset of the script in execute trace mode. This is particularly useful for debugging
a small section of a long script.
An example of using execute trace inside a shell script is shown below.
Example
$ cat save
while [[ -a $1 ]]
do
set -o xtrace # turn on execute trace
cp $1 $1.save
set +o xtrace # turn off execute trace
shift
done
$ save abc.dat xyz
+ cp abc.dat abc.dat.save
+ cp xyz xyz.save

EY-G994E-SG-0002 2 – 25
UNIX Shell Programming Featuring KornShell

Command Substitution
Introduction
Command substitution may be used to assign the output of a command to a
variable.

Procedure
Assign the standard output of command to var, as shown below.
var=$(command)

Assigning Output of a Command to a Variable


The following examples show assigning the output of a command to a variable.
Example
$ foo=$(ls s*) # assign the output of ls s* to variable foo
$ print $foo
s.cnv.c start superx

$ d=$(date) # assign the output of the date command to d


$ print $d
Tue Feb 2 15:00:01 EST 1999

Using the Grave Accent


The KornShell also supports command substitution using the grave accent as
demonstrated in the following example; however, this usage is considered to be
old-fashioned.
Example
$ d=‘date‘
print $d
Tue Feb 2 15:01:37 EST 1999

2 – 26 EY-G994E-SG-0002
Getting Started with Scripting

Shell Script Examples


Example 1
This example shows a shell script to determine the number of users logged on. If
that number exceeds the number specified as a command line argument, an
appropriate message is written to your terminal and the shell process and its parent
process are killed.
#!/usr/bin/ksh
#usercheck

if (( $# != 1 ))
then
print "Usage: $0 number" >&2
exit 1
fi
number_logged_in=$(who | wc -l)
if (( $number_logged_in > $1 ))
then
print "Too many users logged on"
kill -9 $PPID
fi

EY-G994E-SG-0002 2 – 27
UNIX Shell Programming Featuring KornShell

Example 2
This example shows a shell script which takes one filename as a command line
argument and determines how many lines are in that file. If the file is longer than
two pages, the shell script asks you if you wish to print it after hours. If you
respond affirmatively, the shell script exits; otherwise, the file is printed. If the
command line argument is a directory or is not readable, an appropriate error
message is printed.
#!/usr/bin/ksh
#printlater

if [[ $# -ne 1 ]] ; then
print "Usage: $0 filename" >&2
exit 1
fi
if [[ -d $1 || ! -r $1 ]]
then
print "$1 cannot be printed." >&2
exit 2
fi
let "len=$(wc -l $1)/60+1"
if [[ $len -gt 2 ]]
then
read ans?"Print after hours?: "
if [[ $ans = y* ]] ; then
exit
fi
fi

lpr $1

2 – 28 EY-G994E-SG-0002
Getting Started with Scripting

Summary
Defining and Using Processes
A process is a kernel environment in which a user program executes. Each
program runs in a process, so the process acts as a program wrapper. Use
fork() to create a new process running in the same program and exec() to
replace a program in a process with another.

Executing a Shell Script


A KornShell script can be executed either as an argument to ksh or by name. The
user invoking the shell script must have read permission on the shell script file. C
shell scripts can be invoked from the KornShell command line.

Using Positional Parameters


Positional parameters allow you to pick out arguments from the command line.

Using Escape Sequences


Escape sequences allow you to use special characters and wildcards as literal
characters, such as quotation or question marks.

Passing Arguments to Shell Scripts


There are three ways to pass arguments to a shell script: use environmental
variables, use a positional parameter, or use the read command.

Testing Arguments
Arguments are tested using four techniques described in this chapter. All four
techniques put the outcome of the test into the variable $?.

Using Operators
This chapter discussed integer, string, file enquiry, and logic operators.

Using Conditions, Control Statements and Loops


Conditions, control statements and loops can be used to make decisions. This
chapter discussed using the if command, the while command, and the for
loop.

Debugging Scripts Using Execute Trace and Verbose Trace


You can use execute trace (-x) or verbose trace (-v) options to help debug a
script.

EY-G994E-SG-0002 2 – 29
UNIX Shell Programming Featuring KornShell

Using Command Substitution


Command substitution can be used to assign the output of a command into a
variable.

2 – 30 EY-G994E-SG-0002
Getting Started with Scripting

Exercises
Instructions
1. Write a shell script that checks for a minimum of two arguments. Print an
error message to the standard error file if the user types less than two
arguments; otherwise, print all the arguments to the standard output file.
2. Write a shell script that will accept a single file name on the command line. If
the file exists, the script will ask the user whether a backup copy of the file
should be made. A backup copy with a suffix of .backup is created, if
requested, and the vi editor is invoked for the file.
3. Write a shell script that will prompt for and read from the standard input file
a first name, a last name, and a telephone extension. Write these values to a
file named ./phonelist and loop until a first name of "quit" is typed.
4. The shell script dirtest works incorrectly. Make a copy in your directory
and execute it with several pathnames as command line arguments, for
example:
$ dirtest /etc /etc/motd /mnt
Use shell debugging techniques to find and correct the problems. A listing of
dirtest follows:
#!/bin/ksh
#dirtest - shell script with bugs
for i
if (-d i)
print "$i is a directory"
else
print "$1 is not a directory"
endif
end
5. Write a shell script that will accept an option (-r or -w) and a file name
argument on the command line. Have the script print an appropriate message
to the standard output file indicating that the file does not exist, is readable,
or is writeable. If an option is not typed on the command line, assume -r.
6. Write a shell script that will make a copy with a suffix of .save of any file
specified on the command line, for example abc to abc.save. Assume that
users might specify multiple file names on the command line. If no file
names are typed on the command line, make the .save copies for all files in
the current working directory.
7. Use the ps(1), cut(1), and sort(1) commands to print a list of process ids
currently in use. Print the list of PIDs in increasing numerical order.

EY-G994E-SG-0002 2 – 31
UNIX Shell Programming Featuring KornShell

2 – 32 EY-G994E-SG-0002
Intermediate Shell Scripting
Module 3

EY-G994E-SG-0002 3–1
UNIX Shell Programming Featuring KornShell

3–2 EY-G994E-SG-0002
Intermediate Shell Scripting

About This Chapter


Introduction
This chapter introduces more shell control features such as grouping shell
commands, more advanced looping and branching statements, and functions. It
compares different techniques for executing shell scripts and introduces aliases.

Objectives
When you complete this module, you will be able to:
! Execute a script as a sibling
! Group shell commands
! Use control statements
! Enhance loop processing
! Create and use aliases
! Create and use functions
! Use the ENV variable
! Recall and use directory information in scripts

Resources
For more information on the topics in this module, refer to the following:
! Tru64 UNIX Reference Pages
! Command and Shell User’s Guide
! Kornshell Programming Tutorial, Barry Rosenberg, Reading, MA: Addison-
Wesley Publishing Co., 1991 (ISBN 0-201-56324-X)
! The Kornshell Command and Programming Language, Morris I. Bolsky and
David G. Korn, Englewood Cliffs, NJ: Prentice Hall, 1989 (ISBN 0-13-
516972-0)

EY-G994E-SG-0002 3–3
UNIX Shell Programming Featuring KornShell

Executing a Script as a Sibling


Normal Execution of a Shell Script
The diagram below shows normal execution of a shell script.

login child
ls
ksh ksh

Parent and Child Scripts


When you run a shell script from the KornShell command line, the shell script is a
“child” and the KornShell command line is the “parent.” A child cannot change
the parent’s environment.
The following example shows how a child cannot change the parent’s working
directory.
Example
$ pwd # what is the current directory?
/usr/users/sam

$ cat show_cron # here is a simple shell script


cd /var/spool/cron/crontabs
ls

$ show_cron # run the shell script


adm cronuucp milt
root sys uucp

$ pwd # why did the directory not change?


/usr/users/sam

Running a Sibling
Instead of running a KornShell script as a child, you can run it as a sibling.
Although the second script is termed “sibling,” the original script remains the
parent. By running as a sibling, the script can change the parent’s environment.
To run as a sibling, precede the KornShell script with a . (dot) and then a space.
The dot command serves the same purpose in KornShell as the source command
does in the C shell. The dot command is used for shell scripts only.

3–4 EY-G994E-SG-0002
Intermediate Shell Scripting

Using the Dot Command


The following example runs show_cron as a sibling.
Example
$ . show_cron # run the script as a dot command
adm cronuucp milt
root sys uucp

$ pwd # now the directory changed!


/var/spool/cron/crontabs

Class Discussion
Consider: $ .profile

The exec Command


The exec command replaces the parent shell with any shell command, as shown
in the following example. The exec command is seldom used.
Example
$ exec show_cron
adm cronuucp milt
root sys uucp

login:
This process can be viewed as:
login
ksh

becomes

ksh ls

(cd)

EY-G994E-SG-0002 3–5
UNIX Shell Programming Featuring KornShell

The following example replaces the parent shell with ls.


Example
$ exec ls
eval labs mtpt opt1 opt2 opt3 opt4
redir redir2
login:

login
ksh

becomes

ls

3–6 EY-G994E-SG-0002
Intermediate Shell Scripting

Grouping Shell Commands


Introduction
You can group shell commands on the same line using the following:
! Semicolon (;)
! Parenthesis (())
! Braces ({}) (or curly braces)

Using the Semicolon


Use the semicolon to join several shell commands on the same line. Each
command executes as if typed on separate lines.
The following example shows how to group two commands with a semicolon.
Example
$ cd /etc ; ls m*
mail.aliases miscd mkfs
mklost+found mknod mkpasswd
mkproto motd mount
$ pwd
/etc
This can be viewed as:
login
ls
ksh

(cd )

Using Parentheses
Parentheses force the group to execute in a child shell.
The following example shows how to use parenthesis.
Example
$ pwd # where are we?
/usr/users/sam

$ (cd /etc ; ls m*) # run in a child shell


mail.aliases miscd mkfs
mklost+found mknod mkpasswd
mkproto motd mount

$ pwd # child shell cannot influence its parent.


/usr/users/sam

EY-G994E-SG-0002 3–7
UNIX Shell Programming Featuring KornShell

This can be viewed as:


login child
ls
ksh ksh

(cd )

Using Braces
Braces cause the group to execute in the parent shell, but allow redirection of
multiple commands.
$ { cd /etc ; pwd ; ls m* ; } > ~/list
Note the punctuation: Spaces before and after the braces are essential; the trailing
semicolon is also essential.
The following example shows how to use braces.
Example
$ cat ~/list
/etc
mail.aliases miscd mkfs
mklost+found mknod mkpasswd
mkproto motd mount

$ pwd
/etc
This can be viewed as:
login
ls
ksh

(cd,pwd)

3–8 EY-G994E-SG-0002
Intermediate Shell Scripting

Control Statements
Command Execution Dependent on Previous Command (&&, | |)
Using the boolean AND operator (&&) tells the KornShell to execute the command
on the right only if the command on the left succeeds. Using the boolean OR
operator (||) tells the KornShell to execute the command on the right only if the
command on the left fails.

The && Operator


The following example shows how to use the && operator.
Example
cmd1 && cmd2 execute cmd2 only if cmd1 succeeds.
$ who | grep -q sam && print "sam is logged on"

The | | Operator
The following example shows how to use the || operator.
Example
cmd1 || cmd2 execute cmd2 only if cmd1 fails.
$ who | grep -q sam || print "sam NOT logged on"

The case Statement


The case statement associates any number of statements with each possible value.
! At most, one action is taken.
! If no patterns match, no action is taken (fall through).
! Any shell command(s) or shell scripts can be executed in each action.
! A delimiter (;;) is required to terminate each case. You can put the ;;
delimiter on a separate line or at the end of the last command for this action.
! Patterns may employ KornShell wildcards, such as * ? [].
! Many case statements list the * as the last pattern; the * will match
anything that has not yet been matched.

EY-G994E-SG-0002 3–9
UNIX Shell Programming Featuring KornShell

Using the case Statement


The following code example shows typical usage of case.
Example
case $1 in
[0-9]) print "is a single digit"
;;
dog|cat) print "is name of house pet"
;;
lion) print "is king of the jungle"
;;
[a-z]*) print "begins with lower case"
;;
*) print "none of the above"
;;
esac

The until Statement


The until statement generates a negative iteration loop. A negative iteration
loop counts backwards. The until loop is the reverse of a while loop. The
until loop continues looping while a specified condition is false.

Using the until Statement


The following example shows how to use until in a negative iteration loop.
Example
until [[ $1 = "harry" ]]
do
print "Where is harry? - $1"
shift
done

$ myscript sam mary harry tom


Where is harry? - sam
Where is harry? - mary
Loops as long as the command returns failure status.

3 – 10 EY-G994E-SG-0002
Intermediate Shell Scripting

In the following example, the grep command returns a failure status if the search
string is not in the input file.
Example
until grep -s Results $1
do
print "No Results found in $1"
shift
done
$ myscript abc.txt data.file nov.dat
No Results found in abc.txt

Syntax for break and continue


! The syntax for break is:
break [n]
Terminates execution of n (default=1) innermost enclosing loops (for,
while, until, select).
! The syntax for continue is:
continue [n]
Causes execution to resume at the beginning of the n (default=1) nearest
loop (for, while, until, select).

Using break and continue


The break statement provides a simple way to terminate input. In the following
example, the continue statement tosses out illegal input values.
Example
while true
do
print “Enter an input value (or 0 to quit): “
read input_value
if (( $input_value == 0 ))
then
break # go to the (ps) command after done
elif (( $input_value > 100 ))
then
continue # go to the top of loop
else
(( running_total = running_total + input_value ))
fi

done
print “$running_total”

EY-G994E-SG-0002 3 – 11
UNIX Shell Programming Featuring KornShell

Advanced Loop Processing


Redirecting Input/Output in while Loops
KornShell programmers frequently use the statement combination while read.
This phrase tells the KornShell to loop until the end of input is reached. In such
loops, KornShell programmers often redirect standard input and standard output.
You can place the redirection operators just after the done keyword.

Using the while read Statement


The following is an example of using the while read statement combination
below.
Example
$ cat myscript
while read line
do
print $line
done < file1 > file2

print “That is all.”


By redirecting standard input and standard output on the done line, the KornShell
will gather all input for the while read statement from file1 and will send
all output within the body of the loop to file2.
$ cat file1 # here is the input file
This is the first line of file1.
This is the second line of file1.
This is the third line of file1.

$ myscript # now we will run the script


That’s all. # notice that this line does not go into file2.

$ cat file2 # here is the output file


This is the first line of file1.
This is the second line of file1.
This is the third line of file1.

3 – 12 EY-G994E-SG-0002
Intermediate Shell Scripting

KornShell Aliases
Alias Definition
An alias is a synonym or nickname for a command or shell script. Aliases take no
arguments.
Use aliases to save some typing, to give cryptic UNIX names a name that is easily
remembered, or to indicate a warning.

Preset Aliases
Preset aliases are defined by the ksh program. The user takes no action to get
preset aliases.

Defined Aliases
Defined aliases are commonly defined in .profile.

Tracked Aliases
Tracked aliases are defined for common shell commands the first time that shell
command is used. The KornShell can find a tracked alias faster than other
commands.

EY-G994E-SG-0002 3 – 13
UNIX Shell Programming Featuring KornShell

Using Aliases in a Script


An example of using aliases is shown below.
Example
$ alias delete=rm
$ alias lst=’ls -l’
$ alias cprog=’cd /class/prog ; pwd’
$ alias psm=’ps ax | more’

$ delete myfile
$ lst savefile
-rwxr-xr-x 1 sam staff 580 Jul 5 11:38 savefile

$ alias lst
lst = ls -l

$ alias

autoload=typeset -fu Preset


Aliases
cat=/bin/cat
chmod=/bin/chmod Tracked
cp=/bin/cp Alias
cprog=cd /class/prog ; pwd Defined
delete=rm Alias
false=let 0
functions=typeset -f
history=fc -l
integer=typeset -i
ls=/bin/ls
lst=ls -l
psm=ps ax | more
r=fc -e
true=:
type=whence -v

Using $ set -o trackall


By using $ set -o trackall, every command becomes a tracked alias when
first encountered.

3 – 14 EY-G994E-SG-0002
Intermediate Shell Scripting

Exporting Aliases
To export an alias, use the –x option to the alias statement, as shown below.
Note that you cannot export an alias to a script that is invoked as an argument to
ksh.
Example
$ alias -x delete=’rm –i’ # export the delete alias

$ cat myscript # invoke the exported alias


delete hope

$ myscript foo # run script by name; alias works


rm: remove foo (y/n)? n

$ ksh myscript # run script as arg to ksh; alias


does not work delete alias not found

EY-G994E-SG-0002 3 – 15
UNIX Shell Programming Featuring KornShell

KornShell Functions
Function Definition
In the KornShell, a function is a named block of code. Like functions in other
languages, you can create KornShell functions that accept arguments and return
values. You can define a function inside a startup file, inside a large program, or
even as the sole contents of a file.
Many programmers place their KornShell functions in the .profile startup file,
as shown below.
Example
$ cat ui.fn
# A function that invokes the UIL compiler (part of Motif)
function ui
{
uil -o $1.uid $1.uil # invoke the UIL compiler
}
If you do not place a function in a startup file, you can use the dot command to
make that function available, as shown below.
$ . ui.fn # make the function accessible from the ksh cmd. line

Invoking a Function
A function is like a shell script built into the ksh process. Invoke a function as
you would invoke a script, for example:
$ ui abc # invoke the ui function, passing one argument
The preceding function invocation UIL compiled abc.uil to produce
abc.uid.
Notice that just like invoking a script, you can pass command line arguments to a
function. The command line arguments for scripts ($1, $2, $#, $*, etc.) can also
appear inside functions.

3 – 16 EY-G994E-SG-0002
Intermediate Shell Scripting

Debugging Functions
You can use the typeset –ft command to force an execution trace of a
function. An example is shown below.
Example
$ cat full.fn
#full- function to print the full pathname for the
#file given as an argument
function full
{
if [[ $1 != /* ]]
then
PathName=$PWD/$1
else
PathName=$1
fi
print $PathName
}

$ . full.fn # define the function


$ typeset -ft full # trace the function

$ full abc
+ [[ abc != /* ]]
+ PathName=/usr/users/sam/abc
+ print /usr/users/sam/abc
/usr/users/sam/abc
To debug a function, you can also embed the set –o xtrace and set +o
xtrace pairs inside the function.

Exporting Functions
To export a function, use the –fx option with the typeset statement, as shown
below. Even if you export the function, scripts invoked as an argument to ksh will
not be able to access the function.
$ typeset –fx ui # export the ui function defined earlier

EY-G994E-SG-0002 3 – 17
UNIX Shell Programming Featuring KornShell

The ENV Environment Variable


Introduction
In ~/.profile or /etc/profile, you can specify a pathname for the ENV
environment variable. The most common pathname to assign to ENV is
~/.kshrc, so the definition would typically be:
export ENV=~/.kshrc

Default Execution
If you do make the preceding assignment, then the KornShell will execute the
following three scripts at login:
! /etc/profile
! ~/.profile
! ~/.kshrc

Execution on Creating a New Kornshell


After login, whenever you create a new KornShell, the KornShell will execute the
following two scripts:
! ~/.profile
! ~/.kshrc

Executing a Shell Script as an Argument to ksh


Finally, when you execute a shell script as an argument to ksh, the KornShell
invokes ~/.kshrc prior to running your shell script.

3 – 18 EY-G994E-SG-0002
Intermediate Shell Scripting

Summary of Differences between $ myscript and $ ksh myscript


In ~/.kshrc:
! The alias was exported from login ksh.
! ~/.kshrc does NOT execute.
Example
alias -x delete=rm

$ alias delete
delete=rm # defined from ~/.kshrc

$ alias -x delete=’rm -i’

$ cat myscript
alias delete # show value of exported alias

$ myscript
delete=rm -i

Executing ~/.kshrc
~/.kshrc executes in child ksh before myscript.
! Dual script environment exists for aliases and functions.
! Shell scripts that execute by name get exported aliases and functions defined
in login ksh.
! Shell scripts that execute as an argument to ksh get exported aliases and
functions defined in the $ENV file.
Example
$ ksh myscript
delete=rm

Exception for Tru64 UNIX and Some Other Implementations


A shell script that has:
#!/usr/bin/ksh
as its first line behaves as if it had been executed as an argument to ksh when
executed by name.

Autoloading Functions
Autoloading functions are an alternative to defining functions in ~/.profile or
the $ENV file. You can reduce startup time by delaying function definition until
needed.

EY-G994E-SG-0002 3 – 19
UNIX Shell Programming Featuring KornShell

Delaying Function Definition


To delay function definition, do the following:
1. Place each function definition in a separate file. Give this file the same name
as the function. For example, if you create a function named full, you must
put that function in a file named full.
2. Copy all function definition files into one directory, for example, the
~/functions directory.
3. Define the $FPATH variable to point to that directory, for example, in
~/.profile:
export FPATH=~/functions
4. Before calling one of these functions (perhaps from a script), you must warn
the KornShell that the function is in the special autoload directory. To do
so, specify the autoload directive, for example:
autoload full
autoload ui

3 – 20 EY-G994E-SG-0002
Intermediate Shell Scripting

Using Directory Information


Introduction
This section defines three functions intended to be used together:
ma abc
! Remembers the current directory with a line in a disk file
abc:current_directory.
! If no argument is specified, the ma function uses the last segment of the
current directory pathname as the token.
ga abc
! Finds the abc line in the disk file and sets the current directory to the last
field of that line.
! If no argument is specified, the ga function prints an error message.
la abc
! Lists the abc line from the disk file.
! If no argument is specified, the la function lists all lines from the disk file.
basename returns the last segment of pathname.

EY-G994E-SG-0002 3 – 21
UNIX Shell Programming Featuring KornShell

Example Using the ma Function


The following shell script example defines functions ma, ga and la that will
remember the current directory and enable returning to that directory at a later
time. A disk file is used to remember the directory. ma marks the current directory
with the "word" given as argument. If no word is given on the command line, the
basename is used for the current directory as the word. Write a line into a disk
file in the home directory.
Example
# magala.fn

function ma
{
DirFile=$HOME/.dir.data
Usage="Usage: ma [word]"
case $# in
0) print $(basename $PWD):$PWD >> $DirFile
;;
1) print $1:$PWD >> $DirFile
;;
*) print "Too many arguments." >&2
print $Usage >&2
;;
esac
}
The next example writes into DirFile myetc:/etc
$ cd /etc
$ ma myetc
The next example writes into DirFile sys:/usr/sys
$ cd /usr/sys
$ ma

3 – 22 EY-G994E-SG-0002
Intermediate Shell Scripting

Example Using the ga Function


The following shell script example goes to the directory associated with the
"word" given as an argument. It reads the directory name from the disk file in the
home directory.
Example
# ga

function ga
{
DirFile=$HOME/.dir.data
Usage="Usage: ga word"

case $# in
1) d=$(grep "^$1:" $DirFile | awk -F: ’{print $2}’)
# Look for word in DirFile and set d = 2nd field
if [[ -n $d ]]
then
cd $d
print "New current directory is $(pwd)"
else
print "No entry for \"$1\" in $DirFile"
fi
;;
*) print "Too many arguments." >&2
print $Usage >&2
;;
esac
}
As shown below, the new current directory is /etc.
$ ga myetc
As shown below, the new current directory is /usr/sys.
$ ga sys

EY-G994E-SG-0002 3 – 23
UNIX Shell Programming Featuring KornShell

Notes on Using the ga Function


grep "^$1:" $DirFile writes to stdout, the line of the file that begins
with (^), the first positional parameter value ($1) followed by a colon. There
should be only one such line in the file. This function has problems if there are
multiple lines that have the same token. The repair is left as an exercise. Why are
double quotes used? To cause ksh to substitute the value of the first positional
parameter. The double quotes could be removed without harming functionality.
awk -F: ’{print $2}’ reads lines from stdin and defines a field
separator of : Prints field 2 for that line. There should be only one line of input.
See above.
Single quotes are used to prevent ksh from seeing $2 as the second positional
parameter.
d= field 2 of the match line in the file (the directory full pathname).

3 – 24 EY-G994E-SG-0002
Intermediate Shell Scripting

Example Using the la Function


The script example below lists the words and their values from the disk file in the
home directory.
Example
# la

function la
{
DirFile=$HOME/.dir.data
Usage="Usage: la word"

case $# in
0) cat $DirFile
;;
1) e=$(grep $1 $DirFile)
if [[ -n $e ]] ; then
print $e
else
print "No entry for \"$1\" in DirFile"
fi
;;
*) print $Usage >&2
;;
esac
}

$ la sys
sys:/usr/sys

$ la
myetc:/etc
sys:/usr/sys

grep $1 $DirFile
Writes to stdout the line of the file that contains the value of the first positional
parameter. There should be only one such line in the file. This function has
problems if there are multiple lines that have the same token. The repair is left as
an exercise.

EY-G994E-SG-0002 3 – 25
UNIX Shell Programming Featuring KornShell

Another Example
This script prints the name and login shell of every user on the system.
Example
# !/usr/bin/ksh

USAGE=”usage: stats.ksh”

IFS=’:’

while read username pw userid groupid realname logindir


shell
do
print “$username $shell”
done < /etc/passwd

Running This Script


$ stats.ksh
root /usr/bin/ksh
danny /usr/bin/ksh
rachel /usr/bin/csh
mar /usr/bin/sh

3 – 26 EY-G994E-SG-0002
Intermediate Shell Scripting

Summary
Executing a Script as a Sibling
By running a script as a sibling instead of a child, you can modify the parent’s
environment. Sibling scripts are run by invoking the dot command (.). The exec
command, although seldom used, replaces the parent shell with any other shell
command.

Grouping Shell Commands


Shell commands can be grouped on the same line by using a semicolon,
parenthesis, or braces.

Using Control Statements


Control statements in the command line tell KornShell whether or not to execute
the command to the right of the statement. Use until in negative iteration loops
and break and continue to control the loops behavior.

Enhancing Loop Processing


Use the while read statement combination to loop until the end of the input is
reached.

Creating and Using Aliases


An alias is a synonym or nickname for a command or shell script. Tracked aliases
are defined for common shell commands the first time that the shell is used. Use
the –x option with the alias command to export an alias.

Creating and Using Functions


A function is a named block of code. Functions can accept arguments and return
values. Functions are invoked in the same way as scripts. Export functions by
using the –fx option to the typeset statement.

Using the ENV Variable


The ENV variable allows you to set the pathname for the environment.

Using Directory Information


Use the ma, ga, and la functions to provide directory information to the script.

EY-G994E-SG-0002 3 – 27
UNIX Shell Programming Featuring KornShell

Exercises
Instructions
1. Create a function to change the permissions of a file to 600, invoke vi to edit
the file, and change the file permissions to 400.
2. Create a function to display the line(s) of the ps ax output that contain the
string specified as an argument, for example:
$ func inetd
should print:
143 ? I 0:00 /etc/inetd
Ensure that any ps lines created by the function execution are not printed for
example lines containing grep inetd.
3. Define the function llf and alias ll so that after logging out and logging in
again, they will be defined differently for shell scripts that are executed by
name or as an argument to ksh.
For scripts executed by name:
llf = ’ls -l $1’
ll = ’ls -l’
For scripts executed as an argument to ksh:
llf = ’ls -F $1’
ll = ’ls -F’
Test with a shell script containing the lines:
ll $1
llf $1
What is the effect if the following line is inserted as the first line of the shell
script?
#!/bin/ksh
4. Write a shell script using case which takes one or two command line
arguments. If two arguments are typed on the command line, the first is a file
name and the second is a directory name. Use find(1) to search for a file of
that name in the specified directory. If only one argument is typed on the
command line, it is a file name to be sought in the current working directory
tree.
5. Write a shell script to compile a source file whose name (without the .c) is
given on the command line. Additional command line arguments must begin
with a dash (-) and represent options for the compile command.
Note that options could occur before or after the file name, for example:
cc -g -o abc abc.c -lm

3 – 28 EY-G994E-SG-0002
Advanced Shell Scripting
Module 4

EY-G994E-SG-0002 4–1
UNIX Shell Programming Featuring KornShell

4–2 EY-G994E-SG-0002
Advanced Shell Scripting

About This Chapter


Introduction
This chapter provides more advanced scripting techniques. It shows how to create
a menu, handle signals in a shell script, perform integer and floating-point
arithmetic, and use variable arrays. It reviews the order of command evaluation
and shows some common programming errors. It demonstrates how to use some
commands and utilities in your scripts.

Objectives
When you complete this module, you will be able to:
! Create and use menu processors
! Identify and use signals
! Handle signals in a shell script
! Use integer arithmetic
! Use floating-point arithmetic
! Create and use variable arrays
! Change the internal field separator
! Perform command evaluation using eval
! Use tools for shell scripts
! Set positional parameters

Resources
For more information on the topics in this module, refer to the following:
! Tru64 UNIX Reference Pages
! Command and Shell User’s Guide
! Kornshell Programming Tutorial, Barry Rosenberg, Reading, MA: Addison-
Wesley Publishing Co., 1991 (ISBN 0-201-56324-X)
! The Kornshell Command and Programming Language, Morris I. Bolsky and
David G. Korn, Englewood Cliffs, NJ: Prentice Hall, 1989 (ISBN 0-13-
516972-0)

EY-G994E-SG-0002 4–3
UNIX Shell Programming Featuring KornShell

Menu Processing
List Processing (select)
The following example uses the select command to display a menu to the user.
! A shell script fragment is shown at the top of the example. The execution of
that fragment is shown in the middle.
! For the first response, i=fries.
! Note that select is like an infinite loop. A break or exit is needed to
get out of the loop.
! Default value for $PS3 is #?.
Example
$ cat menuscript
PS3="Please enter the number for a food: "
select i in candy carrot fries spinach
do
case $i in
candy|fries) print "Poor choice"
;;
carrot|spinach) print "Good choice"
break
;;
*) print "invalid number"
;;
esac
done

$ menuscript
1) candy
2) carrot
3) fries
4) spinach
Please enter the number for a food: 3
Poor choice
Please enter the number for a food: 2
Good choice

4–4 EY-G994E-SG-0002
Advanced Shell Scripting

User Prompts
In the previous example:
! <RETURN> will redisplay the menu
! 5 (or any number not in the menu) would cause the variable i to be assigned
the value ""
Although the use of menus is a bit old fashioned, user prompts are useful when
menus will be used to indicate user preference. However, a graphical user interface
such as Motif is preferable.

Prompt Reminder
The following table shows the how to set the user prompts.

Variable Prompt Description


PS1 $ Primary shell prompt
PS2 > Secondary shell prompt
PS3 #? Select prompt
PS4 + Execute trace marker

EY-G994E-SG-0002 4–5
UNIX Shell Programming Featuring KornShell

Signals
Introduction
Signals are sent to a process to inform it that an event has occurred. A process can
handle or ignore a signal. The default action for most signals is process
termination. A signal can only be sent to a process with the same UID as the
sender, although superuser can send a signal to any process.

Assigned Signals in Tru64 UNIX


Table 4-1 is a list of assigned signals in Tru64 UNIX.

Table 4-1 Assigned Signals in Tru64 UNIX


Number Signal
1 HUP
2 INT
3 QUIT
4 ILL
5 TRAP
6 IOT
7 EMT
8 FPE
9 KILL
10 BUS
11 SEGV
12 SYS
13 PIPE
14 ALRM
15 TERM
16 URG
17 STOP
18 TSTP
19 CONT
20 CHLD
21 TTIN
22 TTOU
23 IO
24 XCPU
25 XFSZ
26 VTALRM
27 PROF

4–6 EY-G994E-SG-0002
Tips and Tricks

Using setuid
Example
$ ls -l /usr/ucb/passwd
-rws--x--x 3 root bin 16384 Aug 8 19:49 /usr/ucb/passwd

$ passwd
Old password:
New password:
Verify:
This can be represented by:

login
passwd
ksh

uid =mary uid =root

Using sush
Example
/* sush.c program to run a KornShell script
whose name and arguments are given on
the cmd line. */
void
main(int argc, char *argv[])
{
strcpy(argv[0], "ksh");
if (execvp("ksh", argv) == -1) {
perror("sush error");
exit(1);
}
exit(0);
}
The following commands must be typed as superuser:
# cc -o sush sush.c
# chmod 4750 sush
# ls -l sush
-rwsr-x--- 1 root system 27916 Jan 4 10:55 sush

EY-G994E-SG-0002 5 – 17
UNIX Shell Programming Featuring KornShell

The following commands are typed as ‘mary ’:


$ cat myscript
id
if [[ -w /etc/passwd ]] ; then
print "have write access to /etc/passwd"
fi
print $# positional parameters - $*

$ sush myscript abc def


uid=910(gar) gid=2(ins)euid=0(root)
have write access to /etc/passwd
2 positional parameters - abc def

5 – 18 EY-G994E-SG-0002
Tips and Tricks

KornShell Command Processing


KornShell Command Line Parsing
The KornShell processes the command line in the following sequence:
1. The command is split into tokens and these are organized in three categories:
a. Variable assignment words
b. Command words
c. I/O redirections
2. Command words are tested and alias substitution is performed.
3. Tilde expansion is performed.
4. Command substitution is done.
5. Parameter expansion is performed.
6. Results of command substitution are split into words.
7. Pathnames are expanded in command words and I/O redirections.
8. Quote characters are removed.

Command Word Precedence


Command word precedence is as follows:
1. Alias
2. Function
3. Built-in
4. Program

EY-G994E-SG-0002 5 – 19
UNIX Shell Programming Featuring KornShell

Exercise/Discussion
Consider the following.
Examples
$ cd ~$USER # ’~’ expanded before variable
~mary: No such file or directory
$ eval cd ~$USER # eval forces second pass

$ ls ~$(whoami) # ’~’ expanded before cmd subst.


~mary: No such file or directory
$ eval cd ~$(whoami) # eval forces second pass
In the first example, USER is substituted at step 5, but the tilde expansion at step
3 has already occurred.
In the second example, the command substitution is performed at step 4 but the
tilde expansion at step 3 has already occurred.
By placing eval prior to these commands, ksh makes a second pass over the
command. Parameter (or command) substitution occurs in the first pass and tilde
expansion in the second pass. The commands work if eval precedes them.

5 – 20 EY-G994E-SG-0002
Tips and Tricks

Co–processes
Definition
A co-process is a background job that is started in such a way that you can
communicate with it using read and print commands.

Running a Script as a Co-process


To run a script as a co-process, append the |& operator to the end of the command
line, as shown in the following example.
Example
$ cat co-script
# co-script
read file
for dir in ~/save /usr/local/save /public/save
do
cp $file $dir
done
print "$file: Saved successfully"

$ co-script |& # run the script as a co-process


$ print -p savefile # send data to the co-process
$ read -p # gather data from the co-process
$ print $REPLY # gathered data was stored in REPLY, so output it
savefile: Saved successfully
! program |& starts the co-process.
! The co-process’ stdin and stdout are connected to file descriptor p of
the parent shell.
! The parent shell uses read -p and print -p commands to exchange
information with the co-process.

EY-G994E-SG-0002 5 – 21
UNIX Shell Programming Featuring KornShell

Co-process Diagram
The process can be represented as shown in Figure 5-1.
Figure 5-1 Co-process Diagram

Parent Shell Co-process

print -p stdin

read -p
stout

5 – 22 EY-G994E-SG-0002
Tips and Tricks

Using Multiple Co-processes


To access more than one co-process, assign another file descriptor to p, as shown
in the following examples.
Examples
$ cat program1
# program1
while true
do
read text
print "$text - this is from program1"
done

$ cat program2
# program2
while true
do
read text
print "$text - this is from program2"
done

$ program1 |&
$ exec 3<&p
$ exec 4>&p

$ program2 |&
$ exec 5<&p
$ exec 6>&p

$ print -u4 hello # write to program1


$ read -u3 repl1 # read from program1
$ print $repl1
hello - this is from program1

$ print -u6 howdy # write to program2


$ read -u5 repl2 # read from program2
$ print $repl2
howdy - this is from program2
! File descriptor 3 reads from program1
! File descriptor 4 writes to program1
! File descriptor 5 reads from program2
! File descriptor 6 writes to program2

EY-G994E-SG-0002 5 – 23
UNIX Shell Programming Featuring KornShell

Using Co-processes
The following example preserves the co-process started in .profile and saves
the files in ~/trashcan. The original is deleted if the save is successful. Next,
it prints a success message or an error message to stdout. On exit, it archives all
files in ~/trashcan and deletes the saved versions. The signal handler archives
files, compress the archive, and deletes saved versions.
Example
# preserve
tf=obsolete.$(date +%d%m%y).$$ # name for archive
trap ’cd ~/trashcan
tar -cf ~/$tf * ;\
compress ~/$tf ;\
rm * ;\
exit’ TERM HUP INT STOP QUIT
while true # save the files and delete originals.
do
read file
if cp $file ~/trashcan 2>&1 # errors to stdout
then
rm $file
print "$file saved" # msg to stdout
fi
done

# save.fn - Function which writes full pathname of


# files passed as command line arguments to the
# preserve co-process. It reads a message from the
# co-process and prints it to stdout.
function save
{
for fil
do
print -p $([[ $fil != /* ]] && print $PWD/)$fil
read -p stat
print $stat
done
}

Starting preserve shell From .profile


In the previous example, the preserve shell script is started from .profile at
login with:
~/preserve |&
tf = obsolete.120295.3722 on Feb 12, 1995, from pid=3722
When logout occurs, this co-process that was started from .profile at login
will be sent the HUP signal. The signal handler executes to save the trashed files
and to cleanup the trashcan directory.
5 – 24 EY-G994E-SG-0002
Tips and Tricks

Single Quotes
Single quotes are essential in the signal handler to avoid expansion of the * wild
card as the signal handler is established with the trap command.

The if Command
The if statement checks the status return from the cp command. The file is
removed only if cp is successful. Errors from the cp command are written to
stdout, which is the channel back to the calling co-process.

The print Command


The print command writes a message to stdout, which is the channel back to
the calling co-process. The first print command writes the full pathname of the
argument to the function to the co-process as shown in Table 5-4.

Table 5-4 print Command Co-process


Command Description
$( Command substitution for [[ && print
[[ $1 != /* ]] Is the first character in $1 a /
&& Execute the second command only if the first command
is successful
print $PWD/ The current directory name followed by /

If the function is called with a full pathname argument, the command substitution
prints nothing. $file is a full pathname.

The for Loop


The for loop will occur for all arguments on the command line. Those arguments
can be full or relative pathnames.

The read Command


The read command reads from the co-process one of the following:
1. Error message from the cp command in preserve
2. $file saved success message

EY-G994E-SG-0002 5 – 25
UNIX Shell Programming Featuring KornShell

Readability and Maintainability


Suggestions
This section discusses some stylistic suggestions for writing KornShell scripts.

Include Comments at the Beginning of a Script


Always include a comment at the beginning of the script that tells what task the
script was written to perform, as shown in the following example.
Example
# cleanup - script to search my directories and
# eliminate unwanted, space-wasting files.
...

Organize the Script into Sections of Related Commands


Organize the script into sections of related commands with a descriptive comment
at the beginning of each section, as shown in the following example.
Example
...
# set up unique file names for log files
...
# create background processes
...
# check for errors
...

Explain Obscure Syntax with Comments


Explain any obscure syntax with a comment. If it took you more than a second or
two to figure out how to do something clever in the script, you will not be able to
remember how or why if you have to change it later.
Example
# if pathname is relative, prefix pathname with
# the working directory string
path=$([[ $1 != /* ]] && print $PWD/)$path

5 – 26 EY-G994E-SG-0002
Tips and Tricks

Use Blank Lines to Delineate Compound Statements


Place a blank line before and after a compound statement. These include if,
while, until, for, select, and case statements.
Example
id

if [[ -w /etc/passwd ]]
then
print "have write access to /etc/passwd"
fi

print $# positional parameters - $*

Indent the Bodies of Compound Statements


Use spaces or tabs to indent the bodies of compound statements, as shown in the
following example.
Example
while true
do
read file
if cp $file ~/trashcan
then
rm $file
print "$file saved"
fi
done

Set up Compound Statements Consistently


Set up compound statements consistently.
Example
if (( $# == 0 ))
then
print "Usage: $0 filename"
exit 1
fi Choose one or the other
...
if [[ ! -x $file ]]
then
print "$file: Cannot execute"
fi

EY-G994E-SG-0002 5 – 27
UNIX Shell Programming Featuring KornShell

Avoid Combining Unrelated Commands


Avoid combining unrelated commands on a line using the semicolon operator.
Example
cd ~/trashcan ; userlist=$(users) # bad
...
cd $1 ; print "listing of $PWD" ; ls # ok

5 – 28 EY-G994E-SG-0002
Tips and Tricks

Performance
Improving Performance
The following are 11 tips for improving the performance of your KornShell
scripts:
1. Use KornShell built-ins rather than external programs whenever possible.
2. Use aliases rather than functions whenever possible.
3. Keep the $ENV file small. Remember, this file executes for all child
processes.
4. Use tracked aliases by setting the trackall option.
5. Set the nolog option to keep any function definitions out of the history list.
6. Use autoload functions.
7. Avoid creating child processes unnecessarily. Among other techniques,
consider:
! Using the . (dot) command to execute scripts where possible
! Using {} to group commands rather than () where possible
8. Declare variables as integer, whenever possible.
9. Use the semicolon to group related commands. The shell can process two or
three commands after reading a single line from the script file. But beware —
while this is generally good for performance, it can be very bad for
readability.
10. Document what your scripts do. The presence of comment lines seems to
have little or no effect on performance of a script, so you cannot use that as
an excuse not to document your scripts.
11. Use the time command to verify that your strategies have in fact enhanced
performance.

EY-G994E-SG-0002 5 – 29
UNIX Shell Programming Featuring KornShell

Shell Script Examples


Searching the /etc/passwd File
The following script prints the largest user ID number in your system’s
/etc/passwd file.
Example
# uidmax print the largest uid in /etc/passwd.
integer max=0
IFS=: # for set command below
set -o noglob # disable filename expansion in case
# there are * in passwd entries.
while read entry # stdin is /etc/passwd
do
set $entry # passwd entry fields become $1,$2,...

if (( $3 > max )) # then this uid is larger


then
max=$3
fi

done < /etc/passwd # stdin for while loop


print "Largest uid in /etc/passwd is $max"

Repeatedly Invoking a Command


The only difference between replay_opt and replay is that replay_opt
uses getopts to process the command line options, instead of case and
shift. The d and s options take an argument.

5 – 30 EY-G994E-SG-0002
Tips and Tricks

Using replay_opt
The following example shows how to use replay_opt to invoke a command
repeatedly.
Example
# replay_opt shell script to invoke a command repeatedly.
# The command is specified as a cmd line arg.
# Options:
# -d delay interval (default = 5 sec)
# -s size in number of lines (default=23 or LINES)
# -t show the tail of the display
# getopts is used to process the command options.
Delay=5 # delay interval
Tail="" # show tail of display
Size=${LINES:-23} # size of display
Usage="Usage: $0 [-d delay] [-s size] [-t] command"
trap ’clear; exit’ 1 2 3 15

while getopts :d:s:t option


do
case $option in
d) Delay=$OPTARG
;;
s) Size=$OPTARG
;;
t) Tail=True
;;
:) print "$OPTARG option requires argument" >&2
print $Usage >&2
exit 1
;;
\?) print "Unrecognized option \"$OPTARG\"" >&2
exit 1
;;
esac
done

# Shift options from command line


shift $OPTIND-1

# Verify that Delay is numeric


if let $Delay > /dev/null 2>&1
then
: # null statement
else
print "d option argument not an integer" >&2
exit 1
fi

EY-G994E-SG-0002 5 – 31
UNIX Shell Programming Featuring KornShell

The Leading Colon


The leading colon to getopts will not work in ULTRIX. The leading colon
causes the following to be reported.
! The :) choice indicates that an option that requires an argument was used
without an argument. The option is $OPTARG.
! The ?) choice indicates that an invalid option has been used.

Checking Commands
The following example shows checking commands.
Example
# Ensure a command was supplied
if [[ $# -eq 0 ]]
then
print "No command supplied" >&2
print $Usage >&2
exit 1
fi

# Ensure that command is valid


if eval $* > /dev/null 2>&1
then
: # null statement
else
print "$* is not a valid command" >&2
exit 1
fi

# Run the command repeatedly


while true
do
clear
if [[ -n $Tail ]]
then
eval $* | tail -$Size
else
eval $* | sed ${Size}q
fi
sleep $Delay
done

5 – 32 EY-G994E-SG-0002
Tips and Tricks

A Better grep
The following script example invokes sed and makes it work like grep.
Example
# csed shell script to grep for a string in a file
# and display several lines about that line
# containing the string.

if [[ $# -ne 2 ]]
then
print "Usage: $(basename $0) pattern filename" >&2
exit 1
fi

sed -n -e "/$1/{x;p;x;p;n;p;}" -e h $2

# sed reads a line from the file into its pattern space and
# applies the sed command (-e args) to the lines that match.
# As the next line is read, the pattern space is copied into
# the hold space (by the hold command below);
#
# x exchange the contents of the pattern space
# and the hold space
# p print the contents of the pattern space
#
# n replace the pattern space with the next
# line of input
# h replace the contents of the hold space with
# the pattern space
#
# -n option suppress the file contents normally
# written to standard output
! $1 is the search pattern.
! $2 is the name of the file to be searched.
! /$1/ puts a line that has the search pattern into the pattern space.

The sed Command


The sed command has two work areas:
! Pattern space for the selected line
! Hold space as a temporary area where the selected line can be stored

EY-G994E-SG-0002 5 – 33
UNIX Shell Programming Featuring KornShell

How the sed Command Works


As sed reads down through the file looking for the search pattern, it executes the
h command on each line.
The h command places the pattern space (line just read) into the hold space. When
we reach the line that has the search pattern, the hold space will contain the line
prior to the one containing the search pattern. For this line that contains the search
pattern:
! x exchange the pattern space and the hold space. This puts the previous line
in the pattern space.
! p print the pattern space (the previous line).
! x exchange the pattern space and the hold space. This puts the line
containing the search pattern in the pattern space.
! p print the pattern space (the line containing the search pattern).
! n read the next line of the file into the pattern space.
! p print the pattern space (the line following the one containing the search
pattern).

5 – 34 EY-G994E-SG-0002
Tips and Tricks

Summary
Using Here Documents
A Here document consists of one or more lines of data presented to the command
when the command is invoked. Here documents can contain positional
parameters. Here documents can also spawn child shell scripts.

Using File I/O Operators


Use the exec statement to open a stream for reading or writing.

Using Command Options Processing


Command line options (or switches) are usually one letter arguments preceded by
a dash that modify the behavior of the command line processing. Typically, the
dash turns a feature on and the plus turns a feature off. The reserve variable
OPTIND maintains an index of command line arguments.

Reviewing Pattern Matching Operators


This chapter reviewed pattern matching operators and the use of wildcards in
matching.

Using a Lock File to Synchronize Access


File locking allows only one process to access a data file at any one time.

Using Kornshell Command Processing


KornShell parses a command line by first splitting the line into tokens and
organizing them into three categories. Then the command words are tested and
alias substitutions performed, followed by tilde substitution and command
substitutions. Parameters are expanded and the results of command substitutions
are split into two words. Pathnames are expanded in command words and I/O
redirections, and finally, quote characters are removed.

Creating and Using Co-processes


Co-processes are background jobs which are stated in such a way that they can be
referred to with the read and print commands. Multiple co-processes can be
run.

Practice Tips on Code Readability and Maintainability


This chapter provides many suggestions on improving code readability and
maintainability.

Identifying Ways to Improve Performance


This chapter provides suggestions on improving the performance of scripts.
EY-G994E-SG-0002 5 – 35
UNIX Shell Programming Featuring KornShell

Exercises
Instructions
1. Write a shell script that uses getopts to convert an octal, decimal, or
hexadecimal number given on the command line to octal, decimal or
hexadecimal. The -r option takes an argument, o, d, or x, to indicate the
number base of the result (have the script assume a decimal result if the -r
option is not passed). The options -o, -d, or -x indicate the number base of
the input (have the script assume decimal input if none of these options is
passed). The following lab exercise is intended to be a challenge for you. It
illustrates advanced techniques and is not meant to be easy.
2. Write a shell script that will create co-processes to perform remote shell
commands to several hosts on the network simultaneously and log the results
of the commands in log files (one per remote host) on the local host. The
main script should report the pathnames of the log files on exit. The log file
names need to be unique and obvious (in other words, the host name should
be a part of the log file name). The script executing in each co-process will
obtain the name of the host and the name of the log file as command line
arguments. The main script will prompt the user for the command to be
executed remotely, then pass each command to all the co-processes via the
co-process’ standard input. Be sure that when the main script exits, it gives
co-processes time to finish any incomplete commands.

5 – 36 EY-G994E-SG-0002
Common Symbols
Appendix A

EY-G994E-SG-0002 A–1
UNIX Shell Programming Featuring KornShell

A–2 EY-G994E-SG-0002
Common Symbols

List of Symbols
Symbol Table
Table A-1 is a list of common symbols and their definitions.

Table A-1 Common Symbols


Symbol Definition
$1, $2,... $9,${10}, Positional parameters specified on command line
${11}...
$0 Name of shell script
$$ Process id (PID) of currently executing shell
$PPID Process ID of parent process to current shell
$# Number of positional parameters
$* All positional parameters
$? Exit status of most recent command
$! Process ID of last background process
${var:-default} ! Value for variable var

! Default value if var not set

${#var} Number of characters in the value of variable var


${#array[*]} Number of elements in array named array

EY-G994E-SG-0002 A–3
UNIX Shell Programming Featuring KornShell

A–4 EY-G994E-SG-0002
Lab Solutions
Appendix B

EY-G994E-SG-0002 B–1
UNIX Shell Programming Featuring KornShell

B–2 EY-G994E-SG-0002
Lab Solutions

Module 2 Getting Started with Scripting


2.1 Solution
#! /bin/ksh
#
# Simple program which requires at least two arguments and
# echoes them to standard output.
#
if (( $# < 2 ))
then
print -u2 "$0 requires at least two arguments."
exit 1
fi

echo $*

2.2 Solution
#! /bin/ksh
#
# Program which takes one argument, optionally backs it up,
and
# then invokes vi on the file specified.
#
if (( $# != 1))
then
print -u2 "$0 takes exactly one argument"
exit 1
fi

read ans?"Should I backup $1? [y/n]"

if [[ "$ans" != "n" ]]
then
cp $1 $1.backup
fi

vi $1

EY-G994E-SG-0002 B–3
UNIX Shell Programming Featuring KornShell

2.3 Solution
#! /bin/ksh
#
# Script for entering data into a phone list.
#
LIST=./phonelist
read first?"Enter the first name (quit to quit): "
while [[ "$first" != quit ]]
do
read last?"Enter the last name: "
read extension?"Enter the phone extension: "
print $first $last $extension >> $LIST
read first?"Enter the first name (quit to quit): "
done

B–4 EY-G994E-SG-0002
Lab Solutions

2.4 Solution
Script started on Sat Jan 1 02:41:15 2000
$ dirtest /etc /etc/motd /mnt
sh: ./dirtest: Permission denied
$ chmod +x dirtest
$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 4 : `if' unexpected
$ vi dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
print "$i is a directory"
else
print "$1 is not a directory"
endif
end
$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 8 : `else' unexpected
$ vi +8 dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
then
print "$i is a directory"
else
print "$1 is not a directory"
endif
end
$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 8 : `else' unmatched
$ vi +8 dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
then
print "$i is a directory"
else
print "$1 is not a directory"
fi
end

EY-G994E-SG-0002 B–5
UNIX Shell Programming Featuring KornShell

2.4 Solution (Continued)


$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 3 : `for' unmatched
$ vi +3 dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
then
print "$i is a directory"
else
print "$1 is not a directory"
fi
done
$ dirtest /etc /etc/motd /mnt
./dirtest[5]: -d: not found
/etc is not a directory
./dirtest[5]: -d: not found
/etc is not a directory
./dirtest[5]: -d: not found
/etc is not a directory
$ ksh -x dirtest /etc /etc/motd /mnt
+ [ != ]
+ -d i
dirtest[5]: -d: not found
+ print /etc is not a directory
/etc is not a directory
+ -d i
dirtest[5]: -d: not found
+ print /etc is not a directory
/etc is not a directory
+ -d i
dirtest[5]: -d: not found
+ print /etc is not a directory
/etc is not a directory
$ vi dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if [[ -d $i ]]
then
print "$i is a directory"
else
print "$1 is not a directory"
fi
done

B–6 EY-G994E-SG-0002
Lab Solutions

2.4 Solution (Continued)


$ dirtest /etc /etc/motd /mnt
/etc is a directory
/etc is not a directory
/mnt is a directory
$

EY-G994E-SG-0002 B–7
UNIX Shell Programming Featuring KornShell

2.5 Solution
#! /bin/ksh
#
# A script doing file testing, illustrating many if statements
#
USAGE1="Usage: $0 [-r|-w] file"
USAGE2="If neither -r nor -w are supplied, -r is assumed."

# verifying arguments are as expected is always a good idea


if (( $# < 1 )) || (( $# > 2 ))
then
print -u2 "Incorrect argument count: $#"
print -u2 $USAGE1
print -u2 $USAGE2
exit 1
fi

# deal with the optional flag


flag=-r
if (( $# == 2 ))
then
flag=$1
shift # $1 goes away, $2 is moved to be $1
fi
file=$1

# verify the flag is valid


if [[ "$flag" != "-r" ]] && [[ "$flag" != "-w" ]]
then
print -u2 "Invalid flag: $flag"
print -u2 $USAGE1
print -u2 $USAGE2
exit 1
fi

# do the test
if [[ ! -f "$file" ]]
then
print "$file does not exist"
exit 0
fi

# note: if we are here, we know the file exists.


if [[ "$flag" = "-r" ]]
then
if [[ -r $file ]]
then
print "$file is readable"
else
print "$file is not readable"
fi
else # we know the flag must be -w because it was verified above
if [[ -w $file ]]
then
print "$file is writable"
else
print "$file is not writable"
fi
fi

B–8 EY-G994E-SG-0002
Lab Solutions

2.6 Solution
#! /bin/ksh
#
# Back up lots of files. If we are not told which files to
back up,
# do all in the current directory.
#
if (( $# > 0 ))
then
files=$*
else
files=*
fi

for file in $files


do
cp $file $file.save
done

2.7 Solution
#! /bin/ksh
ps -e | tail +1 | cut -c1-12 | sort -n

EY-G994E-SG-0002 B–9
UNIX Shell Programming Featuring KornShell

Module 3 Intermediate Shelf Scripting


3.1 Solution
#! /bin/ksh
#
# a function for editing read-only files
#
function editro {
chmod 600 $1
vi $1
chmod 400 $1
}

3.2 Solution
#! /bin/ksh
#
# function to extract certain lines from ps
#
function psg {
# The last grep prevents the 2nd grep from appearing in
the ps
# listing. We have to be careul; what if we are called
with "grep"
# as an argument?
ps ax | grep $1 | grep -v "grep $1"
}

3.4 Solution
#! /bin/ksh
#
# Script to locate a file either below the current dir or
below a
# dir specified by the user

case $# in
1) find . -name $1 -print ;;
2) find $2 -name $1 -print ;;
*) print -u2 "Usage: $0 file [dir]"
exit 1;;
esac

B – 10 EY-G994E-SG-0002
Lab Solutions

Module 4 Advanced Shell Scripting


4.1 Solution
#! /bin/ksh
#
# Only allow the user to execute a few commands.
# Note that if they can get a shell escape from
# any of these commands (e.g. vi), this .profile will
# be limited in its effectiveness. Additionally, if they
can edit
# this file, again there are no real limits on what they can
do.
#

function byebye {
print -u2 "Caught a signal. Exiting."
exit 1
}

trap byebye INT QUIT TSTP TERM HUP

while read line?"vi or ls only: "


do
case $line in
exit*) exit 0;;
vi*|ls*) eval $line;; # hazardous; user could type ls ; rm *

*) print -u2 "The command line '$line' is not allowed.";;


esac
done

4.2 Solution
#! /bin/ksh
# print the hexadecimal of a decimal integer in $1
print "obase=16\n$1" | bc
#
# or
#
typeset -i16 hex
hex=$1
print $hex

EY-G994E-SG-0002 B – 11
UNIX Shell Programming Featuring KornShell

4.3 Solution
#! /bin/ksh
#
# A script illustrating arithmetic operations and non-base-
10 #integers.
#
typeset -i2 binary
n=$1
while (( $n != 1 ))
do
if (( $n % 2 == 0 ))
then
(( n = $n / 2 ))
else
binary=$n
print "$n $binary"
(( n = $n * 3 + 1 ))
fi
done

4.4 Solution
#! /bin/ksh
#
# Produce a histogram of the values in the array hist.
#
for value in ${hist[*]}
do
n=0
while (( $n < $value ))
do
print -n "*"
(( n = $n + 1 ))
done
print
done

B – 12 EY-G994E-SG-0002
Lab Solutions

4.5 Solution
#! /bin/ksh
#
# A replacement for rm whch stores the files in $TRASH,
compressed
# with a timestamp
#
TRASH=~/trashcan
if [[ ! -d $TRASH ]]
then
mkdir $TRASH
fi

for file
do
stamp=`date +%Y%m%d%H%M%S`
newfile=$TRASH/$file.$stamp
mv $file $newfile
compress $newfile
done

4.6 Solution
#! /bin/ksh
#
# Script which un-trashes a file trashed by the prior lab
exercise
#

for file
do
for possible in ~/trashcan/$file.*
do
local=${possible##*/}
read ans?"restore $local? [y/n]"
if [[ "$ans" = y ]]
then
mv $possible .
uncompress $local
local=${local%.Z} # uncompressed, it no longer ends in .Z
# remove timestamp
mv $local ${local%.[0-9]*}
break
fi
done
done

EY-G994E-SG-0002 B – 13
UNIX Shell Programming Featuring KornShell

Module 5 Tips and Tricks


5.1 Solution
#! /bin/ksh
#
# Convert integers from one base to another
#
USAGE="$0 [-r o|d|x] [-o|d|x]
-r specifies the result base
-o, -d, -x specify the base of the number on the command line
o: octal, d: decimal, x, hexadecimal"

ibase=10
obase=10
while getopts :r:odx opt
do
case $opt in
o) ibase=8;;
d) ibase=10;;
x) ibase=16;;
r) case $OPTARG in
o) obase=8;;
d) obase=10;;
x) obase=16;;
*) print -u2 "Unknown result base specified in
'$OPTARG'\n$USAGE"
exit 1;;
esac;;
:) print -u2 "Missing base for result.\n$USAGE"
exit 1;;
\?) print -u2 "Unknown argument '$OPTARG'\n$USAGE"
exit 1;;
esac
done
shift $OPTIND-1

# actually do the conversion


print "obase=$obase\nibase=$ibase\n$1" | bc

# Note, we could have solved this problem using ksh's integers of a


# given base. It takes notably more code, however. Lesson: know
# the tools available to you.

B – 14 EY-G994E-SG-0002
Lab Solutions

5.2 Solution
#! /bin/ksh
#
# Run commands on multiple machines.
#
# The remote machines are specified as arguments on the
command
# line.
function die {
print -u2 $*
exit 1
}

# initialization: open all connections to co-routines


fd=3
logfiles=
for machine
do
(( $fd == 10 )) && die "Too many machines (7 max)"
./logremote $machine $machine.$$ |&
eval "exec $fd>&p"
(( fd = $fd + 1 ))
logfiles="$logfiles $machine.$$"
done
(( topfd = $fd - 1 ))

# main loop
while read command?"Command to execute: "
do
fd=3
while (( $fd <= $topfd ))
do
print -u$fd "$command"
(( fd = $fd + 1 ))
done
done

# close all pipes to co-routines and allow them to finish


fd=3
while (( $fd <= $topfd ))
do
eval "exec $fd>&-"
(( fd = $fd + 1 ))
done
wait
print "\nLog files are$logfiles"

EY-G994E-SG-0002 B – 15
UNIX Shell Programming Featuring KornShell

B – 16 EY-G994E-SG-0002
Tips and Tricks

Using setuid
Example
$ ls -l /usr/ucb/passwd
-rws--x--x 3 root bin 16384 Aug 8 19:49 /usr/ucb/passwd

$ passwd
Old password:
New password:
Verify:
This can be represented by:

login
passwd
ksh

uid =mary uid =root

Using sush
Example
/* sush.c program to run a KornShell script
whose name and arguments are given on
the cmd line. */
void
main(int argc, char *argv[])
{
strcpy(argv[0], "ksh");
if (execvp("ksh", argv) == -1) {
perror("sush error");
exit(1);
}
exit(0);
}
The following commands must be typed as superuser:
# cc -o sush sush.c
# chmod 4750 sush
# ls -l sush
-rwsr-x--- 1 root system 27916 Jan 4 10:55 sush

EY-G994E-SG-0002 5 – 17
UNIX Shell Programming Featuring KornShell

The following commands are typed as ‘mary ’:


$ cat myscript
id
if [[ -w /etc/passwd ]] ; then
print "have write access to /etc/passwd"
fi
print $# positional parameters - $*

$ sush myscript abc def


uid=910(gar) gid=2(ins)euid=0(root)
have write access to /etc/passwd
2 positional parameters - abc def

5 – 18 EY-G994E-SG-0002
Tips and Tricks

KornShell Command Processing


KornShell Command Line Parsing
The KornShell processes the command line in the following sequence:
1. The command is split into tokens and these are organized in three categories:
a. Variable assignment words
b. Command words
c. I/O redirections
2. Command words are tested and alias substitution is performed.
3. Tilde expansion is performed.
4. Command substitution is done.
5. Parameter expansion is performed.
6. Results of command substitution are split into words.
7. Pathnames are expanded in command words and I/O redirections.
8. Quote characters are removed.

Command Word Precedence


Command word precedence is as follows:
1. Alias
2. Function
3. Built-in
4. Program

EY-G994E-SG-0002 5 – 19
UNIX Shell Programming Featuring KornShell

Exercise/Discussion
Consider the following.
Examples
$ cd ~$USER # ’~’ expanded before variable
~mary: No such file or directory
$ eval cd ~$USER # eval forces second pass

$ ls ~$(whoami) # ’~’ expanded before cmd subst.


~mary: No such file or directory
$ eval cd ~$(whoami) # eval forces second pass
In the first example, USER is substituted at step 5, but the tilde expansion at step
3 has already occurred.
In the second example, the command substitution is performed at step 4 but the
tilde expansion at step 3 has already occurred.
By placing eval prior to these commands, ksh makes a second pass over the
command. Parameter (or command) substitution occurs in the first pass and tilde
expansion in the second pass. The commands work if eval precedes them.

5 – 20 EY-G994E-SG-0002
Tips and Tricks

Co–processes
Definition
A co-process is a background job that is started in such a way that you can
communicate with it using read and print commands.

Running a Script as a Co-process


To run a script as a co-process, append the |& operator to the end of the command
line, as shown in the following example.
Example
$ cat co-script
# co-script
read file
for dir in ~/save /usr/local/save /public/save
do
cp $file $dir
done
print "$file: Saved successfully"

$ co-script |& # run the script as a co-process


$ print -p savefile # send data to the co-process
$ read -p # gather data from the co-process
$ print $REPLY # gathered data was stored in REPLY, so output it
savefile: Saved successfully
! program |& starts the co-process.
! The co-process’ stdin and stdout are connected to file descriptor p of
the parent shell.
! The parent shell uses read -p and print -p commands to exchange
information with the co-process.

EY-G994E-SG-0002 5 – 21
UNIX Shell Programming Featuring KornShell

Co-process Diagram
The process can be represented as shown in Figure 5-1.
Figure 5-1 Co-process Diagram

Parent Shell Co-process

print -p stdin

read -p
stout

5 – 22 EY-G994E-SG-0002
Tips and Tricks

Using Multiple Co-processes


To access more than one co-process, assign another file descriptor to p, as shown
in the following examples.
Examples
$ cat program1
# program1
while true
do
read text
print "$text - this is from program1"
done

$ cat program2
# program2
while true
do
read text
print "$text - this is from program2"
done

$ program1 |&
$ exec 3<&p
$ exec 4>&p

$ program2 |&
$ exec 5<&p
$ exec 6>&p

$ print -u4 hello # write to program1


$ read -u3 repl1 # read from program1
$ print $repl1
hello - this is from program1

$ print -u6 howdy # write to program2


$ read -u5 repl2 # read from program2
$ print $repl2
howdy - this is from program2
! File descriptor 3 reads from program1
! File descriptor 4 writes to program1
! File descriptor 5 reads from program2
! File descriptor 6 writes to program2

EY-G994E-SG-0002 5 – 23
UNIX Shell Programming Featuring KornShell

Using Co-processes
The following example preserves the co-process started in .profile and saves
the files in ~/trashcan. The original is deleted if the save is successful. Next,
it prints a success message or an error message to stdout. On exit, it archives all
files in ~/trashcan and deletes the saved versions. The signal handler archives
files, compress the archive, and deletes saved versions.
Example
# preserve
tf=obsolete.$(date +%d%m%y).$$ # name for archive
trap ’cd ~/trashcan
tar -cf ~/$tf * ;\
compress ~/$tf ;\
rm * ;\
exit’ TERM HUP INT STOP QUIT
while true # save the files and delete originals.
do
read file
if cp $file ~/trashcan 2>&1 # errors to stdout
then
rm $file
print "$file saved" # msg to stdout
fi
done

# save.fn - Function which writes full pathname of


# files passed as command line arguments to the
# preserve co-process. It reads a message from the
# co-process and prints it to stdout.
function save
{
for fil
do
print -p $([[ $fil != /* ]] && print $PWD/)$fil
read -p stat
print $stat
done
}

Starting preserve shell From .profile


In the previous example, the preserve shell script is started from .profile at
login with:
~/preserve |&
tf = obsolete.120295.3722 on Feb 12, 1995, from pid=3722
When logout occurs, this co-process that was started from .profile at login
will be sent the HUP signal. The signal handler executes to save the trashed files
and to cleanup the trashcan directory.
5 – 24 EY-G994E-SG-0002
Tips and Tricks

Single Quotes
Single quotes are essential in the signal handler to avoid expansion of the * wild
card as the signal handler is established with the trap command.

The if Command
The if statement checks the status return from the cp command. The file is
removed only if cp is successful. Errors from the cp command are written to
stdout, which is the channel back to the calling co-process.

The print Command


The print command writes a message to stdout, which is the channel back to
the calling co-process. The first print command writes the full pathname of the
argument to the function to the co-process as shown in Table 5-4.

Table 5-4 print Command Co-process


Command Description
$( Command substitution for [[ && print
[[ $1 != /* ]] Is the first character in $1 a /
&& Execute the second command only if the first command
is successful
print $PWD/ The current directory name followed by /

If the function is called with a full pathname argument, the command substitution
prints nothing. $file is a full pathname.

The for Loop


The for loop will occur for all arguments on the command line. Those arguments
can be full or relative pathnames.

The read Command


The read command reads from the co-process one of the following:
1. Error message from the cp command in preserve
2. $file saved success message

EY-G994E-SG-0002 5 – 25
UNIX Shell Programming Featuring KornShell

Readability and Maintainability


Suggestions
This section discusses some stylistic suggestions for writing KornShell scripts.

Include Comments at the Beginning of a Script


Always include a comment at the beginning of the script that tells what task the
script was written to perform, as shown in the following example.
Example
# cleanup - script to search my directories and
# eliminate unwanted, space-wasting files.
...

Organize the Script into Sections of Related Commands


Organize the script into sections of related commands with a descriptive comment
at the beginning of each section, as shown in the following example.
Example
...
# set up unique file names for log files
...
# create background processes
...
# check for errors
...

Explain Obscure Syntax with Comments


Explain any obscure syntax with a comment. If it took you more than a second or
two to figure out how to do something clever in the script, you will not be able to
remember how or why if you have to change it later.
Example
# if pathname is relative, prefix pathname with
# the working directory string
path=$([[ $1 != /* ]] && print $PWD/)$path

5 – 26 EY-G994E-SG-0002
Tips and Tricks

Use Blank Lines to Delineate Compound Statements


Place a blank line before and after a compound statement. These include if,
while, until, for, select, and case statements.
Example
id

if [[ -w /etc/passwd ]]
then
print "have write access to /etc/passwd"
fi

print $# positional parameters - $*

Indent the Bodies of Compound Statements


Use spaces or tabs to indent the bodies of compound statements, as shown in the
following example.
Example
while true
do
read file
if cp $file ~/trashcan
then
rm $file
print "$file saved"
fi
done

Set up Compound Statements Consistently


Set up compound statements consistently.
Example
if (( $# == 0 ))
then
print "Usage: $0 filename"
exit 1
fi Choose one or the other
...
if [[ ! -x $file ]]
then
print "$file: Cannot execute"
fi

EY-G994E-SG-0002 5 – 27
UNIX Shell Programming Featuring KornShell

Avoid Combining Unrelated Commands


Avoid combining unrelated commands on a line using the semicolon operator.
Example
cd ~/trashcan ; userlist=$(users) # bad
...
cd $1 ; print "listing of $PWD" ; ls # ok

5 – 28 EY-G994E-SG-0002
Tips and Tricks

Performance
Improving Performance
The following are 11 tips for improving the performance of your KornShell
scripts:
1. Use KornShell built-ins rather than external programs whenever possible.
2. Use aliases rather than functions whenever possible.
3. Keep the $ENV file small. Remember, this file executes for all child
processes.
4. Use tracked aliases by setting the trackall option.
5. Set the nolog option to keep any function definitions out of the history list.
6. Use autoload functions.
7. Avoid creating child processes unnecessarily. Among other techniques,
consider:
! Using the . (dot) command to execute scripts where possible
! Using {} to group commands rather than () where possible
8. Declare variables as integer, whenever possible.
9. Use the semicolon to group related commands. The shell can process two or
three commands after reading a single line from the script file. But beware —
while this is generally good for performance, it can be very bad for
readability.
10. Document what your scripts do. The presence of comment lines seems to
have little or no effect on performance of a script, so you cannot use that as
an excuse not to document your scripts.
11. Use the time command to verify that your strategies have in fact enhanced
performance.

EY-G994E-SG-0002 5 – 29
UNIX Shell Programming Featuring KornShell

Shell Script Examples


Searching the /etc/passwd File
The following script prints the largest user ID number in your system’s
/etc/passwd file.
Example
# uidmax print the largest uid in /etc/passwd.
integer max=0
IFS=: # for set command below
set -o noglob # disable filename expansion in case
# there are * in passwd entries.
while read entry # stdin is /etc/passwd
do
set $entry # passwd entry fields become $1,$2,...

if (( $3 > max )) # then this uid is larger


then
max=$3
fi

done < /etc/passwd # stdin for while loop


print "Largest uid in /etc/passwd is $max"

Repeatedly Invoking a Command


The only difference between replay_opt and replay is that replay_opt
uses getopts to process the command line options, instead of case and
shift. The d and s options take an argument.

5 – 30 EY-G994E-SG-0002
Tips and Tricks

Using replay_opt
The following example shows how to use replay_opt to invoke a command
repeatedly.
Example
# replay_opt shell script to invoke a command repeatedly.
# The command is specified as a cmd line arg.
# Options:
# -d delay interval (default = 5 sec)
# -s size in number of lines (default=23 or LINES)
# -t show the tail of the display
# getopts is used to process the command options.
Delay=5 # delay interval
Tail="" # show tail of display
Size=${LINES:-23} # size of display
Usage="Usage: $0 [-d delay] [-s size] [-t] command"
trap ’clear; exit’ 1 2 3 15

while getopts :d:s:t option


do
case $option in
d) Delay=$OPTARG
;;
s) Size=$OPTARG
;;
t) Tail=True
;;
:) print "$OPTARG option requires argument" >&2
print $Usage >&2
exit 1
;;
\?) print "Unrecognized option \"$OPTARG\"" >&2
exit 1
;;
esac
done

# Shift options from command line


shift $OPTIND-1

# Verify that Delay is numeric


if let $Delay > /dev/null 2>&1
then
: # null statement
else
print "d option argument not an integer" >&2
exit 1
fi

EY-G994E-SG-0002 5 – 31
UNIX Shell Programming Featuring KornShell

The Leading Colon


The leading colon to getopts will not work in ULTRIX. The leading colon
causes the following to be reported.
! The :) choice indicates that an option that requires an argument was used
without an argument. The option is $OPTARG.
! The ?) choice indicates that an invalid option has been used.

Checking Commands
The following example shows checking commands.
Example
# Ensure a command was supplied
if [[ $# -eq 0 ]]
then
print "No command supplied" >&2
print $Usage >&2
exit 1
fi

# Ensure that command is valid


if eval $* > /dev/null 2>&1
then
: # null statement
else
print "$* is not a valid command" >&2
exit 1
fi

# Run the command repeatedly


while true
do
clear
if [[ -n $Tail ]]
then
eval $* | tail -$Size
else
eval $* | sed ${Size}q
fi
sleep $Delay
done

5 – 32 EY-G994E-SG-0002
Tips and Tricks

A Better grep
The following script example invokes sed and makes it work like grep.
Example
# csed shell script to grep for a string in a file
# and display several lines about that line
# containing the string.

if [[ $# -ne 2 ]]
then
print "Usage: $(basename $0) pattern filename" >&2
exit 1
fi

sed -n -e "/$1/{x;p;x;p;n;p;}" -e h $2

# sed reads a line from the file into its pattern space and
# applies the sed command (-e args) to the lines that match.
# As the next line is read, the pattern space is copied into
# the hold space (by the hold command below);
#
# x exchange the contents of the pattern space
# and the hold space
# p print the contents of the pattern space
#
# n replace the pattern space with the next
# line of input
# h replace the contents of the hold space with
# the pattern space
#
# -n option suppress the file contents normally
# written to standard output
! $1 is the search pattern.
! $2 is the name of the file to be searched.
! /$1/ puts a line that has the search pattern into the pattern space.

The sed Command


The sed command has two work areas:
! Pattern space for the selected line
! Hold space as a temporary area where the selected line can be stored

EY-G994E-SG-0002 5 – 33
UNIX Shell Programming Featuring KornShell

How the sed Command Works


As sed reads down through the file looking for the search pattern, it executes the
h command on each line.
The h command places the pattern space (line just read) into the hold space. When
we reach the line that has the search pattern, the hold space will contain the line
prior to the one containing the search pattern. For this line that contains the search
pattern:
! x exchange the pattern space and the hold space. This puts the previous line
in the pattern space.
! p print the pattern space (the previous line).
! x exchange the pattern space and the hold space. This puts the line
containing the search pattern in the pattern space.
! p print the pattern space (the line containing the search pattern).
! n read the next line of the file into the pattern space.
! p print the pattern space (the line following the one containing the search
pattern).

5 – 34 EY-G994E-SG-0002
Tips and Tricks

Summary
Using Here Documents
A Here document consists of one or more lines of data presented to the command
when the command is invoked. Here documents can contain positional
parameters. Here documents can also spawn child shell scripts.

Using File I/O Operators


Use the exec statement to open a stream for reading or writing.

Using Command Options Processing


Command line options (or switches) are usually one letter arguments preceded by
a dash that modify the behavior of the command line processing. Typically, the
dash turns a feature on and the plus turns a feature off. The reserve variable
OPTIND maintains an index of command line arguments.

Reviewing Pattern Matching Operators


This chapter reviewed pattern matching operators and the use of wildcards in
matching.

Using a Lock File to Synchronize Access


File locking allows only one process to access a data file at any one time.

Using Kornshell Command Processing


KornShell parses a command line by first splitting the line into tokens and
organizing them into three categories. Then the command words are tested and
alias substitutions performed, followed by tilde substitution and command
substitutions. Parameters are expanded and the results of command substitutions
are split into two words. Pathnames are expanded in command words and I/O
redirections, and finally, quote characters are removed.

Creating and Using Co-processes


Co-processes are background jobs which are stated in such a way that they can be
referred to with the read and print commands. Multiple co-processes can be
run.

Practice Tips on Code Readability and Maintainability


This chapter provides many suggestions on improving code readability and
maintainability.

Identifying Ways to Improve Performance


This chapter provides suggestions on improving the performance of scripts.
EY-G994E-SG-0002 5 – 35
UNIX Shell Programming Featuring KornShell

Exercises
Instructions
1. Write a shell script that uses getopts to convert an octal, decimal, or
hexadecimal number given on the command line to octal, decimal or
hexadecimal. The -r option takes an argument, o, d, or x, to indicate the
number base of the result (have the script assume a decimal result if the -r
option is not passed). The options -o, -d, or -x indicate the number base of
the input (have the script assume decimal input if none of these options is
passed). The following lab exercise is intended to be a challenge for you. It
illustrates advanced techniques and is not meant to be easy.
2. Write a shell script that will create co-processes to perform remote shell
commands to several hosts on the network simultaneously and log the results
of the commands in log files (one per remote host) on the local host. The
main script should report the pathnames of the log files on exit. The log file
names need to be unique and obvious (in other words, the host name should
be a part of the log file name). The script executing in each co-process will
obtain the name of the host and the name of the log file as command line
arguments. The main script will prompt the user for the command to be
executed remotely, then pass each command to all the co-processes via the
co-process’ standard input. Be sure that when the main script exits, it gives
co-processes time to finish any incomplete commands.

5 – 36 EY-G994E-SG-0002
Common Symbols
Appendix A

EY-G994E-SG-0002 A–1
UNIX Shell Programming Featuring KornShell

A–2 EY-G994E-SG-0002
Common Symbols

List of Symbols
Symbol Table
Table A-1 is a list of common symbols and their definitions.

Table A-1 Common Symbols


Symbol Definition
$1, $2,... $9,${10}, Positional parameters specified on command line
${11}...
$0 Name of shell script
$$ Process id (PID) of currently executing shell
$PPID Process ID of parent process to current shell
$# Number of positional parameters
$* All positional parameters
$? Exit status of most recent command
$! Process ID of last background process
${var:-default} ! Value for variable var

! Default value if var not set

${#var} Number of characters in the value of variable var


${#array[*]} Number of elements in array named array

EY-G994E-SG-0002 A–3
UNIX Shell Programming Featuring KornShell

A–4 EY-G994E-SG-0002
Lab Solutions
Appendix B

EY-G994E-SG-0002 B–1
UNIX Shell Programming Featuring KornShell

B–2 EY-G994E-SG-0002
Lab Solutions

Module 2 Getting Started with Scripting


2.1 Solution
#! /bin/ksh
#
# Simple program which requires at least two arguments and
# echoes them to standard output.
#
if (( $# < 2 ))
then
print -u2 "$0 requires at least two arguments."
exit 1
fi

echo $*

2.2 Solution
#! /bin/ksh
#
# Program which takes one argument, optionally backs it up,
and
# then invokes vi on the file specified.
#
if (( $# != 1))
then
print -u2 "$0 takes exactly one argument"
exit 1
fi

read ans?"Should I backup $1? [y/n]"

if [[ "$ans" != "n" ]]
then
cp $1 $1.backup
fi

vi $1

EY-G994E-SG-0002 B–3
UNIX Shell Programming Featuring KornShell

2.3 Solution
#! /bin/ksh
#
# Script for entering data into a phone list.
#
LIST=./phonelist
read first?"Enter the first name (quit to quit): "
while [[ "$first" != quit ]]
do
read last?"Enter the last name: "
read extension?"Enter the phone extension: "
print $first $last $extension >> $LIST
read first?"Enter the first name (quit to quit): "
done

B–4 EY-G994E-SG-0002
Lab Solutions

2.4 Solution
Script started on Sat Jan 1 02:41:15 2000
$ dirtest /etc /etc/motd /mnt
sh: ./dirtest: Permission denied
$ chmod +x dirtest
$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 4 : `if' unexpected
$ vi dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
print "$i is a directory"
else
print "$1 is not a directory"
endif
end
$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 8 : `else' unexpected
$ vi +8 dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
then
print "$i is a directory"
else
print "$1 is not a directory"
endif
end
$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 8 : `else' unmatched
$ vi +8 dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
then
print "$i is a directory"
else
print "$1 is not a directory"
fi
end

EY-G994E-SG-0002 B–5
UNIX Shell Programming Featuring KornShell

2.4 Solution (Continued)


$ dirtest /etc /etc/motd /mnt
./dirtest[3]: syntax error at line 3 : `for' unmatched
$ vi +3 dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if (-d i)
then
print "$i is a directory"
else
print "$1 is not a directory"
fi
done
$ dirtest /etc /etc/motd /mnt
./dirtest[5]: -d: not found
/etc is not a directory
./dirtest[5]: -d: not found
/etc is not a directory
./dirtest[5]: -d: not found
/etc is not a directory
$ ksh -x dirtest /etc /etc/motd /mnt
+ [ != ]
+ -d i
dirtest[5]: -d: not found
+ print /etc is not a directory
/etc is not a directory
+ -d i
dirtest[5]: -d: not found
+ print /etc is not a directory
/etc is not a directory
+ -d i
dirtest[5]: -d: not found
+ print /etc is not a directory
/etc is not a directory
$ vi dirtest
$ cat dirtest
#!/bin/ksh
#dirtest - shell script with bugs
for i
do
if [[ -d $i ]]
then
print "$i is a directory"
else
print "$1 is not a directory"
fi
done

B–6 EY-G994E-SG-0002
Lab Solutions

2.4 Solution (Continued)


$ dirtest /etc /etc/motd /mnt
/etc is a directory
/etc is not a directory
/mnt is a directory
$

EY-G994E-SG-0002 B–7
UNIX Shell Programming Featuring KornShell

2.5 Solution
#! /bin/ksh
#
# A script doing file testing, illustrating many if statements
#
USAGE1="Usage: $0 [-r|-w] file"
USAGE2="If neither -r nor -w are supplied, -r is assumed."

# verifying arguments are as expected is always a good idea


if (( $# < 1 )) || (( $# > 2 ))
then
print -u2 "Incorrect argument count: $#"
print -u2 $USAGE1
print -u2 $USAGE2
exit 1
fi

# deal with the optional flag


flag=-r
if (( $# == 2 ))
then
flag=$1
shift # $1 goes away, $2 is moved to be $1
fi
file=$1

# verify the flag is valid


if [[ "$flag" != "-r" ]] && [[ "$flag" != "-w" ]]
then
print -u2 "Invalid flag: $flag"
print -u2 $USAGE1
print -u2 $USAGE2
exit 1
fi

# do the test
if [[ ! -f "$file" ]]
then
print "$file does not exist"
exit 0
fi

# note: if we are here, we know the file exists.


if [[ "$flag" = "-r" ]]
then
if [[ -r $file ]]
then
print "$file is readable"
else
print "$file is not readable"
fi
else # we know the flag must be -w because it was verified above
if [[ -w $file ]]
then
print "$file is writable"
else
print "$file is not writable"
fi
fi

B–8 EY-G994E-SG-0002
Lab Solutions

2.6 Solution
#! /bin/ksh
#
# Back up lots of files. If we are not told which files to
back up,
# do all in the current directory.
#
if (( $# > 0 ))
then
files=$*
else
files=*
fi

for file in $files


do
cp $file $file.save
done

2.7 Solution
#! /bin/ksh
ps -e | tail +1 | cut -c1-12 | sort -n

EY-G994E-SG-0002 B–9
UNIX Shell Programming Featuring KornShell

Module 3 Intermediate Shelf Scripting


3.1 Solution
#! /bin/ksh
#
# a function for editing read-only files
#
function editro {
chmod 600 $1
vi $1
chmod 400 $1
}

3.2 Solution
#! /bin/ksh
#
# function to extract certain lines from ps
#
function psg {
# The last grep prevents the 2nd grep from appearing in
the ps
# listing. We have to be careul; what if we are called
with "grep"
# as an argument?
ps ax | grep $1 | grep -v "grep $1"
}

3.4 Solution
#! /bin/ksh
#
# Script to locate a file either below the current dir or
below a
# dir specified by the user

case $# in
1) find . -name $1 -print ;;
2) find $2 -name $1 -print ;;
*) print -u2 "Usage: $0 file [dir]"
exit 1;;
esac

B – 10 EY-G994E-SG-0002
Lab Solutions

Module 4 Advanced Shell Scripting


4.1 Solution
#! /bin/ksh
#
# Only allow the user to execute a few commands.
# Note that if they can get a shell escape from
# any of these commands (e.g. vi), this .profile will
# be limited in its effectiveness. Additionally, if they
can edit
# this file, again there are no real limits on what they can
do.
#

function byebye {
print -u2 "Caught a signal. Exiting."
exit 1
}

trap byebye INT QUIT TSTP TERM HUP

while read line?"vi or ls only: "


do
case $line in
exit*) exit 0;;
vi*|ls*) eval $line;; # hazardous; user could type ls ; rm *

*) print -u2 "The command line '$line' is not allowed.";;


esac
done

4.2 Solution
#! /bin/ksh
# print the hexadecimal of a decimal integer in $1
print "obase=16\n$1" | bc
#
# or
#
typeset -i16 hex
hex=$1
print $hex

EY-G994E-SG-0002 B – 11
UNIX Shell Programming Featuring KornShell

4.3 Solution
#! /bin/ksh
#
# A script illustrating arithmetic operations and non-base-
10 #integers.
#
typeset -i2 binary
n=$1
while (( $n != 1 ))
do
if (( $n % 2 == 0 ))
then
(( n = $n / 2 ))
else
binary=$n
print "$n $binary"
(( n = $n * 3 + 1 ))
fi
done

4.4 Solution
#! /bin/ksh
#
# Produce a histogram of the values in the array hist.
#
for value in ${hist[*]}
do
n=0
while (( $n < $value ))
do
print -n "*"
(( n = $n + 1 ))
done
print
done

B – 12 EY-G994E-SG-0002
Lab Solutions

4.5 Solution
#! /bin/ksh
#
# A replacement for rm whch stores the files in $TRASH,
compressed
# with a timestamp
#
TRASH=~/trashcan
if [[ ! -d $TRASH ]]
then
mkdir $TRASH
fi

for file
do
stamp=`date +%Y%m%d%H%M%S`
newfile=$TRASH/$file.$stamp
mv $file $newfile
compress $newfile
done

4.6 Solution
#! /bin/ksh
#
# Script which un-trashes a file trashed by the prior lab
exercise
#

for file
do
for possible in ~/trashcan/$file.*
do
local=${possible##*/}
read ans?"restore $local? [y/n]"
if [[ "$ans" = y ]]
then
mv $possible .
uncompress $local
local=${local%.Z} # uncompressed, it no longer ends in .Z
# remove timestamp
mv $local ${local%.[0-9]*}
break
fi
done
done

EY-G994E-SG-0002 B – 13
UNIX Shell Programming Featuring KornShell

Module 5 Tips and Tricks


5.1 Solution
#! /bin/ksh
#
# Convert integers from one base to another
#
USAGE="$0 [-r o|d|x] [-o|d|x]
-r specifies the result base
-o, -d, -x specify the base of the number on the command line
o: octal, d: decimal, x, hexadecimal"

ibase=10
obase=10
while getopts :r:odx opt
do
case $opt in
o) ibase=8;;
d) ibase=10;;
x) ibase=16;;
r) case $OPTARG in
o) obase=8;;
d) obase=10;;
x) obase=16;;
*) print -u2 "Unknown result base specified in
'$OPTARG'\n$USAGE"
exit 1;;
esac;;
:) print -u2 "Missing base for result.\n$USAGE"
exit 1;;
\?) print -u2 "Unknown argument '$OPTARG'\n$USAGE"
exit 1;;
esac
done
shift $OPTIND-1

# actually do the conversion


print "obase=$obase\nibase=$ibase\n$1" | bc

# Note, we could have solved this problem using ksh's integers of a


# given base. It takes notably more code, however. Lesson: know
# the tools available to you.

B – 14 EY-G994E-SG-0002
Lab Solutions

5.2 Solution
#! /bin/ksh
#
# Run commands on multiple machines.
#
# The remote machines are specified as arguments on the
command
# line.
function die {
print -u2 $*
exit 1
}

# initialization: open all connections to co-routines


fd=3
logfiles=
for machine
do
(( $fd == 10 )) && die "Too many machines (7 max)"
./logremote $machine $machine.$$ |&
eval "exec $fd>&p"
(( fd = $fd + 1 ))
logfiles="$logfiles $machine.$$"
done
(( topfd = $fd - 1 ))

# main loop
while read command?"Command to execute: "
do
fd=3
while (( $fd <= $topfd ))
do
print -u$fd "$command"
(( fd = $fd + 1 ))
done
done

# close all pipes to co-routines and allow them to finish


fd=3
while (( $fd <= $topfd ))
do
eval "exec $fd>&-"
(( fd = $fd + 1 ))
done
wait
print "\nLog files are$logfiles"

EY-G994E-SG-0002 B – 15
UNIX Shell Programming Featuring KornShell

B – 16 EY-G994E-SG-0002

S-ar putea să vă placă și