1 | ---
|
2 | default_highlighter: oils-sh
|
3 | ---
|
4 |
|
5 | OSH Quirks
|
6 | ==========
|
7 |
|
8 | This document describes corner cases in OSH.
|
9 |
|
10 | Related: [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 |
|
19 | In Oils, **values** are tagged with types like `Str` and `AssocArray`, as
|
20 | opposed to the *locations* of values (cells). The construct `()` on the RHS
|
21 | generates an instance of a distinct type `InitializerList`, which modifies the
|
22 | content of the LHS that it is assigned to.
|
23 |
|
24 | This statement binds an empty indexed array to the name `x`:
|
25 |
|
26 | x=() # indexed by integers
|
27 |
|
28 | This clears the content of the associative array.
|
29 |
|
30 | declare -A x=() # indexed by strings, because of -A
|
31 |
|
32 | These 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 |
|
38 | This is not supported:
|
39 |
|
40 | declare -A x=(key value) # Error in osh
|
41 |
|
42 | When the variable does not exist and a type is not specified, the assignment
|
43 | creates an indexed array and applies the `InitializerList` to the created
|
44 | array.
|
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
|
50 | either `BashArray` or `BashAssoc` depending on its content and the context.
|
51 | When it's clear from the context, `()` meant an empty **associative** array:
|
52 |
|
53 | declare -A x=() # indexed by strings, because of -A
|
54 |
|
55 | This was only applied when the array is empty. Otherwise the type was
|
56 | determined by the literal:
|
57 |
|
58 | declare x=(one two) # indexed array
|
59 | declare x=(['k']=v) # associative array
|
60 |
|
61 | These were redundant but OK:
|
62 |
|
63 | declare -a x=(one two) # indexed array
|
64 | declare -A x=(['k']=v) # associative array
|
65 |
|
66 | These 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 |
|
75 | First, some background. These two shell features are fundamentally
|
76 | incompatible:
|
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 |
|
85 | As 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 |
|
93 | Now that we have that background, note that there's is a **third** feature that
|
94 | interacts: the `DEBUG` trap.
|
95 |
|
96 | [OSH]($xref) emulates the [bash]($xref) `DEBUG` trap, which runs before "leaf"
|
97 | commands like `echo hi`, `a=b`, etc.
|
98 |
|
99 | If we run this trap before the last part of a pipeline, **and** that part is
|
100 | run in the current shell (`lastpipe`), then the DEBUG trap makes an existing
|
101 | race condition worse.
|
102 |
|
103 | For example, in
|
104 |
|
105 | echo hi | cat
|
106 |
|
107 | there's nothing stopping `echo hi` from finishing before `cat` is even started,
|
108 | which means that `cat` can't join the process group of the leader.
|
109 |
|
110 | So 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
|
112 | programs.
|
113 |
|
114 | Related 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 |
|
124 | For all shells:
|
125 |
|
126 | sh -c 'date'
|
127 |
|
128 | gets rewritten into:
|
129 |
|
130 | sh -c 'exec date'
|
131 |
|
132 | That is, they **reuse the parent process**.
|
133 |
|
134 | Most shells don't print any diagnostic info when `errexit` is on. However, YSH
|
135 | does:
|
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
|
141 | message. 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 |
|