KSH - Variables & Arrays
KSH - Variables & Arrays
Korn Shell
By Anatole Olczak.
---------------------------------------------------------------------------------
-----------------------------------------------------------
Variables
Korn shell variable names can begin with an alphabetic (a–Z) or underscore character,
followed by one or more alphanumeric (a–Z, 0–9) or underscore characters. Other
variable names that contain only digits (0–9) or special characters (!, @, #, %, *, ?, $) are
reserved for special parameters set directly by the Korn shell.
To assign a value to a variable, you can simply name the variable and set it to a value. For
example, to assign abc to variable X:
$ X=abc
The typeset command can also be used to assign values, but unless you are setting
attributes, it's a lot more work for nothing. If a value is not given, the variable is set to
null. Here, X is reassigned the null value:
$ X=
This is not the same as being undefined. As we'll see later, accessing the value of an
undefined variable may return an error, while accessing the value of a null variable
returns the null value.
Accessing Variable Values
To access the value of a variable, precede the name with the $ character. There can be no
space between $ and the variable name. In this example, CBIN is set to /usr/ccs/bin.
$ CBIN=/usr/ccs/bin
Table 3.1. Assigning Values to Variables
variable= declare variable and set it to null
Now you can just type $CBIN instead of the long pathname:
$ cd $CBIN
$ pwd
/usr/ccs/bin
Here is a new command to go along with this concept: print. It displays its arguments on
your terminal, just like echo.
$ print Hello world!
Hello world!
Here we use print to display the value of CBIN:
$ print $CBIN
/usr/ccs/bin
Variable Attributes
Korn shell variables can have one or more attributes that specify their internal
representation, access or scope, or the way they are displayed. This concept is similar to a
data type in other high-level programming languages, except that in the Korn shell it is
not as restrictive. Variables can be set to integer type for faster arithmetic operations,
read-only so that the value cannot be changed, left/right justified for formatting purposes,
and more. To assign a value and/or attribute to a Korn shell variable, use the following
format with the typeset command:
typeset –attribute variable=value
or
typeset –attribute variable
Except for readonly, variable attributes can be set before, during, or after assignment.
Functionally it makes no difference. Just remember that the attribute has precedence over
the value. This means that if you change the attribute after a value has been assigned, the
value may be affected.
Lowercase (–l) and Uppercase (–u) Attributes
These attributes cause the variable values to be changed to lower or uppercase. For
example, the lowercase attribute and uppercase value ASPD are assigned to variable
MYSYS:
$ typeset —l MYSYS=ASPD
Despite the fact that MYSYS was assigned uppercase ASPD, when accessed, the value is
displayed in lowercase:
$ print $MYSYS
aspd
This is because the attribute affects the variable value, regardless of the assignment.
Variable attributes can also be changed after assignment. If we wanted to display variable
MYSYS in uppercase, we could just reset the attribute:
$ typeset —u MYSYS
$ print $MYSYS
ASPD
Table 3.2. Assigning Values/Attributes to Variables
typeset – attribute variable=value assign attribute and value to variable
typeset –L var Left justify var; the field width is specified by the first assignment
typeset –LZn var Left justify var; set field width to n and strip leading zeros
typeset –R var Right justify var; the field width is specified by the first assignment
typeset –RZn var Right justify var; set field width to n and fill with leading zeros
typeset –t var Set the user-defined attribute for var. This has no meaning to the Korn shell.
typeset –x var Automatically export var to the environment (same as the export command)
$
This is not the same as being set to null. As we'll see later in this chapter, variable
expansion may be performed differently, depending on whether the variable value is set
to null.
Unsetting either the base or nameref variable will unset both variables.
$ unset Y
$ print $X
$ print $Y
$
-----------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
Special Parameters
Some special parameters are automatically set by the Korn shell and usually cannot be
directly set or modified.
The ? Parameter
The ? parameter contains the exit status of the last executed command. In this example,
the date command is executed. It ran successfully, so the exit status is 0:
$ date +%D
05/24/96
$ print $?
0
Notice that there was no output displayed from the date command. This is because the
>&– I/O operator causes standard output to be closed. The next command, cp ch222.out
/tmp, did not run successfully, so 1 is returned:
$ cp ch222.out /tmp
ch222.out: No such file or directory
$ print $?
1
When used with a pipe, $? contains the exit status of the last command in the pipeline.
Table 3.4. Some Preset Special Parameters
? exit status of the last command
E
RRN error number returned by most recently failed system call (system dependent)
O
The $ Parameter
The $ parameter contains the process id of the current shell.
$ print $$
178
It is useful in creating unique file names within Korn shell scripts.
$ touch $0.$$
$ ls *.*
ksh.218
Other Special Parameters
The – parameter contains the current options in effect. The output of the next command
shows that the interactive and monitor options are enabled:
$ print $—
im
To display the error number of the last failed system call, use the ERRNO variable. Here,
an attempt to display a non-existent file returns an error, so ERRNO is checked for the
error number:
$ cat tmp.out
tmp.out: No such file or directory
$ print $ERRNO
2
This is system dependent, so it may not be available on your system. Check your
documentation or /usr/include/sys/errno.h for more information.
Special Reserved Variables
The Korn shell has two types of reserved variables - those that are set and updated
automatically by the Korn shell, and those that are set by each user or the system
administrator.
-----------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
Variable Expansion
Variable expansion is the term used for the ability to access and manipulate values of
variables and parameters. Basic expansion is done by preceding the variable or parameter
name with the $ character. This provides access to the value.
$ UULIB=/usr/lib/uucp
$ print $UULIB
/usr/lib/uucp
Other types of expansion can be used to return portions or the length of variables, use
default or alternate values, check for mandatory setting, and more.
For the sake of convenience, the term variable will refer to both variables and parameters
in the following sections that discuss variable expansion.
$ variable, ${variable}
This is expanded to the value of variable. The braces are used to protect or delimit the
variable name from any characters that follow. The next example illustrates why braces
are used in variable expansion. The variable CA is set to ca:
$ CA=ca
What if we wanted to reset CA to california? It could be reset to the entire value, but to
make a point, let's try using the current value like this:
$ CA=$CAlifornia
$ print $CA
$
Nothing is printed, because without the braces around the variable CA, the Korn shell
looks for a variable named $CAlifornia. None is found, so nothing is substituted. With
the braces around variable CA, we get the correct value:
$ CA=${CA}lifornia
$ print $CA
california
Braces are also needed when attempting to expand positional parameters greater than 9.
This ensures that both digits are interpreted as the positional parameter name.
${#variable}
This is expanded to the length of variable. In this example, X is set to a three-character
string, so a length of 3 is returned:
$ X=abc
$ print ${#X}
3
Whitespace in variable values is also counted as characters. Here the whitespace from the
output of the date command is also counted:
$ TODAY=$(date)
$ print ${#TODAY}
28
${variable:—word}, ${variable—word}
This is expanded to the value of variable if it is set and not null, otherwise word is
expanded. This is used to provide a default value if a variable is not set. In the following
example, the variable X is set to abc. When expanded using this format, the default value
abc is used:
$ X=abc
$ print ${X:—cde}
abc
After X is unset, the alternate value cde is used:
$ unset X
$ print ${X:—cde}
cde
Notice that the value of X remains unchanged:
$ print $X
$
Let's say we needed a command to get the user name. The problem is that some people
have it set to USER, while others have it set to LOGNAME. We could use an if
command to check one value first, then the other. This would be quite a few lines of code.
Or we could use this form of variable expansion to have both values checked with one
command. Here, if USER is set, then its value is displayed, otherwise the value of
LOGNAME is displayed.
$ print USER=$USER, LOGNAME=$LOGNAME
USER=anatole, LOGNAME=AO
$ print ${USER:—${LOGNAME}}
anatole
Now we unset USER to check and make sure that LOGNAME is used:
$ unset USER
$ print ${USER:—${LOGNAME}}
AO
But what if both USER and LOGNAME are not set? Another variable could be checked
like this:
$ print ${USER:—${LOGNAME:—${OTHERVAR}}}
But to demonstrate other ways that the alternate value can be expanded, let's just use
some text.
$ unset USER LOGNAME
$ print ${USER:-${LOGNAME:-USER and LOGNAME \
not set!}}
USER and LOGNAME not set!
In this version, the output of the whoami command is used:
$ print ${USER—${LOGNAME:—$(whoami)}}
anatole
For compatibility with the Bourne shell, it could also be given as:
$ echo ${USER:—${LOGNAME:—`whoami`}}
anatole
Remember that the alternate value is only used and not assigned to anything. The next
section shows how you can assign an alternate value if a default value is not set. The
other format, ${variable– word}, causes the variable value to be used, even if it is set to
null:
$ typeset X=
$ print ${X-cde}
$
${variable:= word}, ${variable=word}
This is expanded to the value of variable if set and not null, otherwise it is set to word,
then expanded. In contrast to the variable expansion format from the previous section,
this format is used to assign a default value if one is not already set. In the next example,
the variable LBIN is set to /usr/lbin. When expanded using this format, the default value
/usr/lbin is used:
$ LBIN=/usr/lbin
$ print ${LBIN:=/usr/local/bin}
/usr/lbin
After LBIN is unset, this form of variable expansion causes LBIN to be assigned the
alternate value, /usr/local/bin:
$ unset LBIN
$ print ${LBIN:=/usr/local/bin}
/usr/local/bin
Notice that LBIN is now set to /usr/local/bin.
$ print $LBIN
/usr/local/bin
Command substitution can also be used in place of word. This command sets the SYS
variable using only one command:
$ unset SYS
$ print ${SYS:=$(hostname)}
aspd
The other format, ${variable=word}, causes the variable value to be used, even if it is set
to null. Here LBIN is not assigned an alternate value. If := was used instead of =, then
LBIN would be set to /usr/local/bin:
$ LBIN=
$ print ${LBIN=/usr/local/bin}
$
${variable:?word}, ${variable:?}, ${variable?word}, ${variable?}
This is expanded to the value of variable if it is set and not null, otherwise word is printed
and the Korn shell exits. If word is omitted, 'parameter null or not set' is printed. This
feature is often used in Korn shell scripts to check if mandatory variables are set. In this
example, variable XBIN is first unset. When expanded, the default error is printed:
$ unset XBIN
$ : ${XBIN:?}
/bin/ksh: XBIN: parameter null or not set
The ? as the word argument causes the default error message to be used. You could also
provide your own error message:
$ print ${XBIN:?Oh my God, XBIN is not set!}
/bin/ksh: XBIN: Oh my God, XBIN is not set!
The other formats, ${variable?word} and ${variable?}, cause the variable value to be
used, even if it is set to null.
${variable:+word}, ${variable+word}
This is expanded to the value of word if variable is set and not null, otherwise nothing is
substituted. This is basically the opposite of the ${variable:–word} format. Instead of
using word if variable is not set, word is used if variable is set. In the first example Y is
set to abc. When expanded, the alternate value def is displayed because Y is set:
$ Y=abc
$ print ${Y:+def}
def
Here, Y is unset. Now when expanded, nothing is displayed:
$ unset Y
$ print ${Y:+def}
$
Like the ${variable:–word} format, the alternate value is only used and not assigned to
the variable. Y is still set to null:
$ print $Y
$
The other format, ${variable+word}, causes the variable value to be used, even if it is set
to null:
$ Y=
$ print ${Y+def}
def
${variable#pattern}, ${variable##pattern}
This is expanded to the value of variable with the smallest (#) or largest (##) part of the
left matched by pattern deleted. What these expansion formats allow you to do is
manipulate substrings. To demonstrate the basic functionality, X is set to a string that
contains a recurring pattern: abcabcabc.
$ X=abcabcabc
When expanded to return the substring that deletes the smallest left pattern abc, we get
abcabc:
$ print ${X#abc*}
abcabc
while the substring that deletes the largest left pattern abc is abcabcabc, or the entire
string:
$ print ${X##abc*}
$
Table 3.5. Variable Expansion Formats
${#variable} length of variable
${variable:–word} value of variable if set and not null, else print word
${variable:= word} value of variable if set and not null, else variable is set to word, then expanded
${variable:+word} value of word if variable is set and not null, else nothing is substituted
${variable:?} value of variable if set and not null, else print 'variable: parameter null or not set'
${variable:?word} value of variable if set and not null, else print value of word and exit
${variable #pattern} value of variable without the smallest beginning portion that matches pattern
${variable ##pattern} value of variable without the largest beginning portion that matches pattern
${variable%pattern} value of variable without the smallest ending portion that matches pattern
${variable%%pattern} value of variable without the largest ending portion that matches pattern
${variable//pattern1/pattern
replace all occurrences of pattern1 with pattern2 in variable
2}
We could use this concept to implement the Korn shell version of the Unix basename
command. The pattern in this command causes the last directory to be returned if variable
X is set to a full pathname:
$ X=/usr/spool/cron
$ print ${X##*/}
cron
${variable%pattern}, ${variable%%pattern}
This is expanded to the value of variable with the smallest (%) or largest (%%) part of
the right matched by pattern deleted. This is the same as the parameter expansion format
from the previous section, except that patterns are matched from the right instead of left
side. It could also be used to display file names without their .suffixes:
$ X=file.Z
$ print ${X%.*}
file
Here, any trailing digits are stripped:
$ X=chap1
$ print ${X%%[0—9]*}
chap
$ X=chap999
$ print ${X%%[0—9]*}
chap
The pattern in this command causes it to act like the Unix dirname command.
Everything except the last directory is returned if variable X is set to a full pathname.
$ X=/usr/spool/cron
$ print ${X%/*}
/usr/spool
${variable//pattern1/pattern2}, ${variable/pattern1/pattern2},
${variable#pattern1/pattern2}, ${variable/%pattern1/pattern2}
The Korn shell supports four search and replace operations on variables. This example
changes all occurrences of abc in X to xyz:
$ X=abcabcabc
$ print ${X//abc/xyz}
xyzxyzxyz
while this one only changes the first occurrence of abc in X to xyz:
$ X=abcabcabc
$ print ${X/abc/xyz}
xyzabcabc
See Table 3.6 for detailed explanation of the other formats.
${variable:start}, ${variable:start:length}
This format returns a substring. The first returns variable from position start to end,
while the second returns length characters from variable from character position start to
end. For example, this returns the first 3 characters of X:
$ X=abcdefghij
$ print {$X:0:3}
abc
while this example returns the value of X starting at character position 5:
$ X=abcdefghij
$ print {$X:5}
fghij
-----------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
Array Variables
One-dimensional arrays are supported by the Korn shell. Arrays can have a maximum of
4096 elements. Array subscripts start at 0 and go up to 4096 (or the maximum element
minus one). Any variable can become a one-dimensional array by simply referring to it
with a subscript. Here, variable X is set to A:
By explicitly assigning X[1] a value, variable X is transformed into an array variable:
$ X[1]=B
The original assignment to variable X is not lost. The first array element (X[0]) is still
assigned A.
Table 3.6. More Variable Expansion Formats
${variable//pattern1/patter
replace all occurrences of pattern1 with pattern2 in variable
n2}
${variable/pattern1/patter
replace first occurrence of pattern1 with pattern2 in variable
n2}
${variable/#pattern1/patte replace first occurrence of pattern1 with pattern2 in variable if variable begins with
rn2} pattern1
${variable/%pattern1/patt replace last occurrence of pattern1 with pattern2 in variable if variable ends with
ern2} pattern1
${variable:start:length} return length characters from variable from character position start to end
$
All the elements of an array can be accessed by using the * or @ as the subscript value.
They both return the same value. These examples print the values of all the elements of
the DAY array variable:
$ print ${DAY[*]}
Mon Tue Wed Thu Fri Sat Sun
$ print ${DAY[@]}
Mon Tue Wed Thu Fri Sat Sun
The number of elements of an array variable is returned by using the # in front of the
array variable name and using * or @ as the subscript value. Let's try it with DAY:
$ print ${#DAY[*]}
7
$ print ${#DAY[@]}
7
To get values for a subset of an array, use this format:
${variable[*]:start_subscript:num_elements}
or
${variable[@]:start_subscript}
Arithmetic expressions can be used to return a subscript value. This example prints the
fifth element of the DAY array variable. Remember that array subscripts start with 0, so
the third array element has a subscript of 2, and the fifth element has a subscript of 4:
$ print ${DAY[4/2]}
Wed
$ print ${DAY[7-6+5-4+3-2+1]
Fri
Variable expansion can also be used to generate a subscript value.
$ X=6
$ print ${DAY[$X]}
Sun
Array Variable Attributes
As with ordinary variables, attributes can be assigned to array-type variables. Arrays can
also be declared, and assigned values and attributes with the typeset command:
typeset –attribute variable[0]=value variable[1]=value. . .
Once set, attributes apply to all elements of an array. This example sets the uppercase
attribute for the DAY array variable using the typeset –u command:
$ typeset —u DAY
Now all the element values are displayed in upper case:
$ print ${DAY[*]}
MON TUE WED THU FRI SAT SUN
Array element attributes can be set before or after assignment. Here, XVAR is initially
assigned lowercase aaa, bbb, and ccc:
$ set —A XVAR aaa bbb ccc
$ print ${XVAR[*]}
aaa bbb ccc
Now, the uppercase, left-justify, two-character-wide attributes are set and the new
element values are displayed. Notice that the third character from each element has been
dropped, and the value is now in uppercase:
$ typeset —uL2 XVAR
$ print ${XVAR[*]}
AA BB CC
Array Variable Reassignments
Besides using regular array-element[n]=value or typeset array-element[n]=value syntax
to reassign values, array variables can also have their values reassigned with the set +A
command:
set +A variable value0 value1 . . .
This is helpful when you don't want to reassign values to all the elements of an array. In
this example, the array variable X is assigned six values:
$ set —A X one two three d e f
$ print ${X[*]}
one two three d e f
Using the set +A command, the first three elements are reassigned a, b, and c:
$ set +A X a b c
$ print ${X[*]}
a b c d e f
Notice that values of the fourth, fifth, and sixth elements have not been affected.
Associative Arrays
This version of the Korn shell also supports associative arrays, that is arrays that use
string subscripts rather than integer subscripts. Associative arrays are declared using this
format:
typeset –A variable
where variable is the name of the associative array. Additional arguments can be given to
the typeset command to specify a data type. For example, we can create an associative
array to store some exchange rates:
$ typeset -AE exchange_rate
$ exchange_rate["DM"]=1.7
$ exchange_rate["FF"]=.15
$ exchange_rate["AS"]=.04
To display a list of associative array subscripts:
${!variable[*]} or ${!variable[@]}
To display the values for all or parts of an associative array:
${!variable[subscript]}
For example, all and a specific exchange rate is displayed here:
$ print ${!exchange_rate[*]}
0.15 1.7
$ print "The DM exchange rate is:${exchange_rate[DM]}"
1.7
---------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
Compound Variables
The Korn shell also supports compound variables, which are similar to structures or
records in other languages, that is a meta-datatype which is a group of related values,
each of which can have a different data type. The syntax for declaring compund variables
is:
compound_variable=(
[datatype] field1[=value]
. . .
[datatype] fieldn[=value]
)
For example, we can use a compound variable to manage employee information:
$ employee=(
typeset name=Allenby
integer id=1243
float salary=9000.50
)
The syntax to display the value of a compound variable field is:
${compound_variable.field}
Here we access the employee compound variable:
$ print $employee
( typeset -E salary=9000.5 name=Allenby typeset -i
id=1243 )
$ print ${employee.name}
Allenby
---------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Quoting
Quotes are used when assigning values containing whitespace or special characters, to
delimit parameters and variables, and to assign command output.
There are three types of quotes: single quotes, double quotes, and back quotes. Single and
double quotes are similar, except for the way they handle some special characters. Back
quotes are used for command output assignment.
Look what happens when you try to perform a variable assignment using a value that
contains whitespace without enclosing it in quotes:
$ GREETING=Hello world
/bin/ksh: world: not found
$ print $GREETING
$
No assignment is made. To assign Hello world to GREETING, you would need to
enclose the entire string in quotes, like this:
$ GREETING='Hello world'
$ print $GREETING
Hello world
Single Quotes
Single quotes are also used to hide the meaning of special characters like $, *, \, !, ", `
and /. Any characters between single quotes, except another single quote, are displayed
without interpretation as special characters:
$ print '* $ \ ! ` / "'
* $ \ ! ` / "
This also means that variable and command substitution does not take place within single
quotes (because $ and `` lose their special meaning). If you want to access the value of a
variable, use double quotes instead of single quotes (discussed in the next section). So,
instead of displaying /home/anatole, we get $HOME:
$ print '$HOME'
$HOME
and instead of the date, we get `date`:
$ print 'Today is `date`'
Today is `date`
Korn shell command substitution $(...) also doesn't work in single quotes, because of the
$ character. You may be thinking what good are single quotes anyway? Well, there are
still some good uses for them. You could print a menu border like this:
$ print '******************MENU*****************'
******************MENU******************
or use the $ character as the dollar symbol:
$ print 'Pass GO — Collect $200'
Pass GO — Collect $200
You couldn't do that with double quotes! Actually there are quite a few good uses for
single quotes. They can be used to print a double-quoted string:
$ print '"This is in double quotes"'
"This is in double quotes"
or just to surround plain old text that has embedded whitespace. This improves
readability by separating the command from the argument. Variables can be set to null
with single quotes:
$ X=''
Single quotes are also used to assign values to aliases and trap commands, and prevent
alias substitution, but we'll get to that later.
Double Quotes
Double quotes are like single quotes, except that they do not remove the meaning of the
special characters $, `, and \. This means that variable and command substitution is
performed.
$ DB="$HOME:`pwd`"
$ print $DB
/home/anatole:/tmp
Double quotes also preserve embedded whitespace and newlines. Here are some
examples:
$ print "Line 1
> Line 2"
Line 1
Line 2
$ print "A B"
A B
The > is the secondary prompt, and is displayed whenever the Korn shell needs more
input. In this case, it waits for the closing double quote:
$ ADDR="ASP,Inc
> PO Box 23837
> San Jose CA 95153 USA
> (800)777-UNIX * (510)531-5615"
Without double quotes around ADDR, we get this:
$ print $ADDR
ASP,Inc PO Box 23837 San Jose CA 95153 USA (800) 777-
UNIX * (510)531-5615
Not quite what we wanted. Let's try it again:
$ print "$ADDR"
ASP,Inc
PO Box 23837
San Jose CA 95153 USA
(800)777-UNIX * (510)531-5615
There are also other uses for double quotes. You can set a variable to null:
$ NULL=""
$ print $NULL
$
or display single quotes.
$ print "'This is in single quotes'"
'This is in single quotes'
If you really wanted to display the $, `, \, or " characters using double quotes, escape
them with a backslash like this:
$ print "\$HOME is set to $HOME"
$HOME is set to /home/anatole
$ print "\`=back-quote \\=slash \"=double-quote"
`=back-quote \=slash "=double-quote
Back Quotes
Back quotes are used to assign the output of a command to a variable. This format, from
the Bourne shell, is accepted by the Korn shell but considered obsolescent. This
command sets the variable SYS to the system name:
$ SYS=`uuname —l`
$ print $SYS
aspd
---------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------