.MH "Tutorial" .SH "Commands" Input to the command interpreter consists of "commands". Commands, in turn, consist of a "command name", which is the name of an executable file. A command is executed simply by entering its name. For example, .be ] [bf help] .ee is a command that will describe how you can obtain online documentation. .pp Some commands may have arguments. Arguments are values supplied by you to the command. Arguments can be required or they may be optional in which case the system uses a default. In the above example when 'help' is invoked with no arguments the Subsystem assumes the command 'help help' (i.e. get me on-line documentation for the 'help' command). However, if you wanted on-line documentation for a specific command you would supply the command name as an argument, e.g. .be ] [bf help lf] .ee will describe the command that can be used to list information about files in a directory. Some commands may have options. Options are used to make the same command execute in slightly different ways. Options usually consist of one letter and are preceded by a dash. The command, .be 3 ] [bf help -f file] .ee will list the names of commands and subroutines that may be associated with the keyword "file". The "-f" is an option and "file" is an argument. Commands, arguments and options are separated from each other by blanks. .pp Here is a final example: .be 5 .in -5 ] [bf lf] adventure ee guide m6800 shell shell.doc subsys time_sheet words zunde ] .in +5 .ee 'Lf' is used to list the names of your files. Executed without any arguments, 'lf' prints the files in your current directory, but (like 'help') 'lf' may be used with or without arguments and options. .# .SH "How the Command Interpreter Locates a Command" Recall that you can access files by their entrynames only if they are located in your current directory. Without help from the shell this would also be true for commands. That is, in order to execute 'help' you would need to have a copy of the 'help' command in your current directory or you would have to enter its full pathname so that the shell could locate it in another directory. Obviously, neither alternative is desirable. In reality, the shell uses a "variable" called "_search_rule" to find commands like "help" in other directories. Each user has his own search rule. (Refer to the section in this guide entitled "Shell Control Variables" for more information.) The search rule tells the shell in what locations to look for commands, and if there is more than one location possible, it specifies the order in which the locations will be searched. .pp Most new users are given the search rule that causes the command interpreter to look for commands in the following five locations in the order shown: .fi .sp 1 .in +8 .ti -4 1.[bl 2]The shell's internal library for an internal command (e.g, 'stop', 'set') .ti -4 2.[bl 2]The user's variables currently stored in memory .ti -4 3.[bl 2]The user's current directory .ti -4 4.[bl 2]The Subsystem library containing locally supported external commands, "=lbin=" (e.g. memo, moot) .ti -4 5.[bl 2]The Subsystem library containing standard external commands, "=bin=" (e.g. 'lf', 'help') .sp 2 .in -8 This variable is explained in more detail in the "Application Notes" section of this guide. .pp Beware that this flexibility can get beginners (and some experienced users) into trouble. With the search rule above, the command interpreter will always look in your current directory for a command before it looks in one of the Subsystem command directories. Therefore, if you create a file having the same name as a command, the shell will try its best to execute the contents of that file. .SH "Special Characters and Quoting" Some characters have special meaning to the command interpreter. For example, try typing this command: .be 4 ] [bf echo Alas, poor Yorick] Alas poor: not found ] .ee 'Echo' is simply a command that types back its arguments. Obviously this example is not working as it should. The strange behavior is caused by the fact that the comma is used for dark mysterious purposes elsewhere in the command language. (The comma actually represents a null I/O connection between nodes of a network. See the section on pipes and networks for more information.) In fact, all of the following characters are potential troublemakers: .be [cc]mc | , ; # @ > | { } [ ] ( ) _ blank [cc]mc .ee The way to handle this problem is to use quotes. You may use either single or double quotes, but be sure to match each with another of the same kind. Try this command now: .be 3 ] [bf echo "Alas, poor Yorick; I knew him well."] Alas, poor Yorick; I knew him well. ] .ee You can use quotes to enclose other quotes: .be 3 ] [bf echo 'Quoth the raven: "Nevermore!" '] Quoth the raven: "Nevermore!" ] .ee .pp A final word on quoting: Note that anything enclosed in quotes becomes a .ul single argument. For example, the command .be ] [bf echo "Can I use that in my book?"] .ee has only one argument, but .be ] [bf echo Can I use that in my book?] .ee has seven. .SH "Command Files" Suppose you have a task which must be done often enough that it is inconvenient to remember the necessary commands and type them in every time. For an example, let's say that you have to print the year-end financial reports for the last five years. If the "print" command is used to print files, your command might look like: .be ] [bf print year74 year75 year76 year77 year78 year79] .ee If you use a text editor to make a file named "reports" that contains this command, you can then print your reports by typing .be ] [bf reports] .ee No special command is required to perform the operations in this "command file;" simply typing its name is sufficient. .pp Any number of commands may be placed in a command file. It is possible to set up groups of commands to be repeated or executed only if certain conditions occur. See the Applications Notes for examples. .pp It is one of the important features of the command interpreter that command files can be treated .ul exactly like ordinary commands. As shown in later sections, they are actually programs written in the command language; in fact, they are often called "shell programs." Many Subsystem commands ('e', 'fos', and 'rfl', for example) are implemented in this manner. .SH "Doing Repetitive Tasks --- Iteration" Some commands can accept only a single argument. One example of this is the 'fos' command. "Fos" stands for "format, overstrike, and spool." It is a shorthand command for printing "formatted" documents on the line printer. (A "formatted" document is one prepared with the help of a program called a "text formatter," which justifies right margins, indents paragraphs, etc. This document was prepared by the Software Tools text formatter 'fmt.') If you have several documents to be prepared, it is inconvenient to have to type the 'fos' command for each one. A special technique called "iteration" allows you to "factor out" the repeated text. For example, .be ] [bf fos (file1 file2 file3)] .ee is equivalent to .be 3 ] [bf fos file1] ] [bf fos file2] ] [bf fos file3] .ee The arguments inside the parentheses form an "iteration group." There may be more than one iteration group in a command, but they must all contain the same number of arguments. This is because each new command line produced by iteration must have one argument from each group. As an illustration of this, .be ] [bf (echo print fos) file(1 2 3)] .ee is equivalent to .be 3 ] [bf echo file1] ] [bf print file2] ] [bf fos file3] .ee Iteration is performed by simple text substitution; if there is no space between an argument and an iteration group in the original command, then there is none between the argument and group elements in the new commands. Thus, .be file(1 2 3) .ee is equivalent to .be 3 file1 file2 file3 .ee Iteration is most useful when combined with function calls, which will be discussed later. .SH "I/O Redirection" Control of the sources and destinations of data is a very basic function of the command interpreter, yet one that deserves special attention. The concepts involved are not new, yet they are rarely employed to the extent that they have been used in the Subsystem. The best approach to learning these ideas is to experiment. Get on a terminal, enter the Subsystem, and try the examples given here until they seem to make sense. Above all, experiment freely; try anything that comes to mind. The Subsystem has been designed with the idea that users are intelligent human beings, and their freedom of expression is the most valuable of tools. Use your imagination; if it needs tweaking, take a look at the Application Notes in the last chapter. .pp Programs and commands in the Subsystem do not have to be written to read and write to specific files and devices. In fact most of them are written to read from "anything" and write to "anything." Only when the program is executed do you specify what "anything" is, which could be your terminal, a disk file, the line printer, or even another program. "Anything"s are more formally known as "standard input ports" and "standard output ports." Programs are said to "read from standard input" and "write on standard output." The key point here is that programs need not take into account how input data is made available or what happens to output data when they are finished with it; the command interpreter is in complete control of the standard ports. .pp A command we will use frequently in this section is 'copy'. 'Copy' does exactly what its name implies; it copies data from one place to another. In fact, it copies data from its first standard input port to its first standard output port. .pp .ne 5 The first point to remember is that .ul by default, standard ports reference the terminal. Try 'copy' now: .be ] [bf copy] .ee After you have entered this command, type some random text followed by a newline. 'Copy' will type the same text back to you. (When you tire of this game, type a control-c; this causes an end-of-file signal to be sent to 'copy', which then returns to the command interpreter. Typing control-c to cause end-of-file is a convention observed by all Subsystem programs.) Since you did not say otherwise, standard input and standard output referred to the terminal; input data was taken from the terminal (as you typed it) and output data was placed on the terminal (printed by 'copy'). .pp Obviously, 'copy' would not be of much use if this was all it could do. Fortunately, the command interpreter can change the sources and destinations of data, thus making 'copy' less trivial. .pp .SH "I/O Redirection to Disk Files or Devices" Standard ports may be altered so as to refer to disk files by use of a "funnel." The greater-than sign (>) is used to represent a funnel. Conventionally, the ">" points in the direction of data flow. For example, if you wished to copy the contents of file "ee" to file "old_ee", you could type .be ] [bf ee> copy >old_ee] .ee The greater-than sign must always be immediately next to its associated filename; no intervening blanks are allowed. At least one blank must separate the '>' from any command name or arguments. This restriction is necessary to insure that the command language can be interpreted unambiguously. .pp The construct "ee>" is read "from ee"; ">old_ee" is read "toward old_ee." Thus, the command above can be read "from ee copy toward old_ee," or, "copy from ee toward old_ee." The process of changing the file assignment of a standard port by use of a funnel is called "I/O redirection," or simply "redirection." .pp It is not necessary to redirect both standard input and standard output; either may be redirected independently of the other. For example, .be ] [bf ee> copy] .ee can be used to print the contents of file "ee" on the terminal. (Remember that standard output, since it was not specifically redirected, refers to the terminal.) Not surprisingly, the last variation of 'copy', .be ] [bf copy >old_ee] .ee is also useful. This command causes input to be taken from the terminal (until an end-of-file is generated by typing a control-c) .nh and placed on the file "old_ee". .hy This is a quick way of creating a small file of text without using a text editor. .pp It is important to realize that .ul 100 all Subsystem programs behave uniformly with regard to redirection. .ul 0 It is as correct to redirect the output of, say, 'lf' .be ] [bf lf >file_list] .ee as it is to redirect the output of 'copy'. .pp Recall that special pathnames which begin with "/dev" may refer to peripheral devices. For example, by redirecting output to "/dev/lps" you can print a file on the line printer. .be ] [bf cat myfile >/dev/lps] .ee Although the discussion has been limited to one input port and one output port up to this point, more of each type are available. In the current implementation, there are a total of six; three for input and three for output. The highest-numbered output port is generally used for error messages, and is often called "ERROUT"; you can "capture" error messages by redirecting this output port. For example, if any errors are detected by 'lf' in this command .be ] [bf lf 3>errors] .ee then the resulting error messages will be placed on the file "errors". .pp .ne 6 Final words on redirection: there are two special-purpose redirection operators left. They are both represented by the double funnel ">>". The first operator is called "append:" .be ] [bf lf >>list] .ee causes a list of files to be placed .ul at the end of (appended to) the file named "list". The second operator is called "from command input." It is represented as just ">>" with no file name, and causes standard input to refer to the current source of commands. It is useful for running programs like the text editor from "scripts" of instructions placed in a command file. [cc]mc | See the Application Notes for examples. [cc]mc .SH "I/O Redirection to other Commands" The last section discussed I/O redirection --- the process of making standard ports refer to disk files or devices, rather than just to the terminal. This section will take that idea one step further. Frequently, the output of one program is placed on a file, only to be picked up again later and used by another program. The command interpreter simplifies this process by eliminating the intermediate file. The connection between programs that is so formed is called a "pipe," and a linear array of programs communicating through pipes is called a "pipeline." .pp Suppose that you maintain a large directory, containing drafts of various manuals. Each draft is in a file with a name of the form "MANxxxx.rr", where "xxxx" is the number of the manual and "rr" is the revision number. You are asked to produce a list of the numbers of all manuals at the first revision stage. The following command will do the job: .be ] [bf lf -c | find .01] .ee [cc]mc | "lf -c" lists the names of all files in the current directory, in a single column. [cc]mc The "pipe connection" (vertical bar) causes this listing to be passed to the 'find' command, which selects those lines containing the string ".01" and prints them. Thus, the pipeline above will print all filenames matching the conventional form of a first-revision manual name. .pp The ability to build special purpose commands cheaply and quickly from available tools using pipes is one of the most valuable features of the command interpreter. With practice, surprisingly difficult problems can be solved with ease. For further examples of pipelines, see the Applications Notes. .pp Combinations of programs connected with pipes need not be linear. Since multiple standard ports are available, programs can be and often are connected in non-linear networks. (Some networks cannot be executed if the programs in the network are not executed concurrently. The command interpreter detects such networks, and prints a warning message if they cannot be performed.) Further information on networks can be found in both the reference and applications chapters of this guide. .SH "I/O Redirection for a Group of Commands" It is sometimes necessary to change the standard port environment of many commands at one time, for reasons of convenience or efficiency. The "compound node" (a set of networks surrounded by curly braces) can be used in these situations. .pp As an example of the first case, suppose that you wish to generate a list of manual names (see the last example) in either the first or the second stage of revision. One way to do this is to generate the list for the first revision stage, place it on a file using a funnel, then generate a list for the second revision stage and place it on the end of the same file using an "append" redirector. A compound node might simplify the procedure thusly: .be ] [bf { lf -c | find .01; lf -c | find .02 } >list] .ee The first network finds all manuals at the first revision stage, and the second finds all those at the second stage. The networks will execute left-to-right, with the output of each being placed on the file "list," thus generating the desired listing. With iteration, the command can be collapsed even farther: .be ] [bf { lf -c | find .0(1 2) } >list] .ee This combination of iteration and compound nodes is often useful. .pp Efficiency becomes a consideration in cases where successive long streams of data are to be copied onto a file; if the "append" redirector is used each time, the file must be reopened and repositioned several times. Using a compound node, the output file need be opened only once: .be ] [bf { (file1 file2 file3)> copy } >all_files] .ee This complex example copies the contents of files "file1," "file2," and "file3" into the file named "all_files." .SH "I/O Redirection to a Command Argument" As mentioned before, some commands may have arguments. The standard output of a command (or a series of commands) can be used as an argument(s) by using the "function call" mechanism. For example, recall the situation illustrated in the section on pipes and networks; suppose it is necessary to actually print the manuals whose names were found. This is how the task could be done: .be ] [bf print [lf -c | find .01]] .ee The function call is composed of the pipeline "lf -c | find .01" and the square brackets enclosing it. The output of the pipeline within the brackets is passed to 'print' as a set of arguments, which it accesses in the usual manner. Specifically, .ul all the lines of output from the pipeline are combined into .ul one set of arguments, with spaces provided where multiple lines have been collapsed into one line. .pp 'Print' accepts multiple arguments; however, suppose it was necessary to use a program like 'fos', that accepts only one argument. Iteration can be combined with a function call to do the job: .be ] [bf fos ([lf -c | find .01])] .ee This command formats and prints all manuals in the current directory with revision numbers "01". .pp Function calls are frequently used in command files, particularly for accessing arguments passed to them. Since the sequence "lf -c | find pattern" occurs very frequently, it is a good candidate for replacement with a command file; it is only necessary to pass the pattern to be matched from the argument list of the command file to the 'find' command with a function call. The following command file, called 'files', will illustrate the process: .be lf -c | find [arg 1] .ee "arg 1" retrieves the first command file argument. The function call then passes that argument to 'find' through its argument list. 'Files' may then be used anywhere the original network was appropriate: .be 3 ] [bf files .01] ] [bf print [files .01]] ] [bf fos ([files .01])] .ee .SH "Variables" .nh It has been claimed that the command language is a programming .hy language in its own right. One facet of this language that has not been discussed thus far is the use of its variables. The command interpreter allows the user to create variables, with scope, and assign values to them or reference the values stored in them. .pp Certain special variables are used by the command interpreter in its everyday operation. These variables have names that begin with the underline (_). One of these is '_prompt', which is the prompt string the command interpreter prints when requesting a command. If you object to "]" as a prompt, you can change it with the "set" command: .be 3 ] [bf set _prompt = "OK, "] OK, [bf set _prompt = "% "] .fi % .bf set _prompt = "] " .nf ] .ee You may create and use variables of your own. To create a variable in the current scope (level of command file execution), use the "declare" command: .be ] [bf declare i j k sum] .ee Values are assigned to variables with the 'set' command. The command interpreter checks the current scope and all surrounding scopes for the variable to be set; if found, it is changed, otherwise it is declared in the current scope and assigned the specified value. .pp Variables behave like small programs that print their current values. Thus the value of a variable can be obtained by simply typing its name, or it can be used in a command line by enclosing it in brackets to form a function call. The following command file (which also illustrates the use of 'if', 'eval', and 'goto') will count from 1 to the number given as its first argument: .be 10 declare i set i = 1 :loop if [eval i ">" [arg 1]] goto exit fi i set i = [eval i + 1] goto loop :exit .ee Note the use of the "eval" function, which treats its arguments as an arithmetic expression and returns the expression's value. This is required to insure that the string "i + 1" is interpreted as an expression rather than as a character string. Also note that 'fi' terminates the 'if' command. [cc]mc | .pp When setting a variable to a string containing unprintable characters, you may use a special mnemonic form to prevent having to type the literal characters. For example .be set crlf = "" .ee sets the variable 'crlf' to a literal carriage return followed by a linefeed. There are times when this is not desirable, so to prevent the interpretation of the string, simply escape the start the start on the mnemonic with the Subsystem escape character (an '@'). To set set the variable 'crlf' to the literal string "" you would type .be set crlf = "@@" .ee The quotes in these two cases are necessary, otherwise the shell would try to interpret the '>' as an I/O redirector. If the string between the "<>" characters is not a legal ASCII mnemonic, no substitution will be made and the string will be passed unchanged. [cc]mc .SH "Interrupts, Quits and Error Handling Mechanisms" Normally, if you interrupt a program, it will terminate and the next thing you will see is the Subsystem's prompt for your next command. However, by defining the shell control variable "_quit_action" in your "=varsdir=/.vars" file, the fault handler will, upon detection of the interrupt, prompt you as to whether to abort the current program, continue, or call Primos. For program errors, the fault handler will always ask whether you want to abort the program, continue, or call Primos (regardless of whether "_quit_action" is defined or not). [cc]mc | The Application Notes discuss how to go about creating shell variables (which are kept in "=varsdir=/.vars" for storage between login sessions). [cc]mc .SH "Conclusion" This concludes the tutorial chapter of this document. Despite the fact that a good deal of material has been presented, much detail has been omitted. The next chapter is a complete summary of the capabilities of the command interpreter. It is written in a rather technical style, and is recommended for reference rather than self-teaching. The last chapter is a set of examples that may prove helpful. As always, the best approach is simply to sit down at a terminal and try out whatever you wish to do. Should you have difficulty, further tutorials are available, and the 'help' command can be consulted for quick reference.