Commands
Overview
Some commands are built into the Qshell interpreter. These are called built-in utilities . Other commands are separate executable files, or external commands . Qshell must locate these files in order to execute them. The external commands supplied with Qshell are stored in directory /usr/bin. If you have installed PASE, the Portable Application Solutions Environment, which permits you to execute AIX binaries, you may also run the PASE utilities, which are stored in /QOpenSys/usr/bin.
The type command, which was added to Qshell in V5R2, tells you how a command is implemented. As you can see in Figure 12.1, echo is a built-in utility, break is a special built-in utility, for is a reserved word, and rexec is a file.
type echo echo is a shell built-in. /home/jsmith $ type break break is a special shell built-in. /home/jsmith $ type for for is a reserved word. /home/jsmith $ type rexec rexec is /usr/bin/rexec.
Figure 12.1: Use the type command to determine how a utility is implemented.
An alternative that works in V4R4 and later releases is the whence utility with the -v option, shown in Figure 12.2.
whence -v echo echo is a shell built-in. /home/JSMITH $ whence -v continue continue is a special shell built-in. /home/JSMITH $ whence -v if if is a reserved word. /home/JSMITH $ whence -v grep grep is /usr/bin/grep.
Figure 12.2: The -v option of whence tells how a utility is implemented.
Yet another alternative is command - V , shown in Figure 12.3. Note that the V must be capitalized.
command -V read read is a shell built-in. /home/jsmith $ command -V exec exec is a special shell built-in. /home/jsmith $ command -V do do is a reserved word. /home/jsmith $ command -V find find is /usr/bin/find.
Figure 12.3: Command - V is equivalent to whence - v .
Regular and Special Built in Utilities
Built-in utilities are divided into two groups: special and regular. Special built-in utilities must conform to certain POSIX standards:
- A syntax error in a special built-in utility may cause the shell to abort.
A syntax error in a regular built-in may not cause the shell to abort.
- Variable assignments must remain in effect after the utility completes.
Variable assignments made during the execution of regular built-in or other utilities do not remain in effect after the utility completes.
The special built-ins for V5R2 are listed in Table 12.1. To make a special built-in utility run as if it were a regular built-in utility, run it under the command utility.
Name |
Description |
---|---|
break |
Exit the loop. |
: ( colon ) |
Interpret as a null command. |
continue |
Begin the next iteration of a loop. |
(dot) |
Execute commands in the current environment. |
eval |
Construct a command from arguments. |
exec |
Execute commands; open , close, or copy file descriptors. |
exit |
Exit the shell. |
export |
Set the export attribute for variables . |
readonly |
Set the read-only attribute for variables. |
return |
Exit a function. |
set |
Set or unset positional parameters and options. |
shift |
Discard positional parameters. |
trap |
Monitor signals. |
unset |
Unset variables and functions. |
Dual Implementations
Some utilities are implemented as both built-in and external utilities. Qshell normally runs the built-in version. The executable file version is included for reasons of backward-compatibility with systems that did not implement the utility as a built-in.
Figure 12.4 shows that the echo utility is implemented both as a built-in and external file in Qshell. The a option of the type and whence utilities tell Qshell to list all implementations of a command.
type -a echo echo is a shell built-in. echo is /usr/bin/echo. /home/jsmith $ whence -a echo echo is a shell built-in. echo is /usr/bin/echo.
Figure 12.4: Some utilities are implemented both as built-in and external utilities. Normally Qshell will run the built-in version.
In Figure 12.5, Qshell uses the echo built-in to run the first echo command and an external file to run the second. Both commands produce identical results, but the first one runs much faster than the second.
echo $QSH_VERSION V5R2M0 /home/jsmith $ /usr/bin/echo $QSH_VERSION V5R2M0
Figure 12.5: Built-in utilities are faster than their external counterparts.
Script Interpreters
As you know, a script is a text file that contains commands. When you run a Qshell script, Qshell reads commands from the text file in the same way that it reads commands from the keyboard. That is, Qshell reads a command, interprets it, executes it, and proceeds to the next command. Scripts run in a new process, unless invoked with the source (dot) operator. For more information about scripts, see chapter 4.
However, Qshell is not the only program that can interpret scripts. When Qshell runs a script, it begins by checking the first line for a magic number , the #! sequence. If the script file contains a magic number, Qshell loads the specified utility and passes the name of the script file to the utility as an argument. Figure 12.6 shows this behavior in action. The script file sayit has one record, a magic-number record that points to the external version of the echo utility. When Qshell executes sayit, it loads the echo utility and passes to it the name of the script. Thus, Qshell runs the command sayit /home/jsmith/sayit , which writes the name of the script to standard output.
cat sayit #! /bin/echo /home/jsmith $ sayit /home/jsmith/sayit /home/jsmith $
Figure 12.6: The echo utility is used here as a script interpreter.
It might be interesting to note that it is hard to write any interesting scripts using echo , because echo doesnt actually read a file passed to it and execute the commands in the file. Instead, it simply echoes the parameters ”in this case, the filename.
You can use this feature to run other programs, such as your own programs and PASE utilities. Running your own programs is discussed in chapter 19. Figure 12.7 gives an example that runs a PASE utility. Notice the magic number pointing to the directory of PASE binaries. Since the C shell is a PASE utility, the script must be stored in an ASCII file and lines must end with a linefeed character, not with the carriage -return linefeed combination that is commonly in use.
cat demo4.csh #! /QOpenSys/usr/bin/csh set file = if ($file == "") then echo "Usage:
cat demo4.csh #! /QOpenSys/usr/bin/csh set file = $1 if ($file == "") then echo "Usage: $0 file" exit 1 endif if (! -f $file) then echo "File $file does not exist." exit 2 endif cp $file $file.ba
file" exit 1 endif if (! -f $file) then echo "File $file does not exist." exit 2 endif cp $file $file.ba
Figure 12.7: The magic number is necessary to make this C shell script run under PASE.
If there is no magic-number record, a shell attempts to run a script file directly. For this reason, you do not need the magic number if you are using Qshell and want Qshell to run the script.
External Utilities and the Magic Number
The external versions of built-in utilities are not worthless. You can use them to run scripts. This is not a terribly useful technique, but it does come in handy on occasion.
The traditional iSeries user interface has one control language, CL, and one shell, the CL command interpreter. Within a Qshell environment or on a Unix system, however, any program that reads the standard input file and writes to the standard output file can be used as a script interpreter, as shown in Figure 12.8.
ls re* read002.qsh removeme /home/jsmith $ cat removeme #! /bin/rm /home/jsmith $ removeme /home/jsmith $ ls re* read002.qsh /home/jsmith $
Figure 12.8: The removeme file deletes itself.
The file removeme in Figure 12.8 has one line, a magic number that executes the external version of the rm utility. When the removeme file is executed as a command, Qshell executes the external version of rm with removeme as its argument. The result is that Qshell deletes the removeme file. A self-deleting file is not uncommon in Unix shell scripts. The presence or absence of such files is tested to control processing within a script.
A common task in many introductory computer science classes is to write a program that can completely print its own source code. To impress your friends , you could write that program trivially using a shell script of the following form:
#! /bin/cat
No, thats not a typo. The entire shell script that prints itself is this single line, with the subtle behaviors of shell scripting behind it. Figure 12.9 provides a larger example of this concept. It does basically the same thing, but is a bit more useful and realistic in day-to-day use.
cat crib #! /bin/cat Subdirectory for EDI: /home/EDI Subdirectory for our scripts: /home/scripts --- To reset files to re-receive EDI: qsh runedi -r --- To delete a directory & everything under it: rm -r To get date as YYYYMMDD : date +%Y%m%d /home/jsmith $ chmod +x crib /home/jsmith $ crib #! /bin/cat Subdirectory for EDI: /home/EDI Subdirectory for our scripts: /home/scripts --- To reset files to re-receive EDI: qsh runedi -r --- To delete a directory & everything under it: rm -r To get date as YYYYMMDD : date +%Y%m%d /home/jsmith $
Figure 12.9: Thanks to the magic number, the crib file lists itself to standard output.
In Figure 12.9, the user has placed reminders into an online "crib" sheet. The first command shows the contents of file "crib." Notice that the magic number runs the external version of the cat utility. The chmod command makes crib executable. The last command shows crib at work. When the user types crib and presses Enter, Qshell reads the magic-number line to see which program is to interpret the file. Qshell loads the external version of cat , passing the name of the file, crib , as an argument.
Even though the external utilities are in directory/usr/bin, the magic numbers in the previous examples specify /bin. The reason for specifying /bin is to be compatible with Unix systems, which store external utilities in /bin. On the iSeries, /bin is a symbolic link to /usr/bin. (Symbolic links are discussed in chapter 9.)
Aliases
An alias is an alternate name for a command. Usually, the alias is an abbreviation of a command and one or more arguments. An alias overrides built-in and regular utilities of the same name .
To define an alias, use the alias utility. To remove an alias, use the unalias utility. These utilities are illustrated in Figure 12.10. In this example, ll ("long listing") is an alias for the ls utility with the l ("ell") option.
alias ll=ls -l /home/jsmith $ ll *.txt -rwx---rwx 1 JSMITH 0 800 Oct 19 15:59 edity.txt -rwxrwxrwx 1 JSMITH 0 43 Feb 22 2002 ftpmodel.txt -rwxrwxrwx 1 JSMITH 0 746 Oct 22 22:18 goodoleboys.txt -rwxrwxrwx 1 JSMITH JSMITHGP 0 Oct 24 18:03 myfile.txt -rw-rw-rw- 1 JSMITH JSMITHGP 51 Oct 23 17:34 temp.txt -rw-rw-rw- 2 JSMITH 0 7 Feb 4 2002 test1.txt -rw-rw-rw- 2 JSMITH 0 7 Feb 4 2002 test1a.txt /home/jsmith $ unalias ll /home/jsmith $ ll *.txt qsh: 001-0019 Error found searching for command ll. No such path or directory.
Figure 12.10: Use the alias and unalias utilities to define and undefine aliases.
V5R2 Qshell predefines three aliases, all having to do with data types:
- float=declare -E
- functions=declare -f
- integer=declare -i
To display the defined aliases, use the alias command with no non-option arguments, as shown in Figure 12.11. The first three in this example are predefined in Qshell V5R2. The last two have been defined within the Qshell session.
alias float=declare -E functions=declare -f integer=declare -i ll=ls -l path=echo $PATH /home/jsmith $
Figure 12.11: The alias command without arguments lists the defined aliases.
To remove all aliases, use the unalias command with the a option, as shown in Figure 12.12.
alias float=declare -E functions=declare -f integer=declare -i ll=ls -l path=echo $PATH /home/jsmith $ unalias -a /home/jsmith $ alias /home/jsmith $
Figure 12.12: Use the unalias utility to remove aliases.
Reserved Words
Certain tokens that are used for flow control in shell scripts are known as reserved words. Qshell will not stop you from using them as names for your own commands, but you should not do so. Compared to other Unix and Unix-like shells, however, Qshell is more lenient. Many shells forbid you from naming variables with reserved-word names.
The Qshell reserved words are listed by related-command group in Table 12.2.
if |
then |
elif |
else |
fi |
case |
esac |
|||
for |
in |
|||
While |
||||
Until |
||||
do |
done |
|||
{ |
} |
|||
function |
||||
[[ |
]] |
Functions
A function is a set of commands grouped under a command name . Functions correspond to subroutines, procedures, functions, and so forth in other programming languages.
Functions are discussed in detail in chapter 13. For now, you need to know that a function is invoked in the same way as any other command: by typing its name.
Simple and Compound Commands
Unix literature differs on the definition of simple and compound commands. Some Unix sources propose a third type of commandthe complex command.
For purposes of this book, a simple command is one that you can run from a Qshell prompt. A simple command may or may not include arguments, variable assignments, and redirections. Qshell reserved words are not considered to be simple commands.
Compound commands are structures made of simple commands. Compound commands allow you to do complex things without writing a script. Qshell treats a compound command as a unit. There are several types of compound commands:
- Lists of simple commands separated by semicolons
- Pipelines
- Looping structures ( while , until , and for )
- Conditional structures ( if , case , &&, and )
- Functions
- Scripts
For example, the following compound command renames all .txt files of a directory, changing the extension to .text:
for i in *.txt; do j=$(echo $i sed -e s/.txt/.text/); echo $i $j; mv $i $j; done
The CMD Parameter
To start the Qshell, use the Start Qshell command (STRQSH or QSH), as discussed in chapter 2. This command accepts one parameter: the name of a Qshell command to execute. The command may be up to 5,000 characters long. The default value of the CMD parameter is *NONE, which tells Qshell to begin an interactive session.
You may specify a Qshell command in the CMD parameter of STRQSH. For example, the following command runs Qshell in batch mode:
STRQSH CMD(ls *.csv)
When the CMD parameter contains a Qshell command, Qshell does not open a terminal session. Instead, Qshell executes the command and ends. If the command produces output, Qshell opens a temporary C runtime terminal session. This is shown in Figure 12.13.
Figure 12.13: Qshell opens a temporary terminal session if necessary.
Use the QIBM_QSH_CMD_OUTPUT environment variable if you wish to direct the output elsewhere. Table 12.3 shows the possible values you can use.
Value |
Definition |
---|---|
STDOUT |
Send the output to a temporary C runtime terminal session. This is the default. |
NONE |
Discard the output. |
FILE= pathname |
Replace the contents of the file in pathname with the output. |
FILEAPPEND= pathname |
Append the output to the file. |
Input is another matter. When you specify a command in the CMD parameter, Qshell ignores requests for terminal input. If you want the command to read input, you must use redirection.
Processes
A process in Unix parlance roughly corresponds to a job in iSeries architecture. In the case of Qshell, the two are the same for all practical purposes. A process is implemented as a job.
A process is a running program. When you start a Qshell terminal session, you start a process. A process has attributes that define it. Among these attributes are the following:
- Environment variables
- The file-creation mask
- Open files
- File redirection
- Permissions
- The current directory
- Signals
Built-in utilities, reserved word commands, functions, and " dotted " scripts run in the process from which they are invoked. To execute other commands, such as scripts and executable files, Qshell has to create other processes. The process that creates other processes is called the parent process. A created process is called a child process.
A child process inherits attributes from the parent shell. Some, such as the current directory, are inherited automatically. Others, such as variables, are not inherited unless you specify that they should be inherited. In V5R2, you can use the Display Process Status utility ( ps ) to list active processes, as shown in Figure 12.14.
ps PID DEVICE TIME FUNCTION STATUS JOBID 2398 sjsmith5 000:00 cmd-qsh dspa 003675/jsmith/sjsmith5 2411 - 000:00pgm-qzshsh evtw 003693/jsmith/qzshsh 2414 - 000:00 pgm-qzshsh evtw 003696/jsmith/qp0zspwp 2415 - 000:00 pgm-ps run 003697/jsmith/qp0zspwp
Figure 12.14: The ps utility displays current processes.
Subshells
A subshell is one type of child process. A subshell is a copy of Qshell running under another copy of Qshell. You can start a subshell by running the qsh command or the sh command from a Qshell process, or by surrounding the command name with parentheses. Use the exit command to end a subshell and return control to the parent.
For example, the parent process in Figure 12.15 is numbered 1981. Executing the qsh command appears to change nothing, as the Qshell prompt remains the dollar sign. However, the fact that another copy of Qshell is now communicating with the terminal is shown by the second print command, which shows that process 1982 is active. The exit command ends process 1982, putting process 1981 back in control of the terminal session.
print $$ 1981 /home/jsmith $ qsh $ print $$ 1982 $ exit /home/jsmith $ print $$ 1981 /home/jsmith $
Figure 12.15: Use the exit command to end a subshell and return control to the parent.
In Figure 12.16, variable name is defined in the parent process, but its definition is not passed along to the subshell.
name=Joe /home/jsmith $ print $$ $name 1981 Joe /home/jsmith $ qsh $ print $$ $name 1983 $ exit /home/jsmith $ print $$ $name 1981 Joe
Figure 12.16: A child process cannot access the variables of the parent process.
You may use the qsh command with the c option to execute a command in a subshell. In this case, the subshell does not communicate with the terminal, but executes the command and ends. For example, in this example, Qshell creates a new shell instance to run script file myscript.qsh:
Qshell and Child Processes
Qshell automatically starts child processes to run scripts and external commands, to process redirection and pipelines, to handle groups of commands, and to implement background processes. Surrounding a command, whether simple or compound, with parentheses forces Qshell to run the command in a child process.
In Figure 12.17, there are two compound commands that change the current directory and list directory contents. The first one runs in the current shell, but the second one, the one in parentheses, runs in a subshell. You can determine this fact by looking at the Qshell prompt that follows the output of each command, because the prompt has been set to show the name of the current directory. In the first instance, the current directory is the /bin directory. This means that the cd command ran in the current shell. In the second instance, the current directory is the /home/jsmith directory, which means that the cd command executed in a different shell.
/home/jsmith $ cd bin ; ls donde.qsh one.qsh two one ten upper.txt /home/jsmith/bin $ cd .. /home/jsmith $ (cd bin ; ls) donde.qsh one.qsh two one ten upper.txt /home/jsmith $
Figure 12.17: Surrounding a command with parentheses forces execution in a separate process.
Exporting Variables to Child Processes
A child process does not automatically inherit the values of variables from the parent process. If you want child processes to inherit the value of a variable, you must set the variables export property. For example, in Figure 12.18, the export keyword ensures that child processes will use the values of these variables, rather than default values.
cat .profile export PATH="$PATH/home/jsmith/bin:" export PS1=$PWD $ export ENV=tscript.qsh /home/jsmith $
Figure 12.18: It is common for the .profile file to export global variables to child processes.
If you prefer, you can assign a value to a variable and export the variable in two steps:
cat .profile PATH ="$PATH/home/jsmith/bin:" export PATH /home/jsmith $
You may use set 's allexport option to cause Qshell to set the export attribute of all variables. It has two forms:
set -a set -o allexport
To turn off the allexport option, use a plus sign instead of a hyphen:
set -a name=Joe Smith dept=Accounting set +a license=A2102
The name and dept variables will be exported to subshells. The variable license will not be exported
Exporting Aliases to Child Processes
Child processes do not inherit aliases defined in the parent process. If you want child processes to inherit an alias, do the following:
- Create a Qshell script file and place the alias in it.
- In the .profile file, assign the name of the file you created to the ENV environment variable and export its value.
When a new instance of Qshell begins to run, the commands in the file named by the ENV variable will be executed, as shown in Figure 12.19. The .profile file assigns a value of tscript.qsh to the ENV variable. Since this variable is exported, the tscript.qsh script will be executed each time a child process begins. Since the alias path is defined in tscript.qsh, this alias will be defined in all child processes.
cat .profile export PATH="$PATH/home/jsmith/bin:" export PS1=$PWD $ export ENV=tscript.qsh /home/jsmith $ cat tscript.qsh alias path=echo $PATH
Figure 12.19: To export an alias to a child process, define the export in the file named in the ENV environment variable.
Locating Commands
As discussed earlier in this chapter, two or more commands may have the same name , but be implemented differently. In such a case, Qshell uses the following order of precedence to decide which implementation to run:
- Reserved word
- Alias
- Special built-in
- Function
- Regular built-in
- Executable file
You can override this search order to a point because of the following rules:
- The command utility ignores aliases and functions.
- The builtin utility will only run a built-in.
- An initial backslash ignores aliases and functions.
For example, in Figure 12.20, an echo function has been created. It displays all arguments, preceding them with an arrow made of two equal signs and a greater-than sign. Qshell gives precedence to this function unless echo is run under the built-in utility.
function echo () > { > print "==> $ @" > } /home/jsmith $ type -a echo echo is a shell built-in. echo is a function. echo is /usr/bin/echo. /home/jsmith $ echo $ QSH_VERSION ==> V5R2M0 /home/jsmith $ builtin echo $ QSH_VERSION V5R2M0
Figure 12.20: Echo is a regular built-in utility, so it normally has precedence.
In Figure 12.21, an alias is created with the same name as the ls utility, which is an executable file in the /usr/bin directory. The alias takes precedence over the file unless the alias is preceded by a backslash.
ls *.txt edity.txt goodoleboys.txt temp.txt test1a.txt ftpmodel.txt myfile.txt test1.txt /home/jsmith $ alias ls=ls -l /home/jsmith $ type -a ls ls is an alias for ls -l. ls is /usr/bin/ls. /home/jsmith $ ls *.txt -rwx---rwx 1 JSMITH 0 800 Oct 19 15:59 edity.txt -rwxrwxrwx 1 JSMITH 0 43 Feb 22 2002 ftpmodel.txt -rwxrwxrwx 1 JSMITH 0 746 Oct 22 22:18 goodoleboys.txt -rwxrwxrwx 1 JSMITH JSMITHGP 0 Oct 24 18:03 myfile.txt -rw-rw-rw- 1 JSMITH JSMITHGP 51 Oct 23 17:34 temp.txt -rw-rw-rw- 2 JSMITH 0 7 Feb 4 2002 test1.txt -rw-rw-rw- 2 JSMITH 0 7 Feb 4 2002 test1a.txt /home/jsmith $ ls *.txt edity.txt goodoleboys.txt temp.txt test1a.txt ftpmodel.txt myfile.txt test1.txt
Figure 12.21: Preceding the command name ls with a backslash tells Qshell to ignore the alias.
The Path Variable
When you run an application or script, Qshell does not search all directories in the IFS. It searches only the directories listed in the PATH variable. If Qshell does not find an executable file of the proper name, it writes an error message to stderr.
The PATH variable contains a list of directories that are to be searched for external files. The directories must be separated by colons. The default value for PATH is /usr/bin: . If you want Qshell to search the current directory, you must include the current directory in the path name.
There are four ways to indicate the current directory. You can use:
- An initial colon in the path value
- Two adjacent colons within the path value
- A trailing colon on the path
- A period in the path variable
The following example uses the print utility to display the current path:
print $PATH /usr/bin:/home/jsmith/bin:
In this example, there are two directories in the path. Because the path name ends with a colon, Qshell will also search the current directory after searching the directories in the path.
To define the path, assign a value to the PATH variable:
PATH=/:/home/jsmith:/usr/bin:/home/jsmith/bin
To add more directories to a path, include the $PATH expansion in an assignment to the PATH variable. Surround the assigned value with double quotes, not single quotes, so that Qshell can substitute the current path into the assignment:
print $PATH /usr/bin:/home/jsmith/bin: /home/jsmith $ PATH="$PATH/QOpenSys/usr/bin" /home/jsmith $ print $PATH /usr/bin:/home/jsmith/bin:/QOpenSys/usr/bin
The Hash Utility
The hash utility, shown in Figure 12.22, maintains a list of the locations of utilities. When a utility is executed for the first time in a Qshell session, hash searches for the utility and stores its location in a list. When the utility is later executed in the same process, Qshell does not have to look through directories for the utility, but retrieves the utility from the location named in the list.
hash ls=/usr/bin/ls donde.qsh=/home/jsmith/bin/donde.qsh mv=/usr/bin/mv function echo
Figure 12.22: The hash utility stores the locations of utilities in a list.
You may use the -r option of hash to clear the list of utility locations, as shown in Figure 12.23.
hash rm=/usr/bin/rm ls=/usr/bin/ls /home/jsmith $ hash -r /home/jsmith $ hash /home/jsmith $
Figure 12.23: The - r option clears the hash table.
With V5R2, IBM added the p option to the hash utility, which allows you to store a utilitys location in the list. The utility name may be different from the base file name. The syntax of hash 's p option is shown here:
hash -p filename utility
For example, in the following command, Qshell is to look for the donde.qsh script in directory /home/jsmith:
hash -p /home/jsmith/donde.qsh donde.qsh
In the following example, whenever Qshell is told to run the wer command, it will run script donde.qsh in directory /home/jsmith:
hash -p /home/jsmith/donde.qsh wer
Running Commands in the Background
Unix systems allow shell commands to run in either foreground or background . When a command runs in foreground, it is in direct communication with a terminal. A background process, on the other hand, runs asynchronously. It cannot communicate directly with the terminal, and halts if it encounters an I/O request.
However, Qshell does not have the same concept of foreground and background jobs. A command that is sent to background runs asynchronously, but it continues to interact with the terminal session. This means that multiple processes may communicate with a terminal simultaneously . Therefore, it might be a good idea to redirect the input and output of background processes.
To run a command in a background process, end the command with an ampersand (&). Qshell responds with the number and ID of the background process. When the background process is finished, Qshell sends another notification message. Since the find command in Figure 12.24 is expected to run a long time, it is submitted to a background process, which the system numbers as 1744. While the find is running, the user runs the ls , cat , and rm utilities. Qshell sends a message when the background job completes. At this point, the user views the output of the find command, which was directed to disk files.
find /myd -exec grep -il print {} ; > prt-f.txt 2>errors.txt & [1] 1744 /myd $ ls *.txt edity.txt goodoleboys.txt temp.txt errors.txt myfile.txt test1.txt ftpmodel.txt print-files.txt test1a.txt /myd $ cat temp.txt Junior April 30 BR-549 Jcival Lilly Faye 12 /myd $ rm temp.txt /myd $[1] Done find /myd -exec grep -il print {} ; >prt-f.txt 2>err.txt & /myd $ cat prt-f.txt /myd/bin/donde.qsh /myd/temp1/casewild.qsh /myd/bu.qsh /myd/casewild.qsh /myd/demo.csh /myd/donde.qsh /myd/listargs.qsh /myd/read002.qsh /myd/select01.qsh /myd/while001.qsh /myd/fix2.qsh /myd $ cat err.txt grep: 001-0023 Error found opening file /myd/bin/one.qsh. Permission denied. grep: 001-2113 Error found getting information for object /myd/temp.txt. No such path or directory.
Figure 12.24: It is a good practice to run long-running processes in background and direct their output to disk files.
To cancel a background process, use the kill command, like this:
find /home/smith -exec grep -il print {} ;> prt-f.txt 2>errors.txt & [1] 1893 /home/jsmith $ kill 1893 /home/jsmith $
When you end a Qshell session, background processes are also terminated . Use the nohup (No Hangup) utility to enable a process to continue to run after Qshell ends. For example, with this command, process 1979 will continue to run when the Qshell session is ended:
nohup find /myd -exec grep -il print {} ;> prt-f.txt 2>errors.txt & [1] 1979 /home/jsmith $
The nohup utility was added to Qshell in V5R2.
Command Substitution
In command substitution, the output of a command replaces the command itself. It is as if the output of the command had been keyed, rather than the command itself.
There are two ways to specify command substitution. The old, and now discouraged, method is to surround the command with backquotes. The new, preferred, way is to precede the command with the $( string (a dollar sign followed by an open parenthesis) and close it with a close parenthesis.
Any command, utility, alias, script, or program can be used in a command-substitution expression. And, a command-substitution expression can be used anywhere ”as an option, filename, string, etc. ”in any part of a Qshell script. Figure 12.25 gives an example of command substitution. Qshell runs the date command, which prints a formatted date to standard output. The output of date replaces the substitution expression. The result is that the current date is inserted into the name of the incoming EDI backup file.
cp edi-in edi-in-$(date +%Y-%m-%d).bak /home/jsmith $ ls edi* edi-in edi-in-2003-01-03.bak
Figure 12.25: Command substitution replaces a command with its output.
The Source (Dot) Utility
The source (or dot) utility executes a script or function within the current process, not in a child process. Running a script or function under the source utility is equivalent to keying the commands within the current process.
The source utility has the following two forms:
. scriptname source scriptname
In releases prior to V5R2, you must use the "dot" form of the source utility. As of V5R2, you may use either form.
A common use of the source utility is to initialize variables that will be used throughout the remainder of the Qshell session. Qshell uses the source utility to execute the .profile, etc/profile, and $ENV startup files.
The closest counterparts in traditional iSeries programming are RPGs /COPY and COBOLs COPY directives or C/C++ #include directives, which include source code at compilation, as if the contents of the copybooks or header files had been keyed directly into the source member.
An example of the source utility is shown in Figure 12.26. Whoami.qsh is a single-line script that prints the ID of the process in which the script is running. The interactive Qshell session is process 2399, as the print command shows. Running whoami.qsh without the dot operator causes Qshell to start another process, 2405. Running whoami.qsh with the dot operator causes Qshell to run the script in the current process, as if the print command in the script had been typed in the current process.
cat whoami.qsh print "I am process $$." /home/jsmith $ print $$ 2399 /home/jsmith $ whoami.qsh I am process 2405. /home/jsmith $ . whoami.qsh I am process 2399.
Figure 12.26: Failing to run the whoami.qsh script under the source utility produces nerroneous results in this example.
In Figure 12.27, SetMyEnv.qsh is a script file that assigns values to certain variables. The first print command shows that the name and dept variables have no values in the current process. Running the script without the dot operator does not affect the current process. However, after running the script with the dot operator, the variables are defined.
cat SetMyEnv.qsh name=Joe Smith firstname=Joe lastname=Smith dept=Accounting /home/jsmith $ print $name $dept /home/jsmith $ SetMyEnv.qsh /home/jsmith $ print $name $dept /home/jsmith $ . SetMyEnv.qsh /home/jsmith $ print $name $dept Joe Smith Accounting
Figure 12.27: Sourcing a script is a good way to assign values to frequently used variables.
The Xargs Utility
The xargs utility (Execute Arguments) reads arguments from standard input and passes them to a command. If no command is given, xargs uses the echo command.
To understand how xargs works, suppose that an input stream contains four records with the values AB, CD, EF , and GH . When xargs reads these values, it combines them into one record of values separated by white spaces. This single record is passed as arguments to the command that is listed as xargs ' first non-option argument. If that command were rm , for example, Qshell would execute the following:
rm AB CD EF GH
If no command is given , echo is used by default.
Figures 12.28 and 12.29 give examples of xargs . In Figure 12.28, xargs effectively combines four records into one, passing the records as arguments to echo (the default command).
cat names.txt Joe Bill Bob Arlis /home/jsmith $ xargs Joe Bill Bob Arlis /home/jsmith $
Figure 12.28: Xargs combines multiple records into one and passes them to a utility.
grep print * bu.qsh: print -u2 "bu is not a directory - backup aborted." casewild.qsh: * ) print -u2 "Parameter 1 is invalid." demo.csh:print "Number of arguments: $#" demo.csh: printf "%3d (%s) " $argnbr "" donde.qsh:print "I am script
grep print * bu.qsh: print -u2 "bu is not a directory - backup aborted." casewild.qsh: * ) print -u2 "Parameter 1 is invalid." demo.csh:print "Number of arguments: $#" demo.csh: printf "%3d (%s) " $argnbr "$1" donde.qsh:print "I am script $0." /home/jsmith $ find $PWD - exec grep print {} ; print "I am script $0." print "here im is" * ) print -u2 "Parameter 1 is invalid." print -u2 "bu is not a directory - backup aborted." * ) print -u2 "Parameter 1 is invalid." print "Number of arguments: $#" printf "%3d (%s) " $argnbr "$1" print "I am script $0." /home/jsmith $ find . -type f -print xargs grep print ./bin/donde.qsh:print "I am script $0." ./bin/one.qsh:print "here i'm is" ./temp1/casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./bu.qsh: print -u2 "bu is not a directory - backup aborted." ./casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./demo.csh:print "Number of arguments: $#" ./demo.csh: printf "%3d (%s) " $argnbr "$1" ./donde.qsh:print "I am script $0."
." /home/jsmith $ find $PWD -exec grep print {} ; print "I am script
grep print * bu.qsh: print -u2 "bu is not a directory - backup aborted." casewild.qsh: * ) print -u2 "Parameter 1 is invalid." demo.csh:print "Number of arguments: $#" demo.csh: printf "%3d (%s) " $argnbr "$1" donde.qsh:print "I am script $0." /home/jsmith $ find $PWD - exec grep print {} ; print "I am script $0." print "here im is" * ) print -u2 "Parameter 1 is invalid." print -u2 "bu is not a directory - backup aborted." * ) print -u2 "Parameter 1 is invalid." print "Number of arguments: $#" printf "%3d (%s) " $argnbr "$1" print "I am script $0." /home/jsmith $ find . -type f -print xargs grep print ./bin/donde.qsh:print "I am script $0." ./bin/one.qsh:print "here i'm is" ./temp1/casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./bu.qsh: print -u2 "bu is not a directory - backup aborted." ./casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./demo.csh:print "Number of arguments: $#" ./demo.csh: printf "%3d (%s) " $argnbr "$1" ./donde.qsh:print "I am script $0."
." print "here im is" * ) print -u2 "Parameter 1 is invalid." print -u2 "bu is not a directory - backup aborted." * ) print -u2 "Parameter 1 is invalid." print "Number of arguments: $#" printf "%3d (%s) " $argnbr "" print "I am script
grep print * bu.qsh: print -u2 "bu is not a directory - backup aborted." casewild.qsh: * ) print -u2 "Parameter 1 is invalid." demo.csh:print "Number of arguments: $#" demo.csh: printf "%3d (%s) " $argnbr "$1" donde.qsh:print "I am script $0." /home/jsmith $ find $PWD - exec grep print {} ; print "I am script $0." print "here im is" * ) print -u2 "Parameter 1 is invalid." print -u2 "bu is not a directory - backup aborted." * ) print -u2 "Parameter 1 is invalid." print "Number of arguments: $#" printf "%3d (%s) " $argnbr "$1" print "I am script $0." /home/jsmith $ find . -type f -print xargs grep print ./bin/donde.qsh:print "I am script $0." ./bin/one.qsh:print "here i'm is" ./temp1/casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./bu.qsh: print -u2 "bu is not a directory - backup aborted." ./casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./demo.csh:print "Number of arguments: $#" ./demo.csh: printf "%3d (%s) " $argnbr "$1" ./donde.qsh:print "I am script $0."
." /home/jsmith $ find . -type f -print xargs grep print ./bin/donde.qsh:print "I am script
grep print * bu.qsh: print -u2 "bu is not a directory - backup aborted." casewild.qsh: * ) print -u2 "Parameter 1 is invalid." demo.csh:print "Number of arguments: $#" demo.csh: printf "%3d (%s) " $argnbr "$1" donde.qsh:print "I am script $0." /home/jsmith $ find $PWD - exec grep print {} ; print "I am script $0." print "here im is" * ) print -u2 "Parameter 1 is invalid." print -u2 "bu is not a directory - backup aborted." * ) print -u2 "Parameter 1 is invalid." print "Number of arguments: $#" printf "%3d (%s) " $argnbr "$1" print "I am script $0." /home/jsmith $ find . -type f -print xargs grep print ./bin/donde.qsh:print "I am script $0." ./bin/one.qsh:print "here i'm is" ./temp1/casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./bu.qsh: print -u2 "bu is not a directory - backup aborted." ./casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./demo.csh:print "Number of arguments: $#" ./demo.csh: printf "%3d (%s) " $argnbr "$1" ./donde.qsh:print "I am script $0."
." ./bin/one.qsh:print "here i'm is" ./temp1/casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./bu.qsh: print -u2 "bu is not a directory - backup aborted." ./casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./demo.csh:print "Number of arguments: $#" ./demo.csh: printf "%3d (%s) " $argnbr "" ./donde.qsh:print "I am script
grep print * bu.qsh: print -u2 "bu is not a directory - backup aborted." casewild.qsh: * ) print -u2 "Parameter 1 is invalid." demo.csh:print "Number of arguments: $#" demo.csh: printf "%3d (%s) " $argnbr "$1" donde.qsh:print "I am script $0." /home/jsmith $ find $PWD - exec grep print {} ; print "I am script $0." print "here im is" * ) print -u2 "Parameter 1 is invalid." print -u2 "bu is not a directory - backup aborted." * ) print -u2 "Parameter 1 is invalid." print "Number of arguments: $#" printf "%3d (%s) " $argnbr "$1" print "I am script $0." /home/jsmith $ find . -type f -print xargs grep print ./bin/donde.qsh:print "I am script $0." ./bin/one.qsh:print "here i'm is" ./temp1/casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./bu.qsh: print -u2 "bu is not a directory - backup aborted." ./casewild.qsh: * ) print -u2 "Parameter 1 is invalid." ./demo.csh:print "Number of arguments: $#" ./demo.csh: printf "%3d (%s) " $argnbr "$1" ./donde.qsh:print "I am script $0."
."
Figure 12.29: The third command uses xargs to solve the problems with the previous two commands.
In Figure 12.29, the first grep command looks for the word print in all files of the current directory. However, it does not search subdirectories. The second grep does search subdirectories; however, it does not tell which file contains which lines, and it runs slowly. The reason for these problems is that the find command launches a new instance of grep for each file name it finds. The third command solves both of these problems. The names of the files found are sent to xargs , which passes them en masse to one instance of the grep command, as if the user had typed one grep and listed all of the file names as arguments.
Theres an interesting side effect of the xargs utility as it relates to grep . As shown above, when grep is searching more than one file, it shows the name of the file containing the match. However, when using xargs , theres no guarantee that the last call to grep will have more than one file. For example, the find utility might find 29 file names, and xargs determines that 28 of them can fit on the first grep command line. The second grep command line generated by xargs will contain only one file. If a match occurs in that single file, grep will not show the file name.
A common solution to this is to use the null file /dev/null in the grep . The following xargs command guarantees there are always two or more files in the generated grep command:
find . -type f -print xargs grep print /dev/null
The Exec Utility
The exec utility is used primarily for controlling file descriptors, but it has another function: You may use it to replace an executing program with another program. No new process is created. This is similar to CLs Transfer Control command (TFRCTL) or the Unix-type C API that is also named exec().
When you run a command via exec , Qshell replaces the current copy of the Qshell interpreter with the command. When that program ends, control does not return to the Qshell interpreter. Instead, the process ends.
This matches the series of steps that Qshell goes through when creating a new process. The new process begins by running the shell, then uses exec to run the command passed to it, since there is no need to return to the shell.
Command Interpretation
Before Qshell can execute a command, it must make substitutions for certain tokens in the command. For example, all occurrences of the string $1 must be replaced by the value of the first positions parameter. This process, known as word expansion , consists of four steps:
- Tilde expansion, parameter expansion, command substitution, and arithmetic expansion
- Field splitting
- Path- name expansion (globbing)
- Quote removal
Tilde expansion, parameter expansion, command substitution, and arithmetic expansion all have the same precedence. They are done as follows :
- Tilde expansion Qshell replaces ~ and ~ user with the name of the users home directory. Qshell replaces ~+ with the value of the PWD variable.
- Parameter expansion Qshell replaces $ n and ${ n }with the value of the n th positional parameter. Qshell replaces $ var and ${ var } with the value of a variable. (Parameter expansion is discussed in chapter 6.)
- Command substitution As mentioned earlier in this chapter, Qshell executes a command that is surrounded by $(and) or backquotes and replaces the command with its output.
- Arithmetic expansion Qshell replaces arithmetic expressions surrounded by $((and)) with their calculated values. (Arithmetic expansion is also discussed in chapter 6.)
If the IFS variable is not null, Qshell uses the value of the IFS variable to divide the command into tokens. This process is called field splitting . There will not necessarily be a one-to-one correspondence between the number of delimited tokens in the original command and the number of fields resulting from field expansion, as an original token may be divided into more than one field.
Path-name expansion is more commonly known as globbing . This step is omitted if the noglob option has been set. (Globbing is discussed in more detail in chapter 14.)
The final step is to remove the single quote, double quote, and backslash quotation characters .
At this point, the command is passed to the Qshell interpreter for execution.
The Eval Utility
Qshells normal method of operation is to retrieve a command, make appropriate substitutions, and execute the result. The eval utility causes Qshell to make two passes through the command before execution.
The eval utility constructs a command string, which it then passes to Qshell for execution. It is similar in concept to the QCMDEXC or the C system() API, which executes a CL command stored in a string.
For example, consider Figure 12.30. To understand how this example works, execute the date command by itself. If the date is January 2, 2003, date sends the string sysYear=03 sysMonth=01 sysDay=02 to standard output. Notice that the string contains three perfectly good variable assignments. To get the system to execute those variable assignments, use command substitution and eval . Command substitution replaces the date command with its output, three assignment commands. The eval utility then passes the three assignments to Qshell for execution. The result is that the current year, month, and day are written to variables sysYear , sysMonth , and sysDay , respectively.
date +sysYear=%y sysMonth=%m sysDay=%d sysYear=03 sysMonth=09 sysDay=09 /home/jsmith $ eval $(date +sysYear=%y sysMonth=%m sysDay=%d) /home/jsmith $ print $sysMonth 01 /home/jsmith $ print $sysDay 02 /home/jsmith $ print $sysYear 03
Figure 12.30: This example combines command substitution and eval to assign and define three current date variables.
To view what steps eval is taking, set the xtrace option, as shown in Figure 12.31. This option causes Qshell to write each command to stdout before executing it.
set -x /home/jsmith $ eval $(date +sysYear=%y sysMonth=%m sysDay=%d) + date +sysYear=%y sysMonth=%m sysDay=%d + date +sysYear=%y sysMonth=%m sysDay=%d + eval sysYear=03 sysMonth=01 sysDay=02 + sysYear=03 sysMonth=01 sysDay=02 /home/jsmith $
Figure 12.31: Use the xtrace option to view the steps eval takes.
Summary
Qshell executes commands of several types: regular and special built-in utilities, reserved words, aliases, functions, and external files. These commands may be used alone or combined into compound commands.
Each command runs in a process, which is similar to an iSeries job. Some commands run in the current process, while others run in child processes under the shell that spawned them. Child processes do not automatically receive the values of the variables of their calling processes. The export utility causes shell to define variables in child processes.
To find a command, Qshell searches the directories listed in the PATH environment variable. The current directory is not searched unless it is listed in the path .
There are several ways to run a command: in background mode, through command substitution, in the current process under the source utility, through the xargs utility, and through the exec utility.
Before executing a command, Qshell substitutes values for certain tokens through the process of word expansion. The eval utility causes Qshell to make two substitution passes before executing the command.
Chapter 13 Functions
Категории