ETSH(1) | General Commands Manual | ETSH(1) |
The options are as follows:
The shell may also be invoked non-interactively to read, interpret, and execute a command file. The specified file and any arguments are treated as positional parameters (see Parameter substitution below) during execution of the command file.
Otherwise, if no arguments except for -v are specified and if both the standard input and standard error are connected to a terminal, the shell is interactive. By default, an interactive shell prompts a regular user with a `% ' or with a `# ' for the superuser before reading each command line from the terminal. (+) Notice that the user can set a non-default shell prompt if desired (see Prompt below).
(+) When an interactive shell starts, it reads and executes commands from the appropriate rc files if possible (see Startup and shutdown below) before reading and executing command lines from the terminal.
When such a character is unquoted, it has special meaning to the shell. The shell uses it to separate words (see Commands and Command lines below). A quoting metacharacter is any one of the following:
See Quoting below. The substitution metacharacter is a:
See Parameter substitution and Variable substitution below. Finally, a pattern metacharacter is any one of the following:
See File name generation below.
If the first argument names a special command, the shell executes it (see Special commands below). (+) Otherwise, the shell treats it either as an alias (see Aliases below) or as an external command, which is located as follows.
(+) If the command name contains no `/' characters, the sequence of directories in the environment variable PATH is searched for the first occurrence of an executable file by that name, which the shell attempts to execute. However, if the command name contains one or more `/' characters, the shell attempts to execute it without performing any PATH search.
(+) If an executable file does not begin with the proper magic number or a `#!shell' sequence, it is assumed to be a shell command file, and a new shell is automatically invoked to execute it. The environment variable EXECSHELL specifies the shell which is invoked to execute such a file.
If a command cannot be found or executed, a diagnostic is printed.
A command line, or list, consists of one or more pipelines separated, and perhaps terminated by ; or &. The semicolon designates sequential execution. The ampersand designates asynchronous execution, which causes the preceding pipeline to be executed without waiting for it to finish. The process ID of each command in such a pipeline is reported, so that it may be used if necessary for a subsequent kill(1).
A list contained within parentheses such as ( list ) is executed in a subshell and may appear in place of a simple command as a filter.
If a command line is syntactically incorrect, a diagnostic is printed.
Hangup Quit Illegal instruction Trace/BPT trap IOT trap EMT trap Floating exception Killed Bus error Memory fault Bad system call Broken pipe
For an asynchronous process, its process ID is prepended to the appropriate message. If a core image is produced, ` -- Core dumped' is appended to the appropriate message.
A redirection of the form <arg causes the file arg to be used as the standard input (file descriptor 0) for the associated command.
A redirection of the form >arg causes the file arg to be used as the standard output (file descriptor 1) for the associated command. If arg does not already exist, it is created; otherwise, it is truncated at the outset.
A redirection of the form >>arg is the same as >arg, except if arg already exists the command output is always appended to the end of the file.
For example, either of the following command lines:
% date >index.txt ; pwd >>index.txt ; ls -l >>index.txt % ( date ; pwd ; ls -l ) >index.txt
creates on the file `index.txt', the current date and time, followed by the name and a long listing of the current working directory.
(+) A <- redirection causes input for the associated command to be redirected from the standard input which existed when the shell was invoked. This allows a command file to be used as a filter.
A >arg or >>arg redirection associated with any but the last command of a pipeline is ineffectual, as is a <arg redirection with any but the first.
The standard error (file descriptor 2) is never subject to redirection by the shell itself. Thus, commands may write diagnostics to a location where they have a chance to be seen. However, fd2 provides a way to redirect the diagnostic output to another location.
If the file for a redirection cannot be opened or created, a diagnostic is printed.
(+) Like the quoting behavior described above, double (") quotes cause the shell to treat characters literally. However, double quotes also allow the shell to perform parameter and variable substitution via the dollar ($) metacharacter, whereas single (') quotes and backslash (\) quotes do not.
Individual characters, and sequences of characters, are quoted when enclosed by a matched pair of double (") or single (') quotes. For example:
% awk '{ print NR "\t" $0 }' README ^ more
causes awk(1) to write each line from the `README' file, preceded by its line number and a tab, to the standard output which is piped to more(1) for viewing. The outer single quotes prevent the shell from trying to interpret any part of the string, which is then passed as a single argument to awk.
An individual backslash (\) quotes, or escapes, the next individual character. A backslash followed by a newline is a special case which allows continuation of command-line input onto the next line. Each backslash-newline sequence in the input is translated into a blank.
If a double or single quote appears but is not part of a matched pair, a diagnostic is printed.
etsh [-v] name [arg1 ...]
If the first character of name is not -, it is taken as the name of a command file, or shell script, which is opened as the standard input for a new shell instance. Thus, the new shell reads and interprets command lines from the named file.
Otherwise, name is taken as one of the shell options, and a new shell instance is invoked to read and interpret command lines from its standard input. However, notice that the -c option followed by a string is the one case where the shell does not read and interpret command lines from its standard input. Instead, the string itself is taken as a command line and executed.
In each command line, an unquoted character sequence of the form $N, where N is a digit, is treated as a positional parameter by the shell. Each occurrence of a positional parameter in the command line is substituted with the value of the Nth argument to the invocation of the shell (argN). $0 is substituted with name.
In all shell instances, $$ is substituted with the process ID of the current shell. The value is represented as a 5-digit ASCII string, padded on the left with zeros when the process ID is less than 10000.
(+) All shell instances attempt to set the special parameters in the following list. `(*)' indicates those which are always set. Otherwise, the parameter is unset when the shell cannot determine its value.
All substitution on a command line is performed before the line is interpreted. Thus, no action which alters the value of any parameter can have any effect on a reference to that parameter occurring on the same line.
A positional-parameter value may contain any number of metacharacters. Each one which is unquoted, or unescaped, within a positional-parameter value retains its special meaning when the value is substituted in a command line by the invoked shell.
Take the following two shell invocations for example:
% etsh -c '$1' 'echo Hello World! >/dev/null' % etsh -c '$1' 'echo Hello World! \>/dev/null' Hello World! >/dev/null
In the first invocation, the > in the value substituted by $1 retains its special meaning. This causes all output from echo to be redirected to /dev/null. However, in the second invocation, the meaning of > is escaped by \ in the value substituted by $1. This causes the shell to pass `>/dev/null' as a single argument to echo instead of interpreting it as a redirection.
Variables may be used both in interactive shells and in non-interactive shells. However, notice that variables are not functional when a non-interactive shell is invoked either with the -c option followed by a string or with the -t option. Such a shell only executes one command line, but setting and using a variable requires executing two command lines in the same shell, one to set it and one to use it.
A variable can either be unset or set. When unset, a variable has no value. When set, a variable has both name and value. A valid variable name is a single ASCII character, which matches either the [A-Z] range or the [a-cfjlnoq-sx-z] range, inclusive. A valid variable value can be anything from an empty string, denoted by "" or '', to whatever you can input into a syntactically correct command line with less than or equal to the maximum command line length of characters, 2048 for the sake of brevity.
For example, `set C value' sets the variable C to value, as you can see in the following examples.
% : Example One % unset C % set C % if $? -eq 1 -a "$C" = "C" echo 'C is unset.' C is unset. % : Example Two % set C '' % ( set C ) >/dev/null % if $? -eq 0 -a -z "$C" echo 'C == "'"$C"'"' C == "" % set W "Hello " % set W "$WWorld!" % if "$W" != "W" -a -n "$W" echo "$W" Hello World! % : Example Three % alias now "date '+%A, %Y-%m-%d, %T %Z';:" % alias loadavg "echo -n $H': ';\ uptime|sed 's/^.*user[s,][ ,] *//';:" % set C '( now ; loadavg )' ; : 'C == Command Line (or List)' % ( set C ) >/dev/null % if $? -eq 0 -a -n "$C" echo "C == `$C'" C == `( now ; loadavg )' % $C | awk '{ print NR "\t" $0 }' 1 Saturday, 2018-02-24, 21:52:03 UTC 2 refugio: load averages: 0.54, 0.22, 0.08
As with parameters (see Parameter substitution above), all substitution on a command line is performed before the line is interpreted. Thus, no action which alters the value of any variable can have any effect on a reference to that variable occurring on the same line.
Also, a variable value may contain any number of metacharacters. Each one which is unquoted, or unescaped, within a variable value retains its special meaning when the value is substituted in a command line.
If a variable name passed as an argument to set or unset is invalid, a diagnostic is printed. Similarly, if a variable value causes an error, a diagnostic is printed.
The meaning of each pattern character is as follows:
Any other character in a pattern matches itself in a file name.
Notice that the `.' character at the beginning of a file name, or immediately following a `/', is always special in that it must be matched explicitly. The same is true of the `/' character itself.
If the pattern contains no `/' characters, the current directory is always used. Otherwise, the specified directory is the one obtained by taking the pattern up to the last `/' before the first unquoted *, ?, or [. The matching process matches the remainder of the pattern after this `/' against the files in the specified directory.
In any event, a list of file names is obtained from the current (or specified) directory which match the given pattern. This list is sorted in ascending ASCII order, and the new sequence of arguments replaces the given pattern. The same process is carried out for each of the given pattern arguments; the resulting lists are not merged. Finally, the shell attempts to execute the command with the resulting argument list.
If a pattern argument refers to a directory which cannot be opened, a `No directory' diagnostic is printed.
If a command has only one pattern argument, a `No match' diagnostic is printed if it fails to match any files. However, if a command has more than one pattern argument, a diagnostic is printed only when they all fail to match any files. Otherwise, each pattern argument failing to match any files is removed from the argument list.
In the case where an interactive shell is not a login shell according to its argv[0], it tries to read and execute commands from the following two rc init files in sequence: /usr/local/etc/etsh.etshrc and $h/.etshrc. The same is true when the shell is invoked with the -i option, regardless of the value of argv[0].
In any case, after the shell finishes its startup actions, it then prompts the user, reads, and executes command lines from the standard input as usual.
If the shell is invoked as a login shell, it tries to read and execute commands from /usr/local/etc/etsh.logout and $h/.etsh.logout in sequence upon logout. These two rc logout files may be used if necessary for cleanup upon termination of a login session by an EOT (see End of file below) or a SIGHUP signal (see Signals below).
Notice that the shell only performs the startup and shutdown actions described above for readable, regular rc files. If any rc file is not readable, the shell ignores it and continues as normal. If any rc file is not a regular file (or a link to a regular file), the shell ignores it, prints a diagnostic, and continues as normal.
In the normal case, a SIGINT or SIGQUIT signal received by the shell during execution of any rc file causes it to cease execution of that file without terminating. Thus, it may be desirable to use the trap special command to ignore these and other signals in some cases. For example, this is particularly true for /usr/local/etc/etsh.login, /usr/local/etc/etsh.etshrc, and /usr/local/etc/etsh.logout.
The exit special command always causes the shell to terminate if it occurs in any rc file.
An interactive shell reads each command line from its terminal and appends the words of each one to the history file as a line after performing parameter substitution and word splitting.
The shell does not read the history file or have any features that allow the user to make direct use of the saved history. Such features are available via standard external commands and also via the history command found in the /usr/local/libexec/etsh-5.4.0/etsh directory. Execute `history -h' to read its documentation.
Notice that the shell never creates or removes the $h/.etsh.history file. It always leaves these actions to the user. For example:
% history -r ; history -c ; exec etsh -l
causes history to remove the existing history file (if any), to create a new (empty) one, and causes the current shell to replace itself with a new login shell, while opening the new history file. This, and future, interactive shells then save the user's command-line history as long as the history file exists.
If desired, the user can use the history file to repeat any command line as a command substitution with sed(1) and etsh. Taking the following command line and history entry for example:
% history -n 6171 Number Command Line ------ ------------ 6171 uname -s | if { fd2 -ef/dev/null \ egrep '([ONF][a-z]{2,3}BSD|Darwin|Linux)' } \ echo '(Open|Net|Free)BSD || (Mac) OS X || OS == GNU/Linux'
and then doing a:
% sed -n 6171p <$h/.etsh.history | etsh OS == GNU/Linux || (Mac) OS X || (Free|Net|Open)BSD (Open|Net|Free)BSD || (Mac) OS X || OS == GNU/Linux
causes sed to output the 6171st command line from the history file via pipe for repetition as a command substitution by etsh.
A command alias is a string that substitutes for a given command alias name set by the user. Command aliases provide a simple way to represent complex, long, or often-used commands as simple command names. Thus, if the first argument names an existing command alias, its alias string substitutes for the command alias name. Any remaining arguments are appended to the argument list.
Aliases may be used both in interactive shells and in non-interactive shells. However, notice that aliases are not functional when a non-interactive shell is invoked either with the -c option followed by a string or with the -t option. Such a shell only executes one command line, but setting and using an alias requires executing two command lines in the same shell, one to set it and one to execute it.
The shell parses each alias in a command line into a list of words from left to right, wraps it as a ( list ), re-parses it while parsing any nested aliases (up to three deep), and executes the resulting alias in a subshell on success. Three examples of alias usage follow:
% : Example One % alias s 'echo $?;:' ; alias status 's' % alias s ; alias status (echo $?;:) (s) % false % status 1 % : Example Two % alias ll 'ls -AlF' % alias ll (ls -AlF) % ll -d [A-Z]* | wc -l | tr -d ' \t' 10 % : Example Three % alias loadavg "uname -n|sed 's/\([^.]*\).*/\1/'|tr -d \\n;\ echo -n ': ';uptime|sed 's/^.*user[s,][ ,] *//';:" % alias loadavg (uname -n|sed 's/\([^.]*\).*/\1/'|tr -d \\n;\ echo -n ': ';uptime|sed 's/^.*user[s,][ ,] *//';:) % loadavg | awk '{ print NR "\t" $0 }' 1 serenity: load average: 0.49 0.39 0.29
If an alias, or its parsed result in a command line, is syntactically incorrect, a diagnostic is printed. If an alias loop error occurs, a `Too many nested aliases' diagnostic is printed.
Notice that `set P string' sets the shell prompt to string. See SetP in the /usr/local/libexec/etsh-5.4.0/etsh directory. You can source or . it if you wish to use it.
Four examples of setting your prompt follow:
% : Example One % : " This is the default prompt, but let's " ; unset P ;\ : " it to be sure. I am not root now, as you can see. " % % : Example Two % : " OK, let's see others. " % set P '=\^\) hello \(\^=\>% ' =^) hello (^=>% % : Example Three =^) hello (^=>% : " Silly! Others? OK. " =^) hello (^=>% . SetP "$u:p2\>" jneitzel:p2>% % : Example Four jneitzel:p2>% : " Or with $w and 2 more lines? OK. " jneitzel:p2>% . $h/.etsh.prompt ~/src/git/v6shell jneitzel@refugio p2>% ~/src/git/v6shell jneitzel@refugio p2>% : " In effect, the '. $h/etsh.prompt' above prepares \ things before doing a special '. SetP string'. " ~/src/git/v6shell jneitzel@refugio p2>%
Notice that an $h/.etsh.prompt file is not required in order to set and use a personal etsh prompt. That said, it can make things simpler or easier, in the same way that having an $h/.etshrc file can do so. In any case, I trust you noticed that "Example Two" above did not mention or use $h/.etsh.prompt at all; it was completely manual.
Now, while the included .etsh.prompt file is admittedly too complex with 74 lines, a simpler and shorter file can be quite effective if your prompt objective is more focused than mine is, or was, when I wrote it.
Documenting all of the appropriate bits here in the manual was quite a challenge. Hopefully, I have not added unnecessary complexity and confusion. That said, if you find room for improvement, I invite you to send me your suggestions.
If the file name contains no `/' characters, the sequence of directories in the environment variable PATH is searched for the first occurrence of a file by that name. However, if the file name contains one or more `/' characters, the shell attempts to source it without performing any PATH search. Notice that the file does not need to be executable.
The file and any arguments are treated as positional parameters (see Parameter substitution above) during execution of the file. The source command may be nested. As with command files, most shell-detected errors cause the shell to cease execution of the file. If the source command is nested and such an error occurs, all nested source commands terminate.
If SIGHUP, SIGINT, SIGQUIT, or SIGTERM is already ignored when the shell starts, it is also ignored by the current shell and all of its child processes. Otherwise, SIGINT and SIGQUIT are reset to the default action for sequential child processes, whereas SIGHUP and SIGTERM are reset to the default action for all child processes.
When a non-interactive shell executes a command file, it does not handle or ignore any signal by default. Any other non-interactive shell ignores SIGINT and SIGQUIT.
For any signal not mentioned above, the shell inherits the signal action (default or ignore) from its parent process and passes it to its child processes. Remember that the trap special command may be used to ignore signals when the shell does not do so by default.
Asynchronous child processes always ignore both SIGINT and SIGQUIT. Also, if such a process has not redirected its input with a <, |, or ^, the shell automatically redirects it to come from /dev/null.
However, if the shell is interactive and detects an error, it exits with a non-zero status if the user types an EOT at the next prompt.
Otherwise, if the shell is non-interactive and is reading commands from a file, any shell-detected error causes the shell to cease execution of that file. This results in a non-zero exit status.
A non-zero exit status returned by the shell itself is always one of the values described in the following list, each of which may be accompanied by an appropriate diagnostic:
Etsh home page: https://etsh.nl/
`The UNIX Time-Sharing System' (CACM, July, 1974):
https://etsh.nl/history/unix/
gives the theory of operation of both the system and the shell.
The Thompson shell was used as the standard command interpreter through Version 6 (V6) UNIX. Then, in Version 7 (V7) UNIX, it was replaced by the Bourne shell. However, the Thompson shell was still distributed with the system as osh because of known portability problems with the Bourne shell's memory management in Version 7 (V7) UNIX.
Notice that certain shell oddities were historically undocumented in this manual page. Particularly noteworthy is the fact that there is no such thing as a usage error. Thus, the following shell invocations are all perfectly valid:
etsh -cats_are_nice!!! ': "Good kitty =)"' etsh -tabbies_are_too! etsh -s
The first two cases correspond to the -c and -t options respectively; the third case corresponds to the - option.
Set-ID execution denied
If the shell did support set-ID execution, it could possibly allow a user to violate the security policy on a host where the shell is used. For example, if the shell were running a setuid-root command file, a regular user could possibly invoke an interactive root shell as a result.
This is not a bug. It is simply how the shell works. Thus, etsh does not support set-ID execution. This is a proactive measure to avoid problems, nothing more.
March 28, 2019 | etsh-5.4.0 |