@DATABASE Shell.guide @MASTER Shell.guide @$VER: Shell.guide 45.3 (7.4.2002) @(C) ©2002 by Amiga, Inc., HAAGE&PARTNER and THOR-Software @SMARTWRAP @AUTHOR Thomas Richter @INDEX Index @NODE MAIN "Shell Guide" @{b}@{u}@{fg shine}The V45 Shell Manual@{fg text}@{uu}@{ub} @{"Introduction: What is this about? " link Intro} @{"New Features: Changes in the V45 Shell " link News} @{"New commands: Changes in the command set of the V45 Shell " link Commands} @{"Shell Syntax: The shell command line language " link Language} @{"History : What happened before " link History} @{"Index " link Index } @{BODY} @ENDNODE @NODE Intro "Introduction: What is this about?" @TOC MAIN The shell, or the CLI how it has been formerly called, is the command line interpreter of the AmigaOs, and therefore one of the two basic user interfaces the Amiga has to offer: The graphical Workbench, and the text-based shell that is opened by the "Shell" icon on the workbench. The shell reads command lines, one at a time, either from a special window called "the console" and provided by console handlers like CON: or ViNCEd, or from a script or "batch" file, of which the "Startup-Sequence" is the most popular known. None of this is of course new in the V45 shell, and this guide will not give a complete introduction into how to use the shell. You'll find examples and tutorials how to operate the shell in the "AmigaDos" Manual you got with your computer. However, the shell has been reworked and enhanced a lot with V45, and a couple of @{"new features" link News} have been introduced. Further, @{"new commands" link Commands} have been added, and old commands have been enhanced. This manual also describes the @{"syntax" link Language} of the shell language as used in batch files and in the console; strictly speaking, the syntax did not change with V45, or got only enhanced very carefully to guarantee backwards compatibility, but it has never been written down in closed form. And, considering how the V40 shell sources used to look like, has never been programmed in a closed form as well... all this changed in V45. @ENDNODE @NODE NEWS "New V45 Shell features" Quite some new features have been added in the V45 release of the Shell. The following will describe these features one after another: @{"Check variables for existance : The $? and $?? operators" link VarEx} @{"Recursive Alias Expansion " link RecAlias} @{"Multiple Backticks " link BackTicks} @{"Launch commands, job control : The & operator " link RunBack} @{"The revival of the h protection bit " link HBit} @{"Alternative script interpreters: The #! and ;! tokens " link HashBang} @{"Datatypes support " link DTypes} @{"Extended redirection support : <<, *>, *>> and *>< " link Redirect} @{"Miscellaneous : $STACK: etc... " link Misc} A detailed description of @{"Shell Syntax for Experts" link Language} is also available. @ENDNODE @NODE VarEx "Check Variables for Existance" @TOC NEWS The V45 shell adds two new operators for variable expansion, "$?" and "$??". The first is used to check whether a local shell or global environment variable has been defined, the second checks whether a variable exists as a global environment definition. Similar to the V40 Shell, local (shell) or global (environment) variables are specified on the command line by prepending the variable name with a "$" sign. Hence, @{CODE} echo $a @{BODY} prints the contents of the variable "a", should it be defined. Optionally, the variable name can be enclosed in braces should it contain blanks or other non-alpha-numeric characters. Hence, the above is equivalent to: @{CODE} echo ${a} @{BODY} Should "a" be undefined, the "$" sign stands for itself and is not regarded as a special character: The first example would print "$a" and the second would "${a}" for undefined a. The V45 shell offers now a method to check whether a variable exists before trying to use it. Even though the advanced freak should be able to implement the very same checks by some trickery without using the new syntax, the V45 method is easier to read and straight-forward: The "$?" operator followed by a variable name expands to "0" should the variable be undefined, or to "1" should the variable exist already. The "$??" operator, however, will expand to "1" only if the following variable name exists as a global environment variable. It will remain zero for local or undefined variables. Hence, to elaborate on the above example, @{CODE} echo $?a @{BODY} prints "0" for a undefined, and "1" otherwise. Similar to above, the braces can be used to enclose variables of "unusal" names. @{CODE} echo $??a @{BODY} prints "1" only if "ENV:a" exists, hence a is a global variable. See also: @{"Details: The variable expansion syntax" link VarSyntax} @ENDNODE @NODE RecAlias "Recursive Alias Expansion" @TOC NEWS The shell "alias" feature allows the definition of short command "macros" that builds new commands on top of old. The line @{CODE} alias ls list @{BODY} defines a new command "ls" that is functional equivalent to "list". Aliases may also re-arrange arguments, see @{"Details: Alias syntax" link AliasSyntax}. In V40 and below, the body of an alias must be a real existing command and could not be an alias again. In V45, this condition was relaxed and the body of an alias could be another alias which is recursively expanded: @{CODE} alias lq ls quick @{BODY} would introduce a new command "lq" that is equivalent to "ls quick", which is again equivalent to "list quick". The above would not work in V40. To avoid endless alias expansion should the alias definition contain circular dependencies, an alias is expanded @{B}at most once@{UB} within one command line invocation. Alias expansion is suppressed if the command is enclosed in quotes. Therefore, quoted commands always refer to executable binaries and never to aliases. See also: @{"Details: Alias syntax" link AliasSyntax} @ENDNODE @NODE BackTicks "Multiple Backticks" @TOC NEWS The Shell backtick operator encloses a command that is executed on interpretation of a command line and whose output then substitutes the backtick sequence. For example, @{CODE} type `list #?.txt` @{CODE} would display the contents of all files in the current directory whose name ends on ".txt" using the "type" command. On V40 and below, at most one pair of backticks was expanded on the command line, the remaining backticks remained unexpanded and were considered as literal characters. In V45, all backtick sequences are expanded at once. @{CODE} type `list #?.txt` `list #?.guide` @{BODY} would display all files ending on ".txt" or ".guide". This example would not work for V40. @{B}NOTE:@{UB} It might happen that some shell scripts depend on this feature of the V40 shell by specifying an empty pair of backticks `` to suppress expansion of a further pair. In V45 (or in V40 as well, as this method is backwards compatible), add a * in front of a backtick sequence to suppress its expansion. This is especially of importance should the backtick be part of an alias: @{CODE} alias displaytext type *`list #?.txt*` @{BODY} would define an alias that displays the contents of all .txt files. Note that we must here "escape" the backtick to avoid its expansion for the alias definition already: The backtick should be part of the alias definition, and not expanded before the alias is defined. See also: @{"Details: Backtick syntax" link BackSyntax} @ENDNODE @NODE RunBack "The Shell Job Control and the & Operator" @TOC NEWS The "&" operator is completely new for the V45 shell; it is a convenient replacement for the "run" command that launches commands while keeping the shell available for further command input. Should the shell find a single & sign on the command line that is not enclosed in quotes and surrounded by blank spaces, the & gets removed and the remaining command is "run" almost as if the "run" command has been placed in front of it. For example, @{CODE} MultiView Shell.guide & @{BODY} would display this guide by MultiView, returning to the shell immediately. There are two minor differences between "run" and &, however: The & operator does not cut the command input stream off, hence the following command line @{B}will@{UB} get input properly: @{CODE} ask "Do you really want to?" & @{BODY} If one would have used "run" instead, "ask" wouldn't be able to wait for any user input. The second difference between "run" and "&" is that "&" makes use of the "job control" feature of the ViNCEd console handler. @{B}This feature is not available for the ROM "CON:" handler.@{UB} As soon as a program launched with "&" tries to print out a text, it is stopped and ViNCEd prints a text similar to @{CODE} [CLI 8] : ask suspended. [ViNCEd output] @{BODY} on its console. The command has been "suspended". You may now move this command "to foreground" by the "fg" script of the ViNCEd distribution: @{CODE} fg 8 @{BODY} The "8" is here the CLI number found in the suspension message above. @{B}NOTE:@{UB} The "&" operator prints a runback note onto the console that is very similar to that of the "run" command. This message is @{B}not@{UB} printed for execution of "&" within shell scripts, but except that, the operator works the like. See also: @{"The & syntax" link RunSyntax} @ENDNODE @NODE HBit "The Revival of the h Protection Bit" @TOC NEWS The V45 shell makes again use of the "h" protection bit for commands: Should a command have its "p" (pure) and "h" (hold) bits set at once, it is made resident on the first invocation of the command as if a "resident" command has been used. This feature is, strictly speaking, not new as it was available in the V39 shell as well. It had to be removed in V40 due to lack of ROM space. This feature is most useful in the startup-sequence to make commonly used commands resident automatically. For example, setting the "h" bit for the "assign" command would load "assign" into RAM as soon as it gets used, without the need of a "resident assign". This is done by: @{CODE} protect C:Assign ph add @{BODY} See also: @{"Details: Command Search Path" link ComPath} @ENDNODE @NODE HashBang "Alternative Script Interpreters" @TOC NEWS Should a file have its "s" (script) bit set, the shell treads this file as a script file and executes this by the "Execute" command as a shell script. In V40, ARexx files could be run with this trick as well, provided the first two characters of the file are "/*", i.e. a Rexx comment. This replaces the "Execute" command by the "Rx" command and runs the script thru ARexx rather by the Shell. V45 enhances this syntax and checks for the special two-character sequence "#!" or ";!" at the start of a script file as well: If found, the shell scans the remaining first line of the script file for a file name and options; the file name is assumed to be the path of the script file interpreter that is required to run the script. The shell then launches this interpreter with the name of the script and further arguments found following the command name on the "#!" or ";!" line as options. Hence, the following script file would display itself in hexadecimal on the console: @{CODE} ;! type hex Hello World! @{BODY} @{B}NOTE:@{UB} The script file name is inserted between the script file interpreter "type" and the option "hex". The following more useful example would run the script file thru the interpreter "perl" @{CODE} #! C:Perl @{BODY} and the following would be equivalent to the old shell behaviour, making use of the "Execute" command: @{CODE} ;! Execute @{BODY} Note that ";" is the shell comment introducer. See also: @{"Details: Command Search Path" link ComPath} @ENDNODE @NODE DTypes "DataTypes Support" @TOC NEWS The V45 shell is aware of the Amiga DataTypes system and can be setup to display data types automatically. For that, the file to be displayed @{B}must have its "e" (executable) bit cleared@{UB} and must be of a data type that is known to the system. Further, the variable "$VIEWER" must be set to the path of a command that is able to display data types. In a typical application, this would be "MultiView", of course. The following command is suitable to enable this feature permanently: @{CODE} SetENV SAVE VIEWER MultiView @{BODY} Then, this guide for example can be viewed with the one and only command @{CODE} Shell.guide @{BODY} @{B}NOTE:@{UB} Unfortunately, it is rather usual that editors and archivers save files with the "e" bit set, failing to identify them as valid data types for the shell. You have then to clear the "e" bit manually by @{CODE} Protect myfile e sub @{BODY} or, alternatively, by using the "Icon/Information" menu of the Workbench, of course. See also: @{"Details: Command Search Path" link ComPath} @ENDNODE @NODE Redirect "Extended Redirection Support" @TOC NEWS The input and output of shell commands could be redirected by means of the ">" and "<" tokens since ever. For example, the output of the list command can be redirected easely into a separate file rather than printing the file list on the console: @{CODE} list >t:out @{BODY} would print the file list into the file "t:out". V37 introduced the ">>" redirection to append the output to an already existing file - or to create this file shouldn't it exist, and "<>" to redirect input and output similarly. The latter works only on "interactive" streams like the console or the "NIL:" pseudo-device (the "data grave"). V45 introduces four new redirection operators, "*>", "*>>", "*><" and "<<". The first three are used to redirect diagnostic error messages of commands into separate files, all provided the executed command makes use of the diagnostic error stream. @{B}NOTE:@{UB} At the time being, very few commands, if any, keep care of the system provided "stderr" stream. Therefore, the three redirection operators "*>", "*>>" and "*><" have little, if any effect. This is unrelated to the shell, but rather due to commands ignoring the diagnostic output stream which was introduced in V37 (Os 2.04). Program authors are urged to have a serious look at the "pr_CES" component of the "struct Process", found in the file. The "*>" operator redirects the diagnostic output into the file name following, very much like ">" does for the "normal" output. "*>>" appends the error messages to an already existing file, or creates this file. "*>>" is the "stderr" counterpart of ">>". Last but not least, "*><" redirects the diagnostic error output into the same file the ordinary output goes to. Currently, this is the default anyhow unless one of the "*>" type operators have been used. Unlike the former, "*><" does not take any arguments. Experts may note that the syntax "*>" can be reminded by "redirecting the current console stream" which is connected to the special file "*". Hence, "*>" could be read as "redirect * into". In fact, "*>" and friends also change the output console in case the target file is interactive. @{B}NOTE:@{UB} There is no "*<>" operator; especially, "*><" is not equivalent to it. The fourth redirection operator "<<" is maybe hardest to understand: It redirects the command input to the script file that is currently executed. The "<<" operator expects an "end marker" specification as argument, and then pushes all following lines up to this end marker into the command input. To make this more clear, consider the following example script: @{CODE} ask "Enter a number:" numeric to num <t:out @{BODY} displays "Hi >t:out" instead of printing the string "Hi" into the file t:out. This is a backwards compatibility switch to V33 and former versions of the shell. @{CODE} echo >t:out "Hi" @{BODY} does the expected, though, in both cases. The V45 shell offers a new variable named "keepdoublequotes" that controls how variables, alias and backtick substitution cooperates with double quotes. The default is either to leave quotes alone, or to remove the first pair of double quotes should the variable be expanded within double quotes itself. If "keepdoublequotes" is set to "on", the shell tries to keep the meaning of quotation as close as possible. Especially, quotes that are part of variable names are escaped by asterisks, and enclosed in double quotes should the variable be not contained within a pair of quotes anyhow. This feature is messy: see @{"Details: The Variable Expansion Syntax" link VarSyntax} The V45 shell no longer limits the size of command lines or variables. Command lines can be as long as free memory permits as the shell is now able to expand all of its internal buffers automatically as required. Hence, any documented limit of "at most 512 characters a line" is obsolete now. Note however that some commands by itself limit the size of its command line and may fail on overlong command lines itself. This is out of control of the shell. Especially, no limit exists for the size of the body of a backtick sequence. Therefore, backtick sequences like @{CODE} echo `list SYS:#? all quick` @{BODY} may grow to @{B}very@{UB} long lines. While not a problem for the shell, command lines as long as the above may successfully choke commands, and hence should be avoided. The shell supports now a feature to enlarge the stack size of commands automatically. For this, the shell scans all hunks of the command to be executed for the magic cookie "$STACK:". Should it be found, a decimal number terminated by a line feed character is expected. This number is then understood as the minimal suitable stack size in bytes for the command, and the stack is enlarged as required. Therefore, program authors may simply place the following line somewhere in their program sources @{CODE} char stacksize[]="$STACK:10240\\n"; @{BODY} to guarantee a minimum stack size of 10K. The terminating line feed is a mandatory part of this syntax and must be present to make this feature work correctly. See also: @{"Details: Miscellaneous Shell Features" link MiscSyn} @ENDNODE @NODE Commands "New and Enhanced Shell Commands" @TOC MAIN The V45 shell offers some new and some enhanced commands that are presented in the following: New commands: @{"PopCD " link PopCD} @{"PushCD " link PushCD} @{"SwapCD " link SwapCD} Enhanced commands: @{"SetEnv " link SetEnv} @{"Ask " link Ask} @{"Path " link Path} @{"UnSetEnv " link UnSetEnv} @ENDNODE @NODE Ask "Ask" @TOC Commands @{b}Ask@{ub} Format: ASK [TO=] [NUMERIC] [STRING] Template: PROMPT/A,TO/K,NUMERIC/S,STRING/S Purpose: To obtain user inut when executing a script file Path: Internal Specification: ASK is used in scripts to write the to the current window, then wait for keyboard input. Depending on the options, valid inputs are either Y (yes), Return = N (no), a number, or any string. Without any further options, ASK signals the return code 5 (WARN) for Y, and 0 for N. An IF WARN statement can be used to check this kind of response. For numeric and string input, the user input is printed onto the current window. Alternatively, ASK may set a local variable to the user input. If the PROMPT contains spaces, it should be enclosed in double quotes. Without the TO, NUMERIC and STRING arguments, ASK expects a boolean input of the type "Y" or "YES" resp. "N" or "NO" or Return (no input) and sets the condition codes accoringly: 5 (WARN) for "Y" and 0 otherwise. If the TO option has been specified, ASK expects the name of a local variable without $ sign and sets this variable to "1" for YES and to "0" for NO. If the NUMERIC keyword has been found, ASK expects the user to input a decimal number. This number is either printed onto the current output, or if the name of a local variable has been specified with the TO option, this variable is set to the user input. If the STRING keyword is found on the input, ASK expects the user to input a string of any kind. Similar to the above, this string is either printed to the current output, or placed into the variable given by the TO keyword. Examples: Assume a script contained the following commands: @{CODE} ASK Continue? IF WARN ECHO Yes ELSE ECHO No ENDIF @{BODY} When the ASK command is reached, "Continue?" will appear on the screen. If Y is pressed, "Yes" will be displayed on the screen. If N is pressed, "No" will be displayed. The following script will ask the user for a number, and will print this number onto the output window: @{CODE} ASK NUMERIC "Enter a number:" TO num ECHO $num @{BODY} ASK will continue to display its prompt until a valid number is found, which will then be put into the variable num. Note that ASK expects the variable name without a $ sign in front. The next example will ask the user for a file name, and will then display the contents of this file on the console: @{CODE} ASK STRING "Enter a file name to display:" TO file TYPE $file @{BODY} See also: IF, SET @ENDNODE @NODE Path "Path" @TOC Commands @{b}Path@{ub} Format: PATH [{}] [ADD] [SHOW] [RESET] [QUIET] [REMOVE] [HEAD] Template: PATH/M,ADD/S,SHOW/S,RESET/S,REMOVE/S,HEAD/S,QUIET/S Purpose: To control the directory list that the shell searches to find commands. Path: Internal Specification: PATH lets you see, add to, or change the search path that AmigaDOS follows when looking for a command or program to execute. When a directory is in the search path, you no longer need to specify the complete path to any files or subdirectories within that directory. You can just enter the filename, and AmigaDOS will look through the directories in the search path until it finds the file. Enter the PATH command alone, or with the SHOW option, and the directory names in the current search path will be displayed. Normally, when PATH is displaying the directory names, a requester appears if a volume that is part of the search path cannot be found. For instance, if you added a floppy disk to the search path, then removed that disk from the disk drive, a requester would ask you to insert the disk. If you specify the QUIET option, PATH will not display requesters for volumes that are not currently mounted. If PATH encounters an unmounted volume, it simply displays the apropriate error code, but continues with the listing. The ADD option specifies directory names to be added to the current PATH. You can add as many directories a time as you wish; the ADD keyword is optional. Names of the directories must be separated by at least one space. When you issue the PATH command, AmigaDOS searches for each of the ADDed directories. Directories within the PATH are searched in the order they appeared on the command line, with new PATH components added to the tail of the PATH list. That is, commands in a former PATH directory override those in a later directory because they are found first when the Shell searches for commands. If you specify the HEAD keyword, however, new directories are added the HEAD of the PATH, to be searched first for commands. To replace the existing search path with a completely new one, use PATH RESET followed by the names of the directories. The exisitng search path, except for the current directory and C:, is then erased and the new one is substituted. The REMOVE option eliminates the named directory from the search path. Examples: @{CODE} 1> PATH EXTRAS2.0:Tools ADD @{BODY} adds the Tools directory on the Extras2.0 disk to the search path of the Shell. If the Extras2.0 disk is not in a disk drive, a requester will ask you to insert it in any drive. If you remove Extras2.0 from the drive, and type @{CODE} 1> PATH @{BODY} a list of directories in the search path will be displayed. A requester will ask you to insert Extras2.0. However, if you had typed: @{CODE} 1> PATH QUIET @{BODY} The list of directores in the search path will be displayed; however, when the path comes to Extras2.0:Tools, only an error message @{CODE} device (or volume) is not mounted @{BODY} will appear in the list. If you use two different versions of the "cmp" program, one residing in SYS:Tools and another in SYS:Compiler/bin, and you add only the former directory to the path in the User-Startup by @{CODE} PATH SYS:Tools ADD @{BODY} only "SYS:Tools/Cmp" will be used whenever you run the "cmp" program from the shell. Even if you enlarge the path manually by @{CODE} 1> PATH SYS:Compiler/bin ADD @{BODY} the "SYS:Tools/Cmp" program gets used because the new directory is added to the tail of the directory list making up the PATH, and "SYS:Tools/Cmp" is found before "SYS:Compiler/bin" is searched for another "cmp" program. However, would you have specified @{CODE} 1> PATH SYS:Compiler/bin ADD HEAD @{BODY} the directory "SYS:Compiler/bin" would have been added in front of "SYS:Tools", and hence the Shell would have looked there first. Therefore, "Cmp" would then refer to "SYS:Compiler/Cmp" rather than "SYS:Tools/Cmp" because it is found first. See also: ASSIGN, @{"Details: Command Search Path" link ComPath} @ENDNODE @NODE PopCD "PopCD" @TOC Commands @{b}PopCD@{ub} Format: POPCD Template: Purpose: Restore the previously stacked directory Path: Internal Specification: POPCD removes the top entry from the directory stack and makes it the current working directory. In case the directory stack is empty, SYS: will be made the current directory. See also: @{"PUSHCD" link PushCD}, @{"SWAPCD" link SwapCD} @ENDNODE @NODE PushCD "PushCD" @TOC Commands @{b}PushCD@{ub} Format: PUSHCD [] Template: DIR Purpose: Change and remember the current directory Path: Internal Specification: PUSHCD places the current working directory onto the directory stack and, optionally, changes the current directory to the specified target. PushCD works very much like the CD command except that it remembers the current directory before it gets changes. This remembered directory can be retrieved later on by means of either POPCD or SWAPCD. The shell maintains a so-called "directory stack" onto which PUSHCD stacks the directories; each "PUSHCD" enlarges this stack by one entry, adding it to the top of the stack. A "POPCD" command removes the top entry from this stack and makes it the current directory; it therefore retrieves the former stored directory. Examples: Assume that the current directory is "SYS:" and you change into "Tools" by means of PUSHCD: @{CODE} 1.SYS:> pushcd Tools @{BODY} afterwards, the current directory will be "SYS:Tools", and the top entry on the directory stack will be "SYS:"; @{CODE} 1.SYS:Tools> @{BODY} A subsequent POPCD will restore the former directory. @{CODE} 1.SYS:Tools> popcd @{BODY} will result in the SYS: directory as current directory again. @{CODE} 1.SYS:> @{BODY} See also: @{"PopCD" link PopCD}, @{"SwapCD" link SwapCD} @ENDNODE @NODE SetEnv "SetEnv" @TOC Commands @{b}SetEnv@{ub} Format: SETENV [SAVE] [] [] Template: NAME,SAVE/S,STRING/F Purpose: To set a global variable Path: Internal Specification: SETENV with and arguments creates a new global environment variable. The first word after SETENV is taken as the . Everything else on the command line is taken as the argument. Quotation marks are not required. SETENV with no arguments list the current global variables. Global variables are stored in ENV: and are used by all processes in parallel. However, if a local variable (defined by SET) and a global variable share the same name, the local variable will be used. Environent variables are called by scripts or other commands by including a dollar sign ($) in front of the variable name. To remove a global variable definition, use the UNSETENV command. If the SAVE command line switch has been used, the variable will also be saved out to disk making it permanent, i.e. reset-persistent. The SAVE keyword must be placed immediately behind the SETENV command as it will be otherwise interpreted as part of the variable definition. If SAVE has been specified, the same variable will remain to be defined even after a reboot and after a power-down cycle. Permanent environment variables will be saved in ENVARC: Examples: @{CODE} 1> SETENV Editor Extras2.0/Tools/MEmacs @{BODY} creates the environment variable Editor which can be used with the More utility. This specfies the ediro as being MEmacs, loocated in the Tools drawer of the Extras2.0 disk. The variable Editor is available in any shell. To make the above choice permanent, use @{CODE} 1> SETENV SAVE Editor Extras2.0/Tools/MEmacs @{BODY} The following line will select the editor "Ed" instead up to the next reboot: @{CODE} 1> SETENV Editor C:Ed @{BODY} Some implementations of the ENV: device will copy environment variables from the permanent storage device ENVARC: to ENV: only when needed. If such a device is used, SETENV without arguments will only list the environment variables that have been used. A complete list can be obtained by @{CODE} 1> list ENVARC: @{BODY} See also: GetEnv, @{"UnSetEnv" link UnSetEnv}, @{"Details: The variable expansion syntax" link VarSyntax} @ENDNODE @NODE SwapCD "SwapCD" @TOC Commands @{b}SwapCD@{ub} Format: SWAPCD [level] [] Template: LEVEL/N,DIR/K Purpose: Swap the top of the directory stack Path: Internal Specification: SWAPCD exchanges the top or the given level of the directory stack with either the current directory, or the given directory should the directory be given. The shell maintains a so-called directory stack of directories; items can be placed on this stack by means of "PUSHCD", and retrieved by "POPCD". SWAPCD is used to quickly switch between two directories, namely the top entry on this directory stack and the current working directory, or the specified directory. SWAPCD with a given level exchanges not the top level, but the n-th level of the directory stack with the current directory. Hence, "SwapCD 2" exchanges the entry below the top of the directory stack with the current directory. Examples: Consider the current directory is SYS: and you change into SYS:Tools with the PUSHCD command: @{CODE} 1.SYS:> pushcd Tools @{BODY} This will place SYS: onto the directory stack and will make SYS:Tools the current directory. Then, you can easely toggle between SYS: and SYS:Tools simply by SWAPCD: @{CODE} 1.SYS:Tools> swapcd @{BODY} will re-install SYS: as current directory and will place SYS: on top of the directory stack instead; a second SWAPCD will restore the above situation again: @{CODE} 1.SYS:> swapcd @{BODY} will therefore result in "SYS:Tools" as current directory again. @{CODE} 1.SYS:Tools> @{BODY} Consider that you now push another directory onto the directory stack: @{CODE} 1.SYS:Tools> pushcd C: @{BODY} resulting in the current directory C: @{CODE} 1.SYS:C> @{BODY} Now the top entry of the directory stack is "SYS:Tools", below we find "SYS:", the current directory is "SYS:C". Then @{CODE} 1.SYS:C> swapcd 2 @{BODY} exchanges the current directory "SYS:C" with level 2 of the directory stack, which is "SYS:". Therefore, the result is @{CODE} 1.SYS:> @{BODY} See also: @{"PushCD" link PushCD}, @{"PopCD" link PopCD} @ENDNODE @NODE UnSetEnv "UnSetEnv" @TOC Commands @{b}UnSetENV@{ub} Format: UNSETENV [] [SAVE] Template: NAME,SAVE/S Purpose: To remove a global variable Path: Internal Specification: UNSETENV removes thenamed global variable from the current varaible list. If the SAVE keyword is specified, a possible permanent copy of the variable is also removed from the system. With no arguments, UNSETENV lists the current variables. See also: @{"SetEnv" link SetEnv} @ENDNODE @NODE Language "The Shell Command Line Syntax" @TOC Main The following is a detailed description of the shell command line syntax. The intended audience is the advanced user that is already familiar with the shell and requires a detailed manual for looking up the shell specifications. @{CODE} @{"Introduction: Command lines " link Lines} @{"Argument Parsing and Quoting " link Quotes} @{"Variable Substitution " link VarSyntax} @{"Alias Substitution " link AliasSyntax} @{"Expansion of Back-Ticks " link BackSyntax} @{"Redirection of Input/Output/Error Streams" link RedirSyntax} @{"The Detach-Operator & " link RunSyntax} @{"The Line Continuation Operator + " link ContSyntax} @{"The Comment Introducer ; " link CommentSyntax} @{"Loading of Commands " link ComPath} @{"Miscellaneous Features " link MiscSyn} @{BODY} @ENDNODE @NODE Lines "Command Lines" @TOC Language The purpose of the Amiga-OS shell is to parse its input stream for commands, then locate these commands either on disk or in memory, and execute these commands. An input stream is typically the stream of a console handler, as for example the CON: window or a ViNCEd window. Other input streams are command files, or "batch files" as they are called sometimes. The most popular batch file is of course the "Startup-Sequence" in the S: assign. The input stream is therefore read line by line, that is, up to a terminating line feed character (or the end of file, where the shell generates this line feed implicitly). Should the input be read from a console window, this line feed is of course generated by the console handler as soon as the user presses the return key. The input line is then scanned for the command itself and for the input/output and error redirection of the command defining the streams the command will print to, will read its input from and may write its diagnostic output to. Furthermore, the shell also handles expansion of back-tick sequences, aliases and variables. A special feature of the shell is the "&" character and the "+" sign at the end of a line, further the comment introducer ";". @ENDNODE @NODE Quotes "Argument Parsing and Quoting" @TOC Language Even though the shell parses the command line, it does not prepare argument lists for the command to be executed; the command line is scanned for shell specific control features like input/output redirection or back-ticks, and the remaining input is forwarded as is to the command. It is the matter of the command itself to parse this input string a second time. This has the unfortune side effect that the shell has to second-guess about the expected syntax of the appropriate command; for the time being, the shell assumes the following syntax, which - surprise, surprise - coincides with the syntax of the ROM function ReadArgs() resp. ReadItem() used by most commands anyhow: This pair of commands knows only two special characters - or maybe four, depending on how you count: Blanks = the space character and the TAB which are considered equivalent, the double quote " and the asterisk * as the BCPL style escape character. A command argument is to be understood as a sequence of non-blank characters up to a line feed or an equal-sign, unless the blanks and the remaining argument are enclosed in double quotes. Interestingly, this goes only for quotes following a blank space or an equal ("=") sign. Quotes within an argument, meaning not following a blank, are not counted as argument separators. Hence, @{CODE} list abc list ab"cd"ef list abcd" @{BODY} present all three the attempt to run "list" with one single argument in which the quote stands for itself. Within quoted arguments, and only there(!) the escape character * gets its special meaning. Outside of quotes, the star stands for itself. Four *-escaped sequences exist: ** is the star itself, *" the double quote, *N a line feed character and *E an "escape" character, of ASCII code 27 = hex 0x1B. Therefore, @{CODE} echo "**" echo ** @{BODY} prints one star, and two stars respectively. Unlike other claims, the sequence "*E[" is not directly related to the "csi" character of code 155 = 0x9b; as far as argument parsing is concerned, this is regarded as "escape [". It is merely the console driver which interprets the latter as a substitute for "csi", neither ReadItem() nor ReadArgs() do. Even though these are all syntax rules as far as the ROM argument parsing routines are concerned, the shell itself parses its input line as well, and performs various operations on this line before it is provided as input to the command to be executed. The first step is back-tick substitution; then the first argument according to the above rules is parsed off, ignoring leading blanks. Alias substitution follows next. As soon as the alias expansion fails to expand further, it checks whether the command name is a shell variable to be substituted, and then tries to locate the command to be executed. This may be either an explicit command found in the current directory, the command search path or the resident list, or an implicit command for script files, non-executable files or directories. The last step parses the command arguments for shell variables to be substituted, and for input/output redirection. @ENDNODE @NODE VarSyntax "Variable Substitution" @TOC Language Local or global variables are set by means of the "set" or "setenv" commands. Both are built into the ROM, or the Shell-Segment, as "resident commands". Should the shell find a variable specification on the command line, this specification is replaced by the contents of the variable before the line is feed into the command as input, or used as the command to be loaded directly. A variable specification is either a $ sign, followed by alpha-numeric characters, or a "${" sequence followed by arbitrary characters up to the line end and a pairing "}" brace to end the variable name. The braces itself do not count as part of the variable name itself and are therefore discarded. Should the variable be undefined, no substitution takes place and the $ followed by the supposed to be variable name stands for itself. An alpha-numeric character is here any digit, any lower or upper case character or any national character within the code range of 161..255, i.e. hex 0xa1..0xff. Hence, @{CODE} echo $a echo ${a} @{BODY} print the same text if there should be a variable named "a", otherwise both print the argument literally. Even though the above side effect can be used to test whether a variable has been set or not, the shell offers a more orthogonal way to check the existence of a variable, namely by $? followed by the variable to be tested for existence. This expression returns the string 0 if the variable is not defined, or 1 otherwise. Hence, @{CODE} echo $?a echo $?{a} @{BODY} generate the same output, 0 for a undefined and 1 for a defined. Note that the ? is part of the $, and not part of the variable name. The second related sequence is $?? which returns 1 only if the variable is defined as a global variable, i.e. exists as a file within the ENV: device. The usage of { } allows rather weird variable names, even names containing spaces, escape characters, question marks, quotes, ">" or "<". Due to a side effect of the internal working, ":" does NOT work as part of any variable name; unfortunately this cannot be fixed since variables are stored by a device handler that reacts in a special way on ":". Similar restrictions arise for the slash / that implies a special meaning for global variables. (Make a guess how!) The $ itself can be escaped by an asterisk, hence *$ stands for a single $. Unfortunately, THIS meaning of escaping is independent of the * concerning the argument parsing mentioned above, and is therefore valid within and outside of quotes, but only in front of a $ sign (and other shell-only tokens like the back-tick ` and the bracket [, see below). For the same reason, the parsing of variable names, though not variable expansion, works the same way within and outside of quotes, except that stars * that are not in front of $ have a different meaning. Therefore, confusingly, @{CODE} echo ${*} echo "${**}" @{BODY} address the same variable, namely *, whereas both @{CODE} echo *$ echo "*$" @{BODY} print the same string $, regardless of the quotes. The escaping can be escaped with another star as well. Hence, @{CODE} echo **$a @{BODY} will print a star, followed by the contents of a. This kind of escaping the $ sign is independent of the quotation as well and works only in front of $ and related shell-only tokens. Therefore, @{CODE} echo "**$a" @{BODY} does right the same. More precisely, $ stands for itself with an odd number of stars in front, and is regarded as variable expansion command with an even number of stars. Since variable substitution happens before argument analysis, @{CODE} echo "***$a" @{BODY} will be parsed as "star, star, escaped dollar, a" on expanding variables, and the resulting string is analyzed as argument of "echo" as "**$a" with the $ meaning of a literal "$". Now, the star escapes the second star within the quoted argument: echo prints therefore *$a onto the console. Since * is not an escape character for itself within arguments, @{CODE} echo ***$a @{BODY} will print one star more instead, but the interpretation of $ does not change. Clearly, all this is messy, and can be understood only in the historical context of the Amiga shell where $ was added after argument parsing syntax has been set in stone. The very same rules for escaping apply to ` and [ as well, for exactly the same reason. The main idea of the above rules is that $ should not influence argument parsing "too much", should work outside and inside of quotes, but should be escapable by the same BCPL syntax. Looking back, a backslash-driven and more consistent escaping mechanism would have been more appropriate, of course. A second rule formulates how variable expansion interacts with quoting: Should the $ sequence be within double quotes, and should the variable contents be quoted as well, the outermost pair of quotes of the variable contents gets removed. Otherwise, the result could have been a quote within a quote, undesirably terminating the argument. The following example demonstrates this: @{CODE} set a "*"hi*"" ;defines a as "hi", note the escaping of quotes ;due to a first parsing step before executing the ;set command. echo "a is $a" ;results in "a is hi" @{BODY} Leaving the quotes around $a would have resulted in @{CODE} echo "a is "hi"" @{BODY} This would be interpreted as two arguments rather than one, the first one being "a is " and the second one being hi"". If this is undesirable, set the local variable "keepdoublequotes" to "on" by the following command @{CODE} set keepdoublequotes on @{BODY} Thereafter, the above command @{CODE} echo "a is $a" ;results in "a is *"hi*"" and prints a is "hi" @{BODY} will get the quotes escaped properly such that the "echo" command really prints the text including the quotes of the variable contents. The very same idea of "double quoting" is applied to variable expansion outside of quotes, except that the shell now either leaves the possibly quoted variable contents alone, or adds quoting and escaping to guarantee that the command reads the quotes as part of its argument. Therefore, the command @{CODE} echo $a ;results in "hi" with keepdoublequotes off ;results in "*"hi*"" with keepdoublequotes on @{BODY} prints either hi or "hi", depending on whether "keepdoublequotes" is "off" or "on". In most cases, you might find "off" more attractive as it is backwards compatible, even though it drops one level of quoting. However, if "keepdoublequotes" is set to "on", the shell tries to preserve as much of the original meaning of the variable, possibly adding escape characters. @ENDNODE @NODE AliasSyntax "Alias Substitution" @TOC Language Before the shell is trying to locate a command, it tries to expand this as an alias. Aliases are command renames with additional argument relocation, to be defined by the shell-resident "alias" command. On invocation, the alias is then replaced by the contents of the alias, "shifting remaining arguments to the right" after inserting a blank. Hence, @{CODE} alias foo echo a foo b @{BODY} will result in the command "echo a b" and the output "a b". With the aid of the additional alias token [] one can relocate the remaining arguments to other places, e.g. in front of additional arguments of the alias contents. No blank is inserted in this case: @{CODE} alias foo echo [] a foo b @{BODY} will move the "b" in place of the [] and therefore print "b a", the opposite of the above. No blank spaces are inserted in front of the [], this should be done manually if required. @{CODE} alias foo echo []a foo b @{BODY} will therefore really print "ba". Unfortunately, the brackets do not try to analyze the argument syntax for traditional reasons and work therefore only the first time within the alias contents, i.e. @{CODE} alias foo echo [] a [] @{BODY} will not work as expected. The second [] is regarded as literal bracket. Future releases might introduce further refinements of [] that allow finer control of the arguments. Similar to the $ sign, the [] can be escaped by a *, applying the very same @{"messy rules" link VarSyntax} concerning the counting of stars. However, one should now be even aware that aliases must enter the shell database somehow: This is of course done by the "alias" command which, by itself, parses its arguments @{B}again@{UB}. Hence, should it be necessary to enclose the "alias body" of the alias command in double quotes, then all stars @{B}MUST BE DOUBLED@{UB}. @{CODE} alias bracket "echo **[]" bracket @{BODY} will just print [] on the console. The ** is escaped into a * by the argument parsing of the alias command, and *[] escapes into [] upon expansion of the alias. Once again, the @{"same quotation rules" link VarSyntax} as for variable expansion apply: By setting "keepdoublequotes" to "on", you may define whether the shell shall prevent quotes or shall drop one level of quoting. Alias expansion happens recursively until no alias are found to expanded any more: If the expansion of an alias happens to be an alias again, then this alias is expanded, and so on. Since this would imply the possibility of an infinite loop of an alias that expands into itself, or into an alias that expands into an alias it was expanded from, no alias will be used more than once within a single command line. Hence, the following alias definition is legal: @{CODE} alias hi ho alias ho echo "ho" @{BODY} and defines the "hi" command to output "ho", whereas the following alias definition is asking for trouble: @{CODE} alias gnu gnu is not unix @{BODY} This will first expand "gnu" into "gnu is not unix", and will stop then since the "gnu" alias has been "used up" already. Since the gnu command cannot be resolved further - unless of course you provide this command as a binary - an error message will result. Expansion of aliases can be prevented completely by enclosing the command in quotes. "hi", therefore, will not try to use the above hi-alias, but will try to find a file named "hi". Otherwise, alias definition take precedence over ordinary files, i.e. the alias expansion will be applied first when applicable, and only if this fails the shell tries to locate an appropriate command. @ENDNODE @NODE BackSyntax "Expansion of Back-Ticks" @TOC Language As the very first step of command line interpretation, the shell will expand all back-tick sequences on this line up to the line end or the start of a comment within the line. The commands enclosed in back-ticks will be executed, and the resulting output, after a mild preprocessing step described below, is inserted instead of the back-tick sequence. The reader should be aware that the resulting command line might be very long; even though the shell itself has no problem to process long lines, some commands may. The back-tick itself is represented by *`, and hence @{"escaped in the very same way" link VarSyntax} as the $ sign for variable expansion and the [ bracket of the alias argument placement operator; this goes also for the messy multiple-star sequences, hence **` is a literal star followed by a back-tick sequence, and ***` a literal star followed by a literal back-tick, and so on - see the @{"syntax description of $" link VarSyntax} for all the side effects one should expect. The command output of the "backtick'd" command is processed before it replaces the back-tick sequence, though. The shell removes a final terminating line feed, should it be present, and replaces all other line feeds by blank spaces. Finally, the shell handles double quotes in the very same way as for $ and []: If "keepdoublequotes" is "off", quotes resulting from back-tick sequences within quotes are removed, and no additional quotes are paired around back-tick expansions resulting in quoted strings. If "keepdoublequotes" is "on", however, the shell tries to preserve the meaning of the string as close as possible and adds escaping stars to preserve quotes within the command output. The body of the backtick'd sequence is considered to be a separate command line and is therefore parsed separately. Especially, quotes outside a back-tick sequence can never match any quote within, and do not influence the meaning of the back-tick sequence as far as escape characters are concerned. Hence, in the following two command lines @{CODE} echo `echo *.` echo "`echo *.`" @{BODY} the star stands for itself, even for the second line. It is not considered to be "within" the surrounding double quotes and is therefore not treated as an escape character. However, the reader should be aware of the * as an escape sequence for the back-tick itself, as mentioned in the first paragraph! The two commands @{CODE} echo `echo *` echo "`echo *`" @{BODY} will NOT print a single star since the star in front of the second back-tick escapes this back-tick and hence disables the expansion of the back-tick sequence at all. One correct way to print a single star by this admittedly complex method would be @{CODE} echo `echo **` @{BODY} whereas @{CODE} echo `echo ***` @{BODY} prints `echo **` with one star less, namely the star that prevented the last back-tick to be part of a back-tick sequence. This is again the "count the stars" rule @{"we have been observing for $ and []" link VarSyntax}. Needless to say, back-tick expansion works also within the prompt, and for the command itself. The latter is a new feature for the V45 shell. @{CODE} `echo type` s:Startup-Sequence @{BODY} is a rather complex and weird way to display the startup sequence on the console window, and @{CODE} prompt "*`date*` %N.%S> " @{BODY} prints the date in front of the typical CLI number/directory prompt indicator. The command to be executed within the prompt should be better fast and small, of course! The reason for the asterisks in front of the back-ticks is left as a little exercise for the alert reader. @ENDNODE @NODE RedirSyntax "Redirection of Input/Output/Error Streams" @TOC Language Each command to be executed inherits three I/O streams from the shell. An input stream user input is read from, an output channel the command prints output to, and a diagnostic channel error codes should appear on. Unfortunately, the consistent use of the latter has never been very popular and is not even supported to full extend by various Os functions; the PrintFault() dos.library call, for instance, does NOT print to the diagnostic output. Nevertheless, all three streams can be redirected by appropriate shell constructions: An unquoted > followed by a file name defines the output stream, a < plus file name the input stream and a *> the diagnostic output channel. Should these sequences be required as part of any argument for the command, they should be enclosed in double quotes. Should the shell find more than one similar redirection on the command line, all additional redirections are regarded as literal strings. Hence, @{CODE} echo >t:out >NIL: @{BODY} prints the string ">NIL:" into the file "t:out". Further, the shell applies special rules to the commands "alias", "run" and "pipe" leaving the parsing of the redirection tokens to the command itself. This has the effect that, for example, the following alias @{CODE} alias foo echo foo >t:out @{BODY} defines the "foo" command to print the string "foo" into the "t:out" file, rather than printing the (empty) result of the alias command into that file. Similar considerations apply to "run" and "alias" as well, of course. Furthermore, should the variable "oldredirect" set to "on", then the shell will only try to interpret redirection tokens following immediately the command name; otherwise, the position of the redirection tokens do not matter. @{CODE} echo "foo" >t:out @{BODY} will either print "foo" to the file "t:out", or will print "foo >t:out" if "oldredirect" is set to "on". This is a backwards compatibility feature for pre V37 shell scripts. Needless to say, the file name argument of >, < and *> must be quoted if it contains spaces, and can be as well a variable specification by means of $, or a back-tick sequence. Therefore, @{CODE} set t t:out echo foo >$t @{BODY} and @{CODE} echo foo >`echo t:out` @{BODY} write both "foo" into the file "t:out". Besides these three commands, the shell also understands the following additional redirection tokens: >> appends the output to a file, or creates it should it be not yet existing. <> redirects both input and output into the same file. However, since this implies that the input stream must be "cloned", the "file" must be either NIL: or any kind of interactive device, e.g. CON: or RAW:. An ordinary file cannot be written to and read from at the same time. *>> works much the same way as >> does, but just for the diagnostic output: It either creates the file, or appends to an existing stream. *>< redirects the diagnostic output into the same file as > does. Unfortunately, this has to be the default anyhow for compatibility reasons beyond logic, and is explained below in more detail. *<> is NOT equivalent to *>< and currently a reserved token. Last but not least, the shell also understands a very special input redirection using the << token. This token takes a string as argument and scans the shell input - typically a batch file containing the << redirection - up to the specified string and pastes the result into the stdin of the command. The following example may enlighten the reader a bit: @{CODE} ask "Enter a number:" numeric to num echo $num @{BODY} asks the user for a number - to be entered on the input stream of course - and then prints this number. The following silly script will define this number to be 42: @{CODE} ask "Enter a number:" numeric to num < sets the terminal console task of the command as well, and hence redirects the "*" output into the console specified by *>. Unfortunately, it is quite typical to detach commands by means of "<>NIL:" from the console. Without further care, this would NOT redirect stderr anymore and would therefore break existing shell scripts. Hence, some messy compatibility rules for *> redirection have been established: @{code} Without *> The diagnostic error stream goes into the output stream, the command console goes into the current console. A "run" command would clear the current console anyhow, and would hence dispatch the file from the terminal. With *> Diagnostic output will go into the specified file, the command console is the current console unless the specified file is interactive or NIL:. In the first case, the program console is set to the controlling console of the stream, in the second no terminal console is provided. With *>< Diagnostic output will go into the output stream, the command console will be set to the console controlling the output stream should this be inter- active, or will be cleared in case the output goes to NIL: @{BODY} Similar rules apply if the command is run in background by means of the "&" token, except that there is no default command console unless one is provided by means of *> or *><. This makes it a bit easier to detach commands from the shell by using "&" and "<>NIL:". @ENDNODE @NODE RunSyntax "The Detach-Operator &" @TOC Language Should the shell find a & sign on the command line that is surrounded by blank spaces and neither quoted, then this sign is removed from the command line and the command gets detached from the shell very similar to what "run" would do. By default, these commands get a new separate console, and a new console owner should the console driver support this concept (ViNCEd does, the ROM CON: handler does not) and hence make use of the "job control feature" of the console. These commands will keep the console window locked open unless you explicitly redirect the command input and output to NIL: - this is simply because commands launched in this way are handled very similar to commands launched by "run". There is, however, one important difference between "run" and "&": Unlike "run", the "&" sign does not detach the command to be run from the standard input. Hence, "ask &" will still ask for input on the console, "run ask" will not. Furthermore, "run" doesn't mix with << input redirection, "&" does. All this is because the "&" sign is handled by the shell directly and is much better integrated into the internal wiring than "run" could. @ENDNODE @NODE ContSyntax "The Line Continuation Operator +" @TOC Language Should a command line end on a single + sign, the shell waits for additional input and concatenates this input with the previous command input, including the line feed, but excluding the + sign itself. However, this second line is not parsed as a shell command line, but is rather placed on the command arguments of the command of the previous line. Since the ROM ReadArgs() and ReadItem() functions only parse up to the first line feed character, this additional line is typically lost and is only of interest for special commands like "run" that read their arguments in a non-standard way. @ENDNODE @NODE CommentSyntax "The Comment Introducer ;" @TOC Language If found unquoted on the command line, the ";" introduces a comment. All characters following the semicolon, and the semicolon itself are removed from the command arguments. @ENDNODE @NODE ComPath "Loading of Commands" @TOC Language Besides @{"aliases" link AliasSyntax}, the shell knows also the following types of commands: The PIPE command, which is used as soon as one of the pipe tokens is found on the command line, resident (or so-called "built-in", though this nomenclature is sort of incorrect) commands, implicit commands, commands on the command search path, and commands within the current directory. Should the shell find that the user defined either the _pchar or the _mchar local variables, then the command line is scanned for the contents of these variables. For historical reasons, both variables should contain strings of at most two characters in size. If these characters are found, then the shell assumes an implicit "PIPE" command that is prepended to the full command line before further parsing. Resident commands are found on the dos.library "resident command" list and are program segments similar to those generated by the LoadSeg() command. Unlike the latter, they do not get a "fresh" copy of their segments each time but are assumed not to touch their code and data space; hence, not all commands should be made resident as they are not "pure". Pure commands are usually in ROM space, or can be placed onto the list of residents by the "resident" command, which, by itself, is resident. The "resident" command checks the "p" bit of the command file whether a certain command can be made resident or not. Since the "p" bit can be set by hand, this is not much of a safety feature, though. The original "arp" ("Amiga Replacement Project") commands where the resident feature goes back to had an additional check concerning the purity of commands. If a command is neither resident nor an alias, the shell tries to find it on the command path list, which is controlled by the "path" command. If an executable binary load file is found, this is assumed to be the command; should the "h" = "hold" bit of this executable be set as well along with the "p" bit, the command is made resident as it is loaded. If the file in question is not executable, but the "s" bit is set, then the first two characters of the file are investigated. If they are "/*", hence a Rexx-comment introducer, a rexx script is assumed and an implicit "RX" command is prepended to the argument line. If they are "#!" or ";!", the first line of the script is assumed to be the file name of a script interpreter. This name must be enclosed in double quotes should it contain blanks. The script name itself gets then inserted @{B}between@{UB} the command interpreter file name, and its options on this line. Therefore, the following command interpreter specification on the first line of the file "foo" @{CODE} #! type HEX @{BODY} will run the following text of foo thru the interpreter "type" with the option "HEX", as if @{CODE} type foo HEX @{BODY} has been input on the command line. Note that the HEX option is placed behind the "script" file name, and not in front of it. If neither a "/*", a "#!" nor a ";!" is found, the script is assumed to be a shell script and run thru the "Execute" command. Curiously, "Execute" expands only the arguments and generates a temporary script file, but the actual command interpretation of this file is left to the shell again by making use of a not-so-well documented side effect of the shell segment. If the found file is neither a script, nor executable, the shell checks for the variable "VIEWER". Should it be found, then the shell further checks whether the file is any known data type by using the datatypes library. If so, then the contents of this variable is understood as a command that should be run to display this file. For this, the contents of the VIEWER variable is placed in front of the command line before further interpretation. Typically, VIEWER should be set to the MultiView command, of course: @{CODE} SetEnv SAVE VIEWER MultiView @{BODY} Finally, if the "file" is in fact a directory, the implicit command "CD" is assumed and inserted in front of the command line. The shell can be restricted to search on the current directory only by enclosing the command within double quotes; this will remove the resident command list and the command path from the search targets the shell will scan for commands. @ENDNODE @NODE MiscSyn "Miscellaneous Features" @TOC Language The shell scans all segments except resident INTERNAL commands for the "magic cookie" @{CODE} $STACK: @{BODY} Should it be found, then a decimal string terminated by a line feed (!) is expected. This string is then regarded as a request to enlarge the stack to at least the specified amount of bytes. Therefore, the following string near the startup code of your program will guarantee at least 10240 bytes of stack: @{CODE} char stack[]="$STACK:10240\\n"; @{BODY} Note that the "\\n" is necessary and that "STACK" is in capitals. @ENDNODE @NODE History "History: What Happened Before?" @TOC Main @{CODE} Shell V45.0: Oh wow, this was a tough job. The Shell really looked like some kind of BCPL left-over. It took several days just to cleanup the code and turn it into something more readable. BCPL was lurking thru the code all over. No more, folks, no more... The V40 shell was unable to perform the "implicit CD" command properly if the directory name was quoted. It inserted an unnecessary blank space behind the directory name. The shell didn't support multiple backticks on the command line; the backtick parser was simply broken: Fixed handling of asterisk-backtick sequences. The backtick wasn't escaped properly. The shell did not treat TABs as blank spaces. Fixed. Removed the SetClock kludge. Whoever is still using a 1.3 SetClock nowadays cannot be helped anymore and deserves to be crashed. (-; Added a few missing Forbid/Permit locks around access of the list of resident commands. Removed an ugly hack to set the RC and Result2 variables; this really uses now the proper system functions and doesn't attempt to hack directly into the variable buffers. Reworked the parser a lot; it partially tried to fiddle in the internal buffers of file handles using structures that have never been documented. The code now loads full lines at once (no matter how big they are) and then parses within its buffers. This removes all dependencies of implementation details of the file handles. Arguments to redirection did not expand backticks. Fixed. Added a lot of proper error checking if it is low on memory. The internal (and deceased "GlobalVector") is no longer passed thru to all functions. Instead, I'm now using some SAS magic to allocate my data segment myself on each invocaton. Decided not to disable echo-ing for non-interactive shells. Otherwise, we could not debug scripts or the startup-sequence. Sorry, Heinz, but I reconsidered and it's really better this way. Shell V45 re-introduces the handling of the "h" bit. Should it find a command with "pure" and "h" bits set, it makes the command resident automatically. Note that this is (again) the V39 behaivour. Note that "h" is for "hold", not for "hidden". The shell does no longer limit command lines to 512 characters. Command lines can now be unlimited in size, provided the command itself is smaller than the AmigaOs file name size limit (107 characters). The Shell will now auto-expand all of its internal buffers if required to do so. Furthermore, it uses now memory pools for this kind of administration. The shell offers now extended "script" support. In case a script file - i.e. a file whose "s" and "e" bits are set - starts with either the magic characters "#!" or ";!", the string behind this magic cookie is understood as the path to a command parser that shall execute this script. Added support for "file browsing". If the shell encounters now a file as first argument that has its "e" bit cleared (!) and is parsed and recognized as a data- type by the data types system, the shell runs a "viewer" program whose path should be placed in the $VIEWER shell variable. Added support for "stderr" redirection. Therefore, the V45 shell introduces three new redirection symbols: *> filename : redirects stderr to a file *>> filename : appends output to stderr to a file *>< : redirects stderr to the same file that stdout goes into. The shell will also assign the console to the stderr stream should stderr be an interactive stream. Added support for "script stdin". A "<<" will pull the next symbol from the shell script and will push all following lines up to a line starting with this symbol as input into the command. Hence, "<<" is exactly the Un*xoid input redirection. Added a new command line token, the "&" character, the "implicit run". If found, the shell opens named consoles with of a name generated thru utility/GetUniqueID(), and assigns these to the stdin/stdout/stderr of the command, shouldn't redirection be used anyhow. Then the command is detached from the shell segment. Added automatic stack size setup for shell commands. This is a new feature that would hopefully limit the number of stack underflows as it allows application developers to increase the stack automatically without the need for stack swap code. The shell scans now the first 1K of each segment of a loaded binary for the magic cookie "$STACK:". The string behind this cookie is expected to be a decimal number, terminated with a line feed (and an ASCII NUL, i.e. '\0', as for all C strings). This number is then interpreted as the stack size in bytes. The shell will automatically increase the stack size *for this command only*. If the pre-defined stack size is larger than the program-implicit, nothing will happen. AGENDA: Rewrite the parser for shell-variables. This looks currently messy and not very well thought-about. Especially the handling of asterisks is broken here. Rewrite the parser for backticks. This happend already to some degree, but it is not yet perfect. Shell 45.1: The shell did not handle script files correctly that did not end on a line feed. Fixed. The shell bootstrap code was partially broken and could not run the "Initial CLI" correctly. The BCPL startup mechanism was rewritten completely (to the better). Incorporated all traditionally resident commands into the Shell-Seg making this a self-contained shell with a resident segment that is rom-able and can bootstrap the system. Rewrote the shell command line parser to some degree. This implies various changes on how the command line gets interpreted. Good news is that backticks can now also be used to specify the command itself, alias [] works within backticks, and backticks cooperate nicely with aliases in general. Furthermore, the new parser allows multiple, recursive, but non-looping alias expansion, and aliases containing backticks that are expanded correctly. Fixed *>< redirection hits. Fixed generation of result codes, the shell forgot to forward command result codes properly. Fixed stderr redirection: stderr defaults to stdout, it now defaults to the shell output stream; hence, an explicit *>< is required for the old behaivour. Fixed escaping for implicit commands CD,EXECUTE, and others: They no longer escape " and * if found in an unquoted string. Note that this is the proper behaivour, even though this is more a "feature" of ReadItem() than anything else. Fixed the "CD" command: It did not allocate the anchor path from memory resulting in a possibly mis-aligned "FileInfoBlock" and damaged memory. Yikes! Fixed the "resident" command: First of all, it also placed "AnchorPath"s on the stack, which is no good. Second, it did the printf for the resident command list within a loop while Forbid(), making the forbid for the list lock pretty much useless. Reworked set,setenv,alias,unset,unsetenv,unalias: All of these six commands are handled by the same segment, but the division into various subcommands happened in a rather unorthogonal way by second-guessing from the command name. It is now organized in several segments calling the same main routine with parameters. "set" became reorganized a lot as well: "setenv" without parameters is no longer redirected to "list", making this independent of whether external commands are available. "set" reformats now ESC and CSI sequences on the screen consistently by using the BCPL escape character "*". Other non-printables are (as it always has been the case) replaced by the smear-character 0x7f (DEL). Reworked get, getenv for the same reason: One segment, two commands. Reworked "run", but not quite as much as I would like to. This still rolls its own faked input file handle. Even worse, it gets the command line from the undocumented internal buffer of its stdin stream. There's currently no better way of doing it. Added a new option "HEAD" to "path". This will add new paths to the start of the command search path and will hence search them first. Reworked "newshell". It no longer tries to place critical DOS structures on the stack, and will therefore long-word align them correctly all the time. The window open path of NewShell has been changed to CON://130/AmigaShell/CLOSE/SHELL to signal shell usage for console handlers, e.g. ViNCEd. AGENDA: The shell argument parsing is still somewhat wierd. Backticks are not recognized as "quotes" and don't group commands on parsing - this happens only on expanding them. This is mainly the matter of ReadItem() which should be replaced by a custom routine. Further, single quotes are not handled at all. I wonder whether I should touch this or leave it as "compatibility feature". Shell 45.2: Fixed several missing error reports, for example for out-of-memory situations and unability to open redirection files. Changes the style of stderr redirection: Stderr defaults now to stdout, unless redirected otherwise. This is a compatibility cludge for <>NIL: type redirection. Redirection with <> checks now explicitly whether the target file is interactive or NIL: It will fail in all other cases. Redirection into "*" with the run-back operator "&" will now go into the console owner established for the command output rather than the current console. Added PushCD, PopCD and SwapCD commands as build-ins. Re-established the "oldredirect" variable. If set to "on", the shell will check for redirection operators only immediately behind the command name and nowhere else. This is a pre-V36 compatibility feature that was dropped in V40 due to limited ROM space. Added SAVE keyword to UNSETENV to make the changes permanent. Fixed wrong templates for unalias, unset and unsetenv. Reworked ASK a lot; this command allows now numeric and string input, and places the result either on stdout or into a user specified local variable. Shell 45.3: The "&" symbol was broken in several ways: First of all, it unlocked home and current directory twice instead of once, causing possible memory trashes. Further, it did not set CLI->cli_Module correctly which is required for some run-back and stay-resident programs. Thrid, it did not check the very same CLI variable to release the program again, hence might have even unloaded segments that are not meant to be unloaded. Fourth, it did not work well with "Execute" due to the rather clumpsy way the original BCPL shell introduced the execute logic. The "&" handling was more or less rewritten for the 45.3. The new logic introduced another kludge in the shell startup logic (oh no!) but makes it possible to run programs in background *without* destroying stdin, hence unlike run. Therefore "run" is more or less obsolete now. The shell command line parser was turned upside down. Nothing of the old parser is left. The net effect is that the general logic of argument parsing has been improved much. The shell now knows on variable expansion and backtick expansion whether it inserts arguments into quotes, and if so, escapes properly. Fixed handling of aliases in some ways, especially fixed a bug of the 45.2 where the shell forgot to insert a space. Removed handling of ( and ) in pipes. They aren't used by any pipe command I know of and are therefore just here to make the syntax even more cludgy. Fixed handling of interpreter setup by means of #! or #;. The line behind the magic symbols is now parsed thru ReadItem and hence requires proper quoting, so beware! Fixed handling of implicit commands on arguments starting with quotes. The old shell could not run these commands properly due to unadequate re-escaping of the command argument. This is now all handled (hopefully correctly) by one common routine that is also used for variable and backtick sub- stitution. Version 45.4: The 45.3 release caused problems with existing scripts because quoted variables within quotes where escaped by * such that double quotes resulted. While this is correct in principle, it broke already existing scripts. For this reason, a special rule is now applied: Variables within quotes get their first pair of surrounding quotes removed. You can avoid this by setting the variable "keepdoublequotes" to "on": set keepdoublequotes on Pre 45.3 releases generated a wrong error for script files that are read-protected. Fixed. Version 45.5: Fixed the parser state machine such that variables of wierd names like ">t" are possible. The parser used to be quite inconsistent how to handle them. SwapCD has an optional LEVEL argument that defines which level of the directory stack to swap with. The stack extension logic scans now the first 4K for the stack cookie. Setenv SAVE clears now the "e" bit on the saved variables. The asterisk in front of $, [ and ` is now handled correctly again. * escapes the following character, ** escapes the star from escaping, *** is a double star with escaped following control character. Added the possibility to check for the existance of a variable by the $?varname method. $?a will expand to 1 if a is defined, 0 otherwise. $??a will expand to 1 only if a is found as a global variable. Fixed handling of quotes in variables and backticks outside of quotes. Behaivour of the shell depends here now again on the "keepdoublequotes" variable. If "on", the quotes are escaped and the full string is put into quotes, otherwise the string is left alone. If already in quotes, "keepdoublequotes" = "on" escapes quotes within the variable body, otherwise the outermost quote gets removed. Fixed the escaping of the rear backtick of a backtick sequence. StdErr redirection to NIL: by *>NIL: will now clear the console task for the command to be run. Command interpreters specified by the #! or ;! token may now get additional arguments specified on this line. The script file name is inserted between the command and these arguments. Fixed the multiple redirection issue. Fixed handling of alias and redirection. As a side condition, the output of the "set" and "setenv" commands can now be redirected just fine. Version 45.6: Various minor code cleanup concerning the command buffers; they are now all static to the functions they belong to. Fixed recognition of $VIEWER which had to be a local variable to be functional. Removed beta warning message on startup. The shell scans now all hunks of a loaded binary for the stack size. Furthermore, several enhanced safety checks have been added for this feature. Added a german documentation, proof-read and spell-checked the english documentation. Version 45.7: The run-back operator & did not move the shell segment in the seg-array and hence might have crashed some BCPL commands. The stack size search routine got improved quite a lot and is now much faster than before - by a factor of four to five. The "setenv" command clears now the "e" bit of the variables it saves. Version 45.8: Fixed handling of backticks within comments. They are now properly ignored. Fixed handling of equal-signs within arguments. They are now considered similar to blank spaces and consider following double-quotes as function, not as literal. @{BODY} @ENDNODE @NODE INDEX "Index" @{CODE} A... @{"Alias Substitution" link ALIASSYNTAX} @{"Recursive Alias Expansion" link RECALIAS} @{"Alternative Script Interpreters" link HASHBANG} @{"Argument Parsing and Quoting" link QUOTES} @{"Ask" link ASK} B... @{"Expansion of Back-Ticks" link BACKSYNTAX} @{"Multiple Backticks" link BACKTICKS} C... @{"Check Variables for Existance" link VAREX} @{"New and Enhanced Shell Commands" link COMMANDS} @{"Command Lines" link LINES} @{"The Comment Introducer ;" link COMMENTSYNTAX} @{"The Line Continuation Operator +" link CONTSYNTAX} D... @{"DataTypes Support" link DTYPES} @{"The Detach-Operator &" link RUNSYNTAX} @{"The Shell Job Control and the & Operator" link RUNBACK} E... @{"Expansion of Back-Ticks" link BACKSYNTAX} @{"Extended Redirection Support" link REDIRECT} F... @{"New V45 Shell features" link NEWS} H... @{"H Bit: The Revival of the h Protection Bit" link HBIT} @{"History: What Happened Before?" link HISTORY} I... @{"Alternative Script Interpreters" link HASHBANG} @{"Introduction: What is this about?" link INTRO} J... @{"The Shell Job Control and the & Operator" link RUNBACK} L... @{"The Shell Language" link LANGUAGE} @{"The Line Continuation Operator +" link CONTSYNTAX} @{"Loading of Commands" link COMPATH} M... @{"Miscellaneous Features" link MISCSYN} @{"Miscellaneous Features of the V45 Shell" link MISC} @{"Multiple Backticks" link BACKTICKS} N... @{"New and Enhanced Shell Commands" link COMMANDS} @{"New V45 Shell features" link NEWS} P... @{"Path" link PATH} @{"PopCD" link POPCD} @{"PushCD" link PUSHCD} R... @{"Recursive Alias Expansion" link RECALIAS} @{"Redirection of Input/Output/Error Streams" link REDIRSYNTAX} @{"Extended Redirection Support" link REDIRECT} @{"The Run-Operator &" link RUNSYNTAX} @{"The Shell Job Control and the & Operator" link RUNBACK} Q... @{"Argument Parsing and Quoting" link QUOTES} S... @{"Alternative Script Interpreters" link HASHBANG} @{"SetEnv" link SETENV} @{"SwapCD" link SWAPCD} @{"The Shell Command Line Syntax" link LANGUAGE} U... @{"UnSetEnv" link UNSETENV} V... @{"Check Variables for Existance" link VAREX} @{"Variable Substitution" link VARSYNTAX} @{"$VIEWER: DataTypes Support" link DTYPES} @{BODY} @ENDNODE