OILS / doc / quirks.md View on Github | oils.pub

150 lines, 94 significant
1---
2default_highlighter: oils-sh
3---
4
5OSH Quirks
6==========
7
8This document describes corner cases in OSH.
9
10Related: [Known Differences](known-differences.html).
11
12<div id="toc">
13</div>
14
15## For Bash Compatibility
16
17### The meaning of `()` on the RHS
18
19In Oils, **values** are tagged with types like `Str` and `AssocArray`, as
20opposed to the *locations* of values (cells). The construct `()` on the RHS
21generates an instance of a distinct type `InitializerList`, which modifies the
22content of the LHS that it is assigned to.
23
24This statement binds an empty indexed array to the name `x`:
25
26 x=() # indexed by integers
27
28This clears the content of the associative array.
29
30 declare -A x=() # indexed by strings, because of -A
31
32These assign elements.
33
34 declare -a x=(one two) # set elements
35 declare -a x=(['k']=v) # set an element to the index $((k))
36 declare -A x=(['k']=v) # set an element to the key 'k'
37
38This is not supported:
39
40 declare -A x=(key value) # Error in osh
41
42When the variable does not exist and a type is not specified, the assignment
43creates an indexed array and applies the `InitializerList` to the created
44array.
45
46 declare x=(one two) # creates an indexed array
47 declare x=(['k']=v) # creates an indexed array
48
49**Quirk (osh <= 0.27.0)**: The construct `()` had an ambiguous type, which was
50either `BashArray` or `BashAssoc` depending on its content and the context.
51When it's clear from the context, `()` meant an empty **associative** array:
52
53 declare -A x=() # indexed by strings, because of -A
54
55This was only applied when the array is empty. Otherwise the type was
56determined by the literal:
57
58 declare x=(one two) # indexed array
59 declare x=(['k']=v) # associative array
60
61These were redundant but OK:
62
63 declare -a x=(one two) # indexed array
64 declare -A x=(['k']=v) # associative array
65
66These produced errors:
67
68 declare -A x=(one two) # inconsistent
69 declare -a x=(['k']=v) # inconsistent
70
71## Interactive Shell
72
73### With job control, the DEBUG trap is disabled for the last part of a pipeline
74
75First, some background. These two shell features are fundamentally
76incompatible:
77
78- Job control: e.g. putting a pipeline in a process group, so it can be
79 suspended and cancelled all at once.
80- `shopt -s lastpipe` semantics: the last part of a pipeline can (sometimes) be
81 run in the current shell.
82 - [OSH]($xref) uses it by default because it makes `echo hi | read myvar` work. So
83 [OSH]($xref) is like [zsh]($xref), but unlike [bash](xref).
84
85As evidence of this incompatibility, note that:
86
87- [bash]($xref) simply ignores the `shopt -s lastpipe` setting in job control
88 shells
89- [zsh]($xref) doesn't allow you to suspend some pipelines
90
91---
92
93Now that we have that background, note that there's is a **third** feature that
94interacts: the `DEBUG` trap.
95
96[OSH]($xref) emulates the [bash]($xref) `DEBUG` trap, which runs before "leaf"
97commands like `echo hi`, `a=b`, etc.
98
99If we run this trap before the last part of a pipeline, **and** that part is
100run in the current shell (`lastpipe`), then the DEBUG trap makes an existing
101race condition worse.
102
103For example, in
104
105 echo hi | cat
106
107there's nothing stopping `echo hi` from finishing before `cat` is even started,
108which means that `cat` can't join the process group of the leader.
109
110So we simply disable the `DEBUG` trap for the last part of the pipeline, but
111**only** when job control is enabled. This won't affect debugging batch
112programs.
113
114Related issues in other shells:
115
116- bash: <https://superuser.com/questions/1084406/chained-pipes-in-bash-throws-operation-not-permitted>
117- fish: <https://github.com/fish-shell/fish-shell/issues/7474>
118
119
120<!--
121
122### errexit message and optimized subshells
123
124For all shells:
125
126 sh -c 'date'
127
128gets rewritten into:
129
130 sh -c 'exec date'
131
132That is, they **reuse the parent process**.
133
134Most shells don't print any diagnostic info when `errexit` is on. However, YSH
135does:
136
137 osh -o errexit -c 'false'
138 [ -c flag ]:1: fatal: Exiting with status 1
139
140`false` is a builtin rather than an external process, so YSH can print that
141message. But when running an external process, the message is lost:
142
143 osh -o errexit -c 'env false'
144 (silently fails with code 1)
145-->
146
147## Related
148
149- The doc on [warts](warts.html) relates to YSH.
150