/dis/sh.dis
) are very slow. This is extremely strange, because usually the speed of applications written in Limbo (in JIT mode) is between the speed of C and fast scripting languages ​​like Perl. Therefore, full-fledged applications on sh
will not work. But, nevertheless, for every sneeze, uncovering Limbo is also inconvenient, so all sorts of starting scripts and other small things anyway have to be written on sh
. The second serious drawback - the inconvenience of using the text console, the lack of command history, autocompletion, easy editing when entering is very annoying (but I was just told about the rlwrap utility, running emu through rlwrap -a
seems to solve this problem). The third - the syntax of this shell is unusual using unmatched quotes, which, when trying to highlight the syntax of its scripts using the highlight for absolutely any other shell (due to the lack of a ready highlight for infernovskogo) leads to a complete nightmare. I was going to solve this problem today by implementing syntax highlighting for vim, for which I sat down to deal with the shell ... and as a result, instead of syntax highlighting, I could not resist and write this article. :)/dis/sh.dis
out of the box is impressive! There is not even conditional operators and cycles! No features. And then what is there, and how can one exist under these conditions? Now see. So what is:exit
, run
and load
; unquote
used commands for working with strings are quote
and unquote
. As for run
, it simply executes the specified script in the current shell (analog .
Or source
in bash).load
- yes, this is a bomb! It allows loading additional modules into the current shell that are written in Limbo and allow you to add absolutely any functionality to the shell - if, for, functions, exceptions, regular expressions, mathematical operations, etc. etc. There is also the unload
command that allows you to unload
these modules dynamically. :) Nevertheless, even without loadable modules, the shell is absolutely complete and functional - which I will prove at the end of the article by implementing on “bare sh” if and for!#
. :).dis
files) and scripts (files in which the first line is shebang #!
);
&
|
>
, >>
and <
; echo one two | wc 1 2 8
cmd <stdin.txt >stdout.txt >[2]stderr.txt
cmd >[1=2]
(redirect stdin to stderr)cmd <[3]file.txt
(command runs with additional file descriptor 3 open for reading from file)cmda |[2] cmdb
(instead of stdout of the first command, stderr is sent to the input of the pipeline, and stdout is displayed simply on the screen)cmda |[1=2] cmdb
(stderr is sent to the pipeline again, not stdout cmda, but it connects not to stdin, but to stdout cmdb - which is opened for reading, not a record, of course)cmd <>in_pipe <>[1]out_pipe
(stdin and stdout are open for reading and writing to different files at the same time)cmd <{;;} >{;;}
(both command blocks run in parallel with cmd, while cmd takes two parameters — filenames a la /fd/
, which are connected via pipe to stdout first command block and stdin second command block)cmp
files with its help can compare not the files, but the results of the work of two other commands: cmp <{ ls /dir1 } <{ ls /dir2 }
.$status
environment variable (the prefix “fail:” will be automatically deleted from it, if the exception text except “fail:” contained nothing more, then $status
will contain the string “failed”). ; echo 'quote '' <-- here' quote ' <-- here
"{}
is described below, but for now consider it an analogue ``
bash-a): ; cr="{unicode -t 0D} ; lf="{unicode -t 0A} ; tab="{unicode -t 09} ; echo -n 'a'$cr$lf'b'$tab'c' | xd -1x 0000000 61 0d 0a 62 09 63 0000006
; { echo one; echo two } one two ; '{ echo one; echo two }' one two
sh
checks the syntax of command blocks and reformats them into more compact strings. ; echo { cmd | } sh: stdin: parse error: syntax error ; echo { cmd | cmd } {cmd|cmd} ; echo { echo one echo two } {echo one;echo two}
; echo one two three one two three ; echo (one two) (three) one two three ; (echo () (one (two three))) one two three ; ({echo Hello, $1!} World) Hello, World!
^
operator. It can be applied to two lists containing either the same number of elements (then elements are concatenated in pairs), or one of the lists must contain only one element, then it will be concatenated with each element of the second list. As a special case, if both lists contain only one element at a time, the usual concatenation of two strings is obtained. ; echo (abc) ^ (1 2 3) a1 b2 c3 ; echo (ab) ^ 1 a1 b1 ; echo 1 ^ (ab) 1a 1b ; echo a ^ b ab
; a = 'World' ; echo Hello, $a! Hello, World!
int execvpe(const char *file, char *const argv[], char *const envp[]);
environment variables are just files in the /env
directory. This gives interesting possibilities, for example, an application can provide access to its environment variables by other applications over the network — simply by exporting (via listen (1) and export (4) ) your /env
./env
. But it should be borne in mind that the current sh keeps a copy of all environment variables in memory, so changes made through the files in /env
will see the applications being launched, but not the current sh. On the other hand, by changing the variables in the usual way for the shell (through the operator =
) you automatically update the files in /env
..
, ..
and containing /
). Variables with such names will be available only in the current sh, and launched applications will not see them.a=()
or implicitly through a=
) is the deletion of the variable.$var
is getting a list of strings from a var variable, and there may be zero, one, or several$#var
is getting the number of elements in the $var
string list$"var
is the concatenation of all the elements of the list of strings from $var
into one string (separated by a space) ; var = 'first str' second ; echo $var first str second ; echo $#var 2 ; echo $"var first str second
$var
and $"var
not visually different, because echo
displays all its parameters separated by a space, and $"
also unites all the elements of a variable through a space. But the first echo
command received two parameters, and the last one.^
(which, as you remember, works with lists of strings, not strings). This is convenient, although unaccustomed to it may be unexpected: ; flags = abc ; files = file1 file2 ; echo -$flags $files.b -a -b -c file1.b file2.b ; echo { echo -$flags $files.b } {echo -^$flags $files^.b}
; list = abcd ; (head tail) = $list ; echo $head a ; echo $tail bcd ; (xy) = (3 5) ; (xy) = ($y $x) ; echo 'x='^$x 'y='^$y x=5 y=3
$*
variable, plus individual parameters are in the $1
, $2
, ... variables.=
and :=
. The first will change the value of an existing variable or create a new variable in the current scope. The second one always creates a new variable, overlapping the old value of the variable with that name (if it existed) to the end of the current block. ; a = 1 ; { a = 2 ; echo $a } 2 ; echo $a 2 ; { a := 3; echo $a } 3 ; echo $a 2 ; { b := 4; echo $b } 4 ; echo b is $b b is
$
character. And it can be any string - in single quotes or another variable. ; 'var' = 10 ; echo $var 10 ; ref = 'var' ; echo $$ref 10 ; echo $'var' 10 ; echo $('va' ^ r) 10
`{}
(executes the command, and returns what it output to stdout as a list of strings; the elements of the list are separated using the value of the $ifs
variable; if it is not defined, it is considered that it contains a string of three characters: space, tab and line feed)"{}
(executes the command, and returns what it output to stdout as a single line - the concatenation of all the lines output by the command)ls
call. Given that file names may contain spaces, the standard `{}
behavior doesn’t work for us - we only need to separate list items by line breaks. ; ifs := "{unicode -t 0A} ; files := `{ ls / } ; echo $#files 82 ; ls / | wc -l 82
; files2 := /* ; echo $#files2 82
; run /lib/sh/profile ; load std ; unload std ; exit
${ }
and return (do not output to stdout, namely, return - as it does, for example, referring to the value of a variable) a regular string - i.e. they should be used in the parameters of the usual commands or in the right part of the assignment of values ​​into variables. For example, the command ${quote}
escapes the list of lines passed to it into one line, and ${unquote}
performs the inverse operation turning one line into a list of lines. ; list = 'ab' cd ; echo $list abcd ; echo ${quote $list} 'ab' cd ; echo ${unquote ${quote $list}} abcd
$*
, $1
, etc. ; hello = { echo Hello, $1! } ; $hello World Hello, World!
; greet = {echo $1, $2!} ; hi = $greet Hi ; hello = $greet Hello ; $hi World Hi, World! ; $hello World Hello, World!
; list = abcdef ; { echo $3 } $list c
; do = { $* } ; dont = { } ; if = { (cmd cond) := $* { $2 $cmd } $cond $do $dont } ; for = { (var in list) := $* code := $$#* $iter $var $code $list } ; iter = { (var code list) := $* (cur list) := $list (next unused) := $list $if {$var=$cur; $code; $iter $var $code $list} $next } ; $for i in 10 20 30 { echo i is $i } i is 10 i is 20 i is 30 ;
#!/dis/sh -n
.loaded
builtin command lists all the builtin commands from all loaded modules. And whatis
built-in command displays information on variables, functions, commands, etc.$autoload
variable with the list of shell modules, then these modules will be automatically loaded into every shell that is started.&&
and ||
. These operators are available in “bare sh”, but are converted to a call to the builtin commands and
and or
, which are not in “bare sh”, they are from the std module (so that using &&
and ||
possible only after load std
). ; echo { cmd && cmd } {and {cmd} {cmd}} ; echo { cmd || cmd } {or {cmd} {cmd}}
$apid
, $prompt
, - , sh
., .^
=
$var
, $#var
$"var
/bin/false
:) run
).and
, or
, if
)!
, ~
, no
)apply
, for
, while
, getlines
)fn
, subfn
)raise
, rescue
, status
)${hd}
, ${tl}
, ${index}
, ${split}
, ${join}
)Source: https://habr.com/ru/post/161859/
All Articles