WB2 and WB3 Tools and Tips    by Andrew Wong  Shell Tips  CONTENTS I. Shell Commands 1. CD Command with Wildcards 2. Displaying Directory Entries with List 3. Resident Names Instead of Aliases 4. Searching for a File with Search or List 5. LFORMAT Option in Eval and List 6. The Version and Which Commands Work Together 7. The More Program and the EDITOR Variable II. Features of the Shell 8. Using Command Line Editing Keys 9. Extending Text Selection in Shell Windows III. Command Arguments 10. Changing Colours and Attributes with Escape Codes 11. Command Templates with Other Arguments 12. A General Ask Command 13. Don't Quote Arguments to /F Template IV. Using the Shell 14. Escaping from the Normal Search 15. Substitutions 16. Variable Substitution 17. Position of Redirection Operators on the Command Line 18. Redirection with Console Windows and the "<>" Operator 19. The Rarely-Used Plus Sign 20. Summary of Special Characters and Tokens V. Script Files 21. Script File Templates and Parameter Substitution 22. Finding the Current Process Number in Scripts 23. Multi-way Branching in Scripts with Skip and EndSkip 24. Redirecting the Output of a Script File 25. Saving and Restoring the Resident List State for Scripts 26. Parameter and Command Substitutions Create Temporary Files I. SHELL COMMANDS 1. CD Command with Wildcards The CD command is not often used since changing the current directory can be done just by typing the name of the directory as if it were a command. As it happens, the shell invokes the CD command automatically, which can be seen if you turn echoing on: 1.Workbench:> set echo on 1.Workbench:> ram:t CD ram:t displayed by the shell 1.Ram Disk:T>   Hence, this technique is known as an implied CD. However, issuing the CD command explicitly can be useful as it allows you to specify the directory name with wildcards. For instance, to change to the SYS:Utilities directory: 1> cd sys:ut#? If more than one directory matches the pattern, you may need to make the pattern more specific. If a directory called SYS:Util also existed, for example, you would need to use the command: 1> cd sys:utili#? As another example, to change to SYS:Prefs/Presets: 1> cd sys:p#?/p#? An unusual quirk in the shell is that a null command is equivalent to the CD command. You can see this by typing Ctrl-@ (hold down Ctrl and type 2) and then pressing Return; the shell runs CD. The same thing happens if you issue the command "``": 1> `` ram:clip#? This is equivalent to: 1> cd ram:clip#? 2. Displaying Directory Entries with List Sometimes you may want to use the List command to display the details of a particular directory, but you don't want it to show you the files within. For instance, you may want to see the modification date and protection bits of the SYS:Devs directory, but the list sys:devs command displays the contents of the directory, which is not what you want. The trick is to give the directory name to List as a pattern. Any of the following commands: 1> list sys:(devs) 1> list sys:devs() 1> list sys: pat devs will give the desired result: Directory "sys:" on Sunday 14-Nov-93 Devs Dir ----rwed 14-Oct-93 19:49:38 1 directory - 1 block used 3. Resident Names Instead of Aliases The Resident command allows programs be given arbitrary names as they are made resident. For example, if you were to make the Dir command resident you could give the resident copy a different name, say d. 1> resident d c:dir Thereafter, the Dir command can be invoked by entering d. This has a similar effect to the usual practice of making Dir resident under its real name and then giving it an alias, thus: 1> resident c:dir 1> alias d dir If you're a UNIX user, you could use commands like these: resident . c:execute resident cat c:type resident cp c:copy resident mv c:rename resident rm c:delete 4. Searching for a File with Search or List Under the heading "Is My File Somewhere on This Disk?", the tutorial section of the AmigaDOS Manual invites the user to use the dir opt a command to look for a file on a disk. This would be a clumsy and slow procedure, especially on a hard disk, but there are two commands that can do the job properly, Search and List. Search is usually used to find a text string in a file or group of files. The following command looks for the strings "mystring" and "yourstring" in the files 1992.report and 1993.report: 1> search 199[23].report "(mystring|yourstring)" pattern With the FILE option, it searches for files in a directory. To locate all files on the Workbench disk whose names end in ".info": 1> search Workbench: #?.info all file pattern Any directory pattern can be used in place of the disk name that is used in these examples. If you use this regularly, you can define an alias that contains most of the keywords; the following sequence of commands is equivalent to the previous example: 1> alias ff search all file pattern 1> ff Workbench: #?.info The List command can also be used, with almost identical results: 1> list Workbench: pat #?.info all files lformat "%f%n" The Search command is faster, but List is much more flexible: you can look for directories with its DIRS option, or use its SINCE and UPTO options, or change the LFORMAT string. (See Section 5 for more about List's LFORMAT option.) 5. LFORMAT Option in Eval and List Two standard commands, Eval and List, have an LFORMAT option to specify a user-defined output format. When used in the List command, LFORMAT is useful for creating short scripts. Suppose, for example, you put all your utilities in SYS:Utilities. To keep the directory organised you create a series of subdirectories, with names like Disk, File, Graphics, Icon, Screen, Virus, and so on, to store the programs in. You can add all these subdirectories to your path by inserting the following two lines in the S:User-Startup file: list sys:utilities dirs lformat "path %s%s add" to t:p execute t:p The first command creates a script, t:p, that contains lines of the form: path sys:utilities/Disk add path sys:utilities/File add path sys:utilities/Graphics add . . . The second command executes the script. The LFORMAT string includes various substitution operators of the form "%X". The availability and meaning of these tokens depends on the command. The manual entry for List mentions that "You can put a length specifier and/or justification specifier between the percent sign (%) and the field specifier", but fails to explain how this is done. The general form of a formatting string for List is: %[flag][width.limit]field where the square brackets denote optional items, which are as follows: o flag is the minus sign (-), which specifies that the field is to be left-justified, otherwise it is right-justified; o width is the field width, in characters; if the first character is zero (0), the field is padded with leading zeros; o limit is the maximum number of characters that will be output; o field is one of A, B, C, D, F, K, L, N, P, S, or T, as described in the manual. Examples: 1> list printer.device lformat ">%20n<" > printer.device< 1> list printer.device lformat ">%-20n<" >printer.device < 1> list printer.device lformat ">%-20.7n<" >printer < 1> list printer.device lformat ">%010.7n<" >000printer< 6. The Version and Which Commands Work Together The Version and Which commands are designed to work together well. Version displays the version number, if available, of a command; Which shows the location in the search path of a particular command. At times, you may want to find the version number of a command for which you may not know the exact pathname. For example, if you want the version number of the DiskCopy command, but cannot remember its pathname, you could do this: 1> which diskcopy Workbench:System/DiskCopy 1> version workbench:system/diskcopy diskcopy 38.13 but you can use command substitution (see the article Command_Substitution) to do this is one step, as in: 1> version `which diskcopy` diskcopy 38.13 The designers of Version and Which have obviously decided that this kind of command would be used often. If a command has been made resident, or is an internal shell command, Which indicates it thus: 1> resident c:execute 1> which execute RES execute 1> which endshell INTERNAL endshell The words "RES" and "INTERNAL" in the output of Which correspond to switches of the same name in the template of Version; these switches allow Version to look in the right place to find the command. For example, the following command: 1> version `which execute` expands, after the command substitution processing, to: version RES execute which gives the result (on Workbench 2.1): execute 37.11 Without the RES switch, which was automatically supplied by Which, Version would have looked for a file called "execute" in the current directory and probably would not have found it. In an extra bit of cleverness, Which displays the following output for a disabled internal command: 1> resident fault remove 1> which fault INTERNAL fault ;(DISABLED) Hence, the command: 1> version `which fault` expands to: version INTERNAL fault ;(DISABLED) Which prints the word "DISABLED" to let you know that the command has been disabled, but it adds a semi-colon (the comment character) before it so that effectively it is removed when the output is substituted into the Version command line. If the semi-colon were not present, the command would expand to something like "version INTERNAL fault (DISABLED)" and Version would display an error message. (If you try these commands, the Fault command can be reactivated by typing "resident fault replace".) 7. The More Program and the EDITOR Variable As the manual states, "If the EDITOR environment variable is defined and you are using MORE from the Shell, you can bring up an editor to use on the file you are viewing (type Shift-E)." When you type "E", More appends the name of the current file to the contents of the EDITOR variable and executes the resultant string as an AmigaDOS command. For example, if the EDITOR variable was set to extras:tools/memacs, and the file being viewed was sys:s/spat, More would run the command: extras:tools/memacs sys:s/spat You can set any command line you like in the EDITOR variable. For example, you can have More print the current file when you press "E" by specifying the PrintFiles program: 1> setenv editor extras:tools/printfiles Another way to do this is with the Copy command: 1> setenv editor copy to prt: As another example, the following command allows More to sort the current file and display the results in a console window: 1> setenv editor sort to con:20/20/400/200/Sorted/auto/wait from If the file being viewed is df0:data, pressing "E" runs the command: sort to con:20/20/400/200/Sorted/auto/wait from df0:data (Based on a hint in Megadisc15:Feedback/Readers_Hints.) II. FEATURES OF THE SHELL 8. Using Command Line Editing Keys Section 3 in General_Tips describes the control-key sequences that can be used for editing in text gadgets. The shell recognises similar control-key sequences for editing the command line. In addition to the text gadget editing keys: o Ctrl-R and Ctrl-B are the same as Shift-Up-Arrow (search history buffer) and Shift-Down-Arrow (move to bottom of history), respectively. o Ctrl-K kills the region between the cursor and the end of the line, storing the text in a buffer. o Ctrl-Y yanks the text from the kill buffer (I'm not making up the "kill" and "yank": they're Emacs terms). This could be useful for duplicating text. For instance, suppose I've typed the following command line and have positioned the cursor as shown: 1> rename devs:system-configuration By pressing Ctrl-Y, Ctrl-K, Ctrl-K, I have the following: 1> rename devs:system-configuration devs:system-configuration  This makes it easier to rename long filenames. o The Left-Amiga key has no effect in a shell windows; it cannot be used to insert a literal control character. Section 4 in General_Tips includes a detailed list of all control-key sequences. 9. Extending Text Selection in Shell Windows You can use the mouse to highlight text in console windows (such as those used by the shell or programs like More) and then copy the text to the clipboard by pressing Amiga-C. The text can be pasted into another console window, or any other program that recognises the clipboard (if the ConClip command is running), by pressing Amiga-V. If you have highlighted a block of text and want to change the selection (that is, you want to select less or more text), you can do so by holding down the Shift key and dragging the mouse to the desired location. Consider the following example, which shows the output of the command dir sys:s on my system. I highlighting the text by dragging from point a, which I will call the anchor point, to point b. aux (dir) config (dir) obsolete (dir) revision (dir) std (dir) assignall assignsome base bgshell ce 1 checkmdicons convertlinkindex dbeep de dpat a emacs go1.3   gx last   legg ll   ls-F 2 lsfields   makelinkindex newshellcmd   pcd rcf   rcfclean rcfopen   remenu rhistory  b rpa shell shell-startup spat ss startup-sequence user-startup 3 user0-startup user1-startup whatiz wshell-startup xcmd Whilst holding the Shift key down: o Dragging the mouse in region 2 reduces the selection. o Dragging in region 3 enlarges the selection beyond point b. o Dragging in region 1 also enlarges the selection, but upwards, beyond point a. Point b becomes the new anchor point, so if the mouse was subsequently dragged in region 2, the text would be highlighted between point b and some point in region 2. Point a is no longer of any significance. o Dragging from either region 2 or 3 up into region 1 (for instance, by pointing at "pcd", then holding the left button down and dragging the mouse up to "ce") moves the selection area to region 1, meaning text would be highlighted between point a and some point in region 1. However, point a remains the anchor point. III. COMMAND ARGUMENTS 10. Changing Colours and Attributes with Escape Codes Some commands accept escape sequences within quoted arguments. The escape sequence can be either "*n" to represent a newline character, or "*e" to represent an escape character. The latter is often used to change the text colours or attributes: 1> echo "*e[1mbold*e[m *e[3mitalics*e[m *e[4munderline*e[m *e[7mreverse*e[m" bold italics underline reverse 1> echo "*e[32;43mcolours*e[m *e[42mcolours*e[m *e[32;41mcolours*e[m" colours colours colours Although these escape sequences are most often used with Echo and Prompt, they can also be used with Set, SetEnv, Eval and List (in their LFORMAT strings), and FileNote. For example, to attach a colourful, multi-line comment to a file: 1> filenote project.tex "*e[43mLaTeX source*e[m*n: Type *"latex project*" to process" 1> list project.tex project.tex 47772 ----rw-d 15-Oct-93 09:00:26 : LaTeX source : Type "latex project" to process 1 file - 2 blocks used To list files in bold: 1> list lformat "*e[1m%-25n*e[m %6l %a %d %t" Null-Handler 908 ---arw-d 26-Feb-89 20:11:16 PathHandler 4652 --parw-d 22-Nov-91 11:05:36 Port-Handler 1364 ---arw-d 03-Jun-92 07:40:03 Speak-Handler 4188 ---arw-d 04-Jan-92 16:02:04 WShellSeg 156 --parw-d 22-Nov-91 11:05:35 . . . To store escape sequences in shell variables: 1> set italics "*e[3m" 1> set plain "*e[0m" 1> echo "${italic}This is italic text$plain" This is italic text  (See Megadisc36A:Tutes_&_CLI/CliPrompts&Printing for more about escape sequences. See Section 16 about using shell variables.) 11. Command Templates with Other Arguments Most shell users will know that typing a question mark (?) after a command name causes the command's template to be displayed: 1> delete ? FILE/M/A,ALL/S,QUIET/S,FORCE/S:   Less well-known is that the question mark need not appear immediately after the command name; it can be typed after other arguments, as long as it is the last argument. If it is typed after other arguments, those arguments remain in effect, and additional arguments can be typed when the template appears. For example, the following commands copy the file my_data to a directory specified by the user: echo "Enter a directory to copy my_data to: " noline copy >nil: my_data ? The my_data argument is the source filename; whatever the user types in response to the Copy command template is added to the command line and becomes the destination filename. The >nil: operator suppresses all output, meaning that the template and other text normally shown by Copy (including any error messages) will not appear on the screen. The arguments of most AmigaDOS commands can be rearranged by explicitly using the keywords in their templates. This allows the question mark to prompt for any argument you like, even if would otherwise be in the middle of the command line and not at the end. For example, the Assign command is usually used thus: 1> assign myname: mydir Using the template keywords, this would be expressed as: 1> assign NAME myname: TARGET mydir If you wanted to prompt the user for a device name, you could do this: echo "Enter a logical device name: " assign >nil: TARGET mydir ? An interesting characteristic of templates is that arguments can be entered incrementally: 1> list ? DIR/M,P=PAT/K,KEYS/S,... : df0:devs ? DIR/M,P=PAT/K,KEYS/S,... : nodates ? DIR/M,P=PAT/K,KEYS/S,... : keys ? DIR/M,P=PAT/K,KEYS/S,... : nohead DOSDrivers [ 1224] Dir ----rwed Keymaps [ 1229] Dir ----rwed Monitors [ 1230] Dir ----rwed Printers [ 1231] Dir ----rwed DOSDrivers.info [ 1238] 632 ----rw-d . . . 12. A General Ask command Using the techniques described in the previous section, we can have scripts receive arbitrary input from the user, as an alternative to using the Ask command, which only allows the user to answer Y or N. We do this by using the template feature in conjunction with the Set command. For example: echo "What is your name? " noline set >nil: answer ? echo "Hey, my father was called $answer, too." In the second line, the variable name is specified as "answer". The question mark causes Set to print a template and prompt the user for additional arguments; these become the value of the variable. In the third line, "$answer" is replaced by the contents of the variable "answer" (see Section 16 on variable substitutions). 13. Don't Quote Arguments to /F Template Certain commands, such as Alias, Run, Set, and SetEnv have templates with arguments marked /F, denoting a final argument and meaning that the rest of the command line is taken as that argument. For instance, the template for Set is: 1> set ? NAME,STRING/F:   Everything after the first argument, which becomes assigned to NAME, is taken to be STRING. The manual advises that "quotation marks are not needed around the string, even if it contains spaces". In fact, putting quotation marks around the string may have a detrimental effect. The first example shows that if the STRING/F argument is quoted and extra spaces are typed at the end of the line, those spaces are included in the string: 1> set variable1 "first string" extra spaces at end of line 1> echo ">$variable1<" >first string < spaces were included This is particularly troublesome in scripts, where a comment might be added at the end of the line. The spaces between the closing quotation mark and the semi-colon that introduces the comment are included in the string: 1> set variable2 "second string" ; this is a comment 1> echo ">$variable2<" >second string < If the string is not quoted, extra spaces are ignored, which is probably what was intended: 1> set variable3 third string ; this is another comment 1> echo ">$variable3<" >third string< Arguments to /F templates do need to be quoted if they include leading or trailing spaces (that is, spaces at the start or the end of the string), but they should not be used otherwise. IV. USING THE SHELL 14. Escaping from the Normal Search When you enter a command, the shell searches for the program to execute as follows: o Aliases o Resident and internal commands o Search path For example, if you enter the command stack, the shell first checks to see if an alias for stack is defined and, if so, substitutes the value of the alias. If not, it looks for a resident or internal stack command. Finally, it searches the path (defined with the Path command) for an executable file "stack". You can partially defeat this search process by entering the command name differently: o To bypass an alias, enclose the command name in quotation marks. For example: 1> alias stack echo "This is an alias" 1> stack This is an alias 1> "stack" Current stack size is 4000 bytes In the third command, the "stack" alias was skipped and the internal Stack command was run instead. o To bypass aliases and resident commands to run a program in the current directory, add a slash (/) after the command name. For example, if there was an executable program called "stack" in the current directory, you would run it by typing "stack/". This also works when performing an implied CD (see Section 1): if there was a subdirectory of the current directory called "stack" you could change to it by entering either "cd stack" or "stack/". 15. Substitutions The shell performs three types of substitutions on its input. These are described below in the order in which they occur: o Alias Substitution If the first word of the command line matches the name of an alias, it is replaced by the text of the alias. If the alias string contains a "[]" sequence, the remaining arguments are substituted there, otherwise they are appended to the string. o Variable Substitution If a word on the command line begins with a dollar sign ($) and matches the name of a variable, the value of the variable is substituted. See the next section for more details. o Command Substitution A string enclosed in backticks (`cmd`) is executed as a separate command in a subshell. The output from the command, with tabs and newlines removed, is substituted for the original string. This is described in more detail in Command_Substitution. To demonstrate that the substitutions are indeed performed in this order, consider the following commands: 1> alias showtime *$vname 1> set vname echo "*`date*`" len 8 The first command makes showtime an alias for the string $vname; the second makes vname a variable with the value echo "`date`" len 8. The asterisks are needed so that the dollar sign and backticks are retained; without them, variable and command substitution would be performed immediately (AmigaShell_Quoting has more details on this). If we enter the command showtime: 1> showtime the shell performs alias substitution and replaces the command name with the string $vname: $vname The shell then performs variable substitution and replaces the variable name with its value: echo "`date`" len 8 The shell then performs command substitution by running the Date command and replacing the backticked string with its output: echo "Saturday 20-Nov-93 01:05:28" len 8 Finally, the shell executes the fully-transformed command line to give the following output: 1> showtime 01:05:28 16. Variable Substitution The manual refers to shell variables as "local environment variables" and environment variables as "global environment variables". To set, remove, and display the value of shell variables, use the Set, UnSet, and Get commands. For environment variables, use the SetEnv, UnSetEnv, and GetEnv commands. The value of a variable can also be retrieved by preceding its name with a dollar sign ($) anywhere on the command line. If a local (shell) or global (environment) variable of that name exists, its value is substituted for the variable name, otherwise the text is left unchanged. A local variable has precedence over a global one. The name of the variable can be enclosed in braces. This is needed for the following situations: o Referencing variable names containing non-alphanumeric characters: variable names are usually composed of letters (A-Z and a-z) and digits (0-9), but other characters can also be used: 1> set my_var.name! any string 1> echo "${my_var.name!}" any string 1> set "spaced out" another string 1> echo "${spaced out}" another string o Isolating variable names from surrounding text: if the character immediately after a variable name is a letter or digit, the variable name must be distinguished from the following text by braces: 1> set myvar sys:locale/ 1> echo "${myvar}countries" sys:locale/countries If braces were not used: 1> echo "$myvarcountries" $myvarcountries Since the shell performs variable substitution before command substitution (as described in the previous section), variables can contain "backticked" commands. For example: 1> set tick `date 1> echo "The date is ${tick}`" which the shell expands to: echo "The date is `date`" resulting in the following output: The date is Tuesday 16-Nov-93 19:17:44 17. Position of Redirection Operators on the Command Line The manuals insist that the redirection operators, "<", ">", and ">>", must be specified immediately after the command name and before any arguments. In fact, they can usually be typed anywhere after the command name; in normal use, is it often convenient to put them at end: 1> copy devs:#?.device df0: >nil: However, this may not always work. Commands with /F arguments in their templates will consider the redirection operator part of the final argument: 1> set variable any_string >nil: 1> echo "$variable" any_string >nil: For this reason, it is best to stick to the official method: 1> set >nil: variable any_string 1> echo "$variable" any_string The Run command is an exceptional case because its argument is a command line that may itself contain redirection operators. A Run command line may therefore include two sets of redirection operators, thus: run run_out command cmd_out arguments The second set of operators corresponds to the redirection you would normally use even if you were not using Run to start the program. The first set of operators is almost always "nil:" which has the effect of preventing the string "[CLI n]" from appearing on the screen; and of detaching the program from the shell so that the window can be closed even before the program is terminated. 18. Redirection with Console Windows and the "<>" Operator The output of a command can be redirected to a console window with an operator like >CON: - a new window is opened and the output of the command appears in that window. However, the window usually disappears immediately afterwards, before giving you a chance to peruse its contents, making this technique seemingly of limited use. The console window WAIT flag can be used to rectify this. Console windows in System 2.04 and higher have the general form: CON:left/top/width/height/title/flags where flags is one or more of the keywords AUTO, BACKDROP, CLOSE, NOBORDER, NODRAG, NOSIZE, SCREEN, SIMPLE, SMART, WAIT, and WINDOW. (See Section 6 of General_Tips about another option, ALT.) If title contains any spaces, the whole specification string must be enclosed in quotation marks. If you give the WAIT keyword, the window will not automatically close when it stops receiving input, but waits for you to close it by pressing Ctrl-\. Thus, the following example displays in a separate window the contents of the current directory, and keeps the window open until you close it: 1> dir >con:20/30/400/300/MyOutput/wait However, if the command produces no output and you redirect its output to a console window in the same way, am empty, unwanted window opens regardless. To prevent this, use the AUTO keyword, which causes the window to open only when it needs to display output. Hence the following command runs without opening any window: 1> echo >con:20/30/400/300/MyCon/wait/auto The AUTO keyword also has the effect of adding a close gadget to the window, so you can close any window that does appear by either pressing Ctrl-\ or clicking on the close gadget with the mouse. A close gadget can also be requested by an explicit CLOSE keyword. If you use this technique often - for example, for keeping directory listings on the screen while you perform file management - you may find it convenient to assign the console specification to a shell variable: 1> set window con:20/30/500/200/Output/auto/wait 1> dir > $window You can also use a console window as the source of input to a command by using the "<" operator. The following example opens a small window for user to type into; note that the WAIT keyword is not used here because we want the window to disappear once the user presses Return: 1> set >nil: <"con:50/100/300/50/Enter Your Name" namevar ? But now consider a command that both produces output and solicits input, such as the Dir command with the INTER switch. In this first example, a window is opened that displays the output of the Dir command, but the user must enter input into the original shell window: 1> dir inter >con:30/50/400/100/MyDirOutput/auto/wait In the next command, a window is opened to receive input from the user, but the output of Dir appears in the original shell window: 1> dir inter " operator to accomplish this: the "<>" syntax is for use especially for console windows and specifies that both input and output are to be redirected to a console window: 1> dir inter <>con:50/50/500/200/InputandOutput 19. The Rarely-Used Plus Sign The "crib card" included within the packaging for Megadisc 32 describes numerous special characters used by the shell and some shell commands. It describes the plus sign (+) as being "used exclusively with the RUN command", but that is untrue. Although the plus sign is almost always associated with the Run command, it is a special character of the shell, and not of Run itself. The example given by the crib card of using the plus sign with Run is a good one: Run wait until 16:00 + Echo "Time to put the potatoes on! The time is now: " NOLINE + Date This would create a background shell that would execute the Wait, Echo, and Date commands in sequence. The background shell exits when the commands finish execution, shortly after 4 pm. What the plus sign actually does is provide a way to insert newline characters into the command stream. Usually, pressing the Return key terminates user input and sends the contents of the command line to the shell for processing. However, if you end a line with a plus sign before pressing Return, the cursor moves to the next line, but the shell does not yet begin interpreting the command line, waiting instead for you to supply additional input. The plus sign is removed, but the newline character remains as part of the command line. Hence, the plus sign is an escape character for the newline. You can see this for yourself by running a command that performs no parsing on its arguments, such as a simple in-line ARexx program. Try the following command (type in all three lines): 1> rx "say 'My args are:'; say arg(1)" Enter any + arguments you like to + the ARexx program The following output will be displayed: Enter any arguments you like to the ARexx program When you give a list of commands to the Run command, ending each one with a plus sign, Run receives as its arguments the commands you specified separated by newline characters (that is, on separate lines). It then executes each one in turn as if you had typed them into the shell yourself, pressing Return after each command. A very useful application of the plus sign is in using in-line ARexx programs with shell script files. Please see the ARexx_and_AmigaDOS article in this drawer for details, but here's a taste of what is possible: 1> rxlib rexxsupport.library 0 -30 0 1> rx "parse arg dir + call pragma('d', dir) + files=showdir(dir, 'f') + do i=1 to words(files) + file=word(files, i) + if right(file, 5)='.info' then + address command 'rename' file upper(file) + end" sys:wbstartup This would convert to upper-case the names of all files in the SYS:WBStartup directory whose names end in ".info". 20. Summary of Special Characters and Tokens Here is a complete list of all special symbols used by the shell and standard shell commands. Each character or token has been categorised into one of the following types: o Shell: Recognised by the shell o Arguments: Recognised when given as an argument to some commands o Patterns: Used in pattern matching to specify files or strings o Paths: Recognised by the file system when forming pathnames o Scripts: Used in shell script files Symbol Form Type Description ~~~~~~ ~~~~ ~~~~ ~~~~~~~~~~~ space Shell Word separator. tab Shell Word separator. " "word" Shell Quoting character. ; Shell Comment character. $ $var Shell Variable substitution of var. $ <a$b> Scripts Supplies a default, b, for parameter a. $$ <$$> Scripts Current process number. $$ Shell Null string. { } ${var} Shell Variable substitution of var. ` `cmd` Shell Command substitution of cmd. + Shell At end of line, escape character for newline. < <file Shell Redirect input from file. > >file Shell Redirect output to file. >> >>file Shell Redirect and append output to file. <> <>con Shell Redirect input and output to console con. * Shell Escape character for dollar signs ($) and backticks (`). * Arguments In quoted strings, escape character for quotation marks (") and asterisks (*). * Arguments In quoted strings, introduces escape sequences *e and *n. * Arguments Denotes the current console window. * Patterns Wildcard; same as #? (only if enabled by a program like WildStar on Megadisc 26). "" Arguments Denotes the current directory. = Arguments Joins template keyword and argument. ? Arguments Requests the template for a command. ? Patterns Single character. # #pat Patterns Repetition of pat. ( ) (pat) Patterns Grouping of pat. | (a|b) Patterns Alternation of a or b. ~ ~(pat) Patterns Negation of pat. ~ [~chr] Patterns Inversion of character class chr. [ ] [chr] Patterns Character class chr. - [a-b] Patterns Character range from a to b. % Patterns Null pattern. ' Patterns Escape character for pattern symbols. / Arguments Separates tokens in CON: specifications. / Paths Directory name separator. / Paths Parent directory. : Paths Device name terminator. : Paths Root directory. . Scripts Introduces dot commands. < > <ARG> Scripts Parameter substitution of ARG. The following symbols are specific to certain AmigaDOS commands: Symbol Command(s) Description ~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~~ [] Alias Argument substitution. % Prompt Introduces special tokens. % Eval, List Introduces tokens for LFORMAT. + Protect Add protection bits. + Eval Addition. - Protect Remove protection bits. - Eval Subtraction. - Eval Unary minus. - Date, SetDate, List Word separator in date string. : Date, SetDate, Wait Word separator in time string. # Eval Denotes octal type. #X Eval Denotes hexadecimal type. ' Eval Denotes character type. * Eval Multiplication. / Eval Division. & Eval Bitwise AND. | Eval Bitwise OR. ~ Eval Bitwise NOT. << Eval Left shift. >> Eval Right shift. V. SCRIPT FILES 21. Script File Templates and Parameter Substitution Script files often start with a .KEY directive, which specifies the template that arguments to the script must follow. The arguments listed in the template are denoted in the script by angle brackets, as in "". The less-than and greater-than signs can be replaced by different characters with the .BRA and .KET directives. The manual mentions that each argument in the template can optionally be qualified with a /A or /K code, but it two other codes, /S and /F are also valid. The following codes, shown alongside an example argument ARG, can be used in script file templates: o ARG/A means the argument is always required. The user supplies a value to be substituted for ARG. Within the script, the token "" is replaced by the value that the user entered. o ARG/K means that if the argument is given, the keyword must be given along with the argument itself. So an argument my_arg would have to be supplied as ARG my_arg. Within the script, the token "" is replaced, in this case, by my_arg. o ARG/S denotes a switch. The argument is either ARG or nothing. Within the script, the token "" is replaced by either ARG or a null string. o ARG/F means the rest of the command line is taken to be ARG. Within the script, the token "" is replaced by whatever arguments were left on the command line. The codes can sometimes be combined. The following script requires a FILE argument, which must be given along with the FILE keyword, and will accept a VERBOSE switch. Any other arguments are taken to be STRING. .key FILE/A/K,VERBOSE/S,STRING/F .bra { .ket } echo "My file is *"{FILE}*"" echo "My string is *"{STRING}*"" if "{VERBOSE}" not eq "" echo "You specified the verbose option" endif If run with the command line: 1> scriptname file l:queue-handler verbose rainy day women the following output appears: My file is "l:queue-handler" My string is "rainy day women" You specified the verbose option The following template codes do not work under System 2.1: o ARG/N is meant to denote a numeric argument. If specified in a template, the script will indeed insist that the argument is a number, but no text is substituted for "". o ARG/M should allow multiple arguments to be specified, but instead causes garbage to be substitued for "". o "=" is not a code in itself, but separates alternative forms of a keyword in commands like Copy, List, and Resident. This syntax is not supported for script file templates. (For an introduction to script files, please see the articles Megadisc36A:Tutes_&_CLI/Script_Files, Megadisc38B:Tutes_&_CLI/PowerScripting, and Megadisc6:Articles/BatchFiles. Note, however, that the MD36 article contains some inaccuracies, such as its reference to an "ELSE IF" command, which does not exist.) 22. Finding the Current Process Number in Scripts The manual states that, "The current Shell number can be referenced by the characters <$$>. This is useful in creating unique temporary files, logical assignments, and PIPE names." To clarify this advice: o The substitution delimiters, "<" and ">", may differ depending on whether the script contains .BRA and .KET directives. If the script contains the lines: .bra [ .ket ] then the process number is referred to by "[$$]". o The process number is only available in this way if a .KEY directive has been defined. In a simple script file with no "dot" commands, the token "<$$>" will yield the string "<>". If you want to use "<$$>", but don't require any arguments to be passed to the script, the following command at the beginning of the script will suffice: .key "" (However, see Section 26 for a warning about including superfluous dot commands in your scripts.) o Regardless of whether or not a .KEY directive has been defined, you can retrieve the current process number from the shell variable process with: $process or ${process} The first form is acceptable if the variable is not followed immediately by any alphanumeric characters. The second form is needed if the variable name has to be isolated from any surrounding text. However, if you use the second form and you have defined a template with .KEY, make sure the substitution delimiters are not currently set to "{" and "}". Section 16 explains variable substitution in more detail. 23. Multi-way Branching in Scripts with Skip and EndSkip An unfortunate aspect of the shell script language is that there is no "else if" statement, the next closest thing being the use of nested If commands. This results in clumsy constructions like: if condition1 command1 else if condition2 command2 else if condition3 command3 endif endif endif If an "else if" command were available, this might have be written as: ; not possible with the current script language if condition1 command1 else if condition2 command2 else if condition3 command3 endif Nested If commands may be all right for only a few conditions, but what if there were several dozen choices, such as in a script that presented the user with a menu of selections? An alternative is to use Skip to branch to a series of labels. This works best if all the possible labels are known in advance. For example, suppose the command my_command set the local variable choice to either 1, 2, or 3. The following script shows how the branching could be accomplished: my_command skip $choice lab 1 echo "Your choice is 1" skip end lab 2 echo "Your choice is 2" skip end lab 3 echo "Your choice is 3" skip end lab end (This is demonstrated in the dirs script in the Directory_Stack drawer.) However, extra work is required if not all choices are known in advance. In a menu program, for example, the user would be asked to enter a choice, and the script would attempt to branch appropriately. But if the user typed in an incorrect answer, a script such as the one above would simply abort with the message "object not found" because Skip wouldn't be able to find a label to branch to. This is where the EndSkip command comes in useful. The EndSkip command, says the manual, "is used in scripts to terminate the execution of a SKIP block". In other words, when the script encounters an EndSkip, it immediately stops Skip'ing and resumes execution after the EndSkip line. We can use EndSkip to provide a default case for the Skip command in our script. Our initial attempt to create a menu script might look like this, but the following example has a bug: ; menu script example, doesn't work properly yet lab start echo "This is a menu. You may choose A or B." echo "Please enter a selection: " noline set >nil: answer ? See Section 12 for an explanation of this skip $answer lab A echo "You chose item A from the menu" skip end lab B echo "You chose item B from the menu" skip end endskip echo "You didn't choose correctly. Try again" skip back start lab end echo "Script finished" The endskip line is intended to deal with the case where the user did not select a recognised option. For instance, if the user chose "C", the command skip $answer would become skip C, but no corresponding label would be found, so the script prints an error message. Can you see the bug? The problem is with the lines that say skip end; in fact, they will never reach their intended destination (the lab end line) because they will be interrupted by the endskip command. The result is that the error message, "You didn't choose correctly", will appear after every selection whether or not the user chooses correctly. There are two ways to solve this problem: o We could substitute the Quit command for the skip end lines. This is not a general solution, however, as we may not want to exit the script immediately after a menu selection is received and processed. o We can introduce a flag (a variable) that is set when a correct menu selection is made. Whether or not the error message is printed will then be determined by the state of that flag. Here is the modified script: ; correctly-working example of a multi-way branching script lab start echo "This is a menu. You may choose A or B." echo "Please enter a selection: " noline set >nil: answer ? skip $answer lab A echo "You chose item A from the menu" set goodchoice 1 skip lab B echo "You chose item B from the menu" set goodchoice 1 skip endskip if $goodchoice not eq 1 echo "You didn't choose correctly. Try again" skip back start endif echo "Script finished" You may be wondering why skip back start works and why it doesn't get interrupted by the endskip command a few lines above it. The reason is that skip back doesn't look for a label by searching backwards from the current position: instead, it jumps back through the script as far as possible and begins searching forwards from there. Usually it jumps back to the start of of the script, but if there are any Execute commands in the script, it will only jump back as far as the last such command. (See the references at the end of Section 21 for introductory articles on script files.) 24. Redirecting the Output of a Script File You cannot redirect the output of a script file in the same way that you would for a program. For instance, if you had a script called batch, you could not suppress its output by typing: 1> batch >nil: Instead, you would have to edit the script file and add a ">nil:" directive after every command that produced output - hardly convenient. An alternative is to execute the script file through an inline ARexx macro; you can then redirect the output of that macro. The easiest way to do this is to define the macro as an alias (type the command on one line): 1> alias x rx "options failat 21; address command 'execute' arg(1)" You can then do: 1> x batch >nil: or, in general: 1> x myscript arg1 arg2 arg3 >outfile However, be aware that the script file will be executed in a separate shell process, so if it contains commands like CD, Path, Prompt, Set, Stack, or any others that affect values specific to each shell, they will not have any effect on the current shell. 25. Saving and Restoring the Resident List State for Scripts Some shell scripts, such as installation scripts, make commands like Copy and Delete resident to improve speed and reduce disk swapping on floppy-disk-based systems. Typically, the commands are removed from the resident list at the end of the script. But what if those commands were already resident before the script was started? In such a case, the script would be making the commands resident unnecessarily and, worse, it would be erroneously removing them from the resident list. The following method allows a script to make a command resident and later remove it from the resident list only if it was not already resident. It uses the Which command with its RES option to determine whether or not a command is resident. which >nil: res command if warn resident command else set cmdres 1 endif main body of script goes here if $cmdres not eq 1 resident command remove else unset cmdres endif 26. Parameter and Command Substitutions Create Temporary Files You may have noticed that the T: directory, which is set aside for holding temporary files, sometimes contains files with names like Command-00-T01. These files are created by the Execute command whenever it runs a script file that contains dot commands. Simple scripts, with no dot commands, do not cause temporary files to be created; but if you use any of .KEY, .BRA, .KET, .DEF, .DOT, .DOLLAR, or even ". " on its own, the Execute command will scan the source file, perform any substitutions required, save the result to a temporary file in T:, and then run the sequence of commands stored there. For this reason, although you can use ". " as a comment character (provided any such comments are at the top of the script file), you should not do so unless you have used other dot commands in the script. If there are no other dot commands, using a semi-colon (;) to introduce comments will prevent a temporary file from having to be created; thus, execution speed will be improved slightly. In the filename Command-00-T01, the last two digits denote the process number of the shell executing the script. The two digits in the middle of the name are usually "00" but are incremented when necessary in order to specify a unique filename. For example, if a script file includes an Execute command to run a second script, a second temporary file will be created, called T:Command-01-T01. Using command substitution, in back apostrophes (`...`), also causes temporary files to be created. The filenames have the form T:tick$1, where the number after the dollar sign is the process number of the shell executing the command. In the case of nested command substitutions, two temporary files are created: one for the current shell, and one for the subshell process that is spawned to execute the command enclosed in the outermost set of back apostrophes. For example, assuming process 3 is the next process number available, the command: 1> echo "`echo "*`date*`" len 18`" first 1 len 9 24-Sep-93 will create the temporary files T:tick$1 and T:tick$3. (See Command_Substitution for more details on this technique.)