| 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).
 | 
| 21 | 
 | 
| 22 | This statement binds an empty indexed array to the name `x`:
 | 
| 23 | 
 | 
| 24 |     x=()  # indexed by integers
 | 
| 25 | 
 | 
| 26 | **Quirk**: When it's clear from the context, `()` means an empty
 | 
| 27 | **associative** array:
 | 
| 28 | 
 | 
| 29 |     declare -A x=()  # indexed by strings, because of -A
 | 
| 30 | 
 | 
| 31 | This only applies when the array is empty.  Otherwise the type is determined by
 | 
| 32 | the literal:
 | 
| 33 | 
 | 
| 34 |     declare x=(one two)  # indexed array
 | 
| 35 |     declare x=(['k']=v)  # associative array
 | 
| 36 | 
 | 
| 37 | Redundant but OK:
 | 
| 38 | 
 | 
| 39 |     declare -a x=(one two)  # indexed array
 | 
| 40 |     declare -A x=(['k']=v)  # associative array
 | 
| 41 | 
 | 
| 42 | Errors:
 | 
| 43 | 
 | 
| 44 |     declare -A x=(one two)  # inconsistent
 | 
| 45 |     declare -a x=(['k']=v)  # inconsistent
 | 
| 46 | 
 | 
| 47 | ## Interactive Shell
 | 
| 48 | 
 | 
| 49 | ### With job control, the DEBUG trap is disabled for the last part of a pipeline
 | 
| 50 | 
 | 
| 51 | First, some background.  These two shell features are fundamentally
 | 
| 52 | incompatible:
 | 
| 53 | 
 | 
| 54 | - Job control: e.g. putting a pipeline in a process group, so it can be
 | 
| 55 |   suspended and cancelled all at once.
 | 
| 56 | - `shopt -s lastpipe` semantics: the last part of a pipeline can (sometimes) be
 | 
| 57 |   run in the current shell.
 | 
| 58 |   - [OSH]($xref) uses it by default because it makes `echo hi | read myvar` work.  So
 | 
| 59 |     [OSH]($xref) is like [zsh]($xref), but unlike [bash](xref).
 | 
| 60 | 
 | 
| 61 | As evidence of this incompatibility, note that:
 | 
| 62 | 
 | 
| 63 | - [bash]($xref) simply ignores the `shopt -s lastpipe` setting in job control
 | 
| 64 |   shells
 | 
| 65 | - [zsh]($xref) doesn't allow you to suspend some pipelines
 | 
| 66 | 
 | 
| 67 | ---
 | 
| 68 | 
 | 
| 69 | Now that we have that background, note that there's is a **third** feature that
 | 
| 70 | interacts: the `DEBUG` trap.
 | 
| 71 | 
 | 
| 72 | [OSH]($xref) emulates the [bash]($xref) `DEBUG` trap, which runs before "leaf"
 | 
| 73 | commands like `echo hi`, `a=b`, etc.
 | 
| 74 | 
 | 
| 75 | If we run this trap before the last part of a pipeline, **and** that part is
 | 
| 76 | run in the current shell (`lastpipe`), then the DEBUG trap makes an existing
 | 
| 77 | race condition worse.
 | 
| 78 | 
 | 
| 79 | For example, in
 | 
| 80 | 
 | 
| 81 |     echo hi | cat
 | 
| 82 | 
 | 
| 83 | there's nothing stopping `echo hi` from finishing before `cat` is even started,
 | 
| 84 | which means that `cat` can't join the process group of the leader.
 | 
| 85 | 
 | 
| 86 | So we simply disable the `DEBUG` trap for the last part of the pipeline, but
 | 
| 87 | **only** when job control is enabled.  This won't affect debugging batch
 | 
| 88 | programs.
 | 
| 89 | 
 | 
| 90 | Related issues in other shells:
 | 
| 91 | 
 | 
| 92 | - bash: <https://superuser.com/questions/1084406/chained-pipes-in-bash-throws-operation-not-permitted>
 | 
| 93 | - fish: <https://github.com/fish-shell/fish-shell/issues/7474>
 | 
| 94 | 
 | 
| 95 | 
 | 
| 96 | <!--
 | 
| 97 | 
 | 
| 98 | ### errexit message and optimized subshells
 | 
| 99 | 
 | 
| 100 | For all shells:
 | 
| 101 | 
 | 
| 102 |     sh -c 'date'
 | 
| 103 | 
 | 
| 104 | gets rewritten into:
 | 
| 105 | 
 | 
| 106 |     sh -c 'exec date'
 | 
| 107 | 
 | 
| 108 | That is, they **reuse the parent process**.
 | 
| 109 | 
 | 
| 110 | Most shells don't print any diagnostic info when `errexit` is on.  However, YSH
 | 
| 111 | does:
 | 
| 112 | 
 | 
| 113 |     osh -o errexit -c 'false'
 | 
| 114 |     [ -c flag ]:1: fatal: Exiting with status 1
 | 
| 115 | 
 | 
| 116 | `false` is a builtin rather than an external process, so YSH can print that
 | 
| 117 | message.  But when running an external process, the message is lost:
 | 
| 118 | 
 | 
| 119 |     osh -o errexit -c 'env false'
 | 
| 120 |     (silently fails with code 1)
 | 
| 121 | -->
 | 
| 122 | 
 | 
| 123 | ## Related 
 | 
| 124 | 
 | 
| 125 | - The doc on [warts](warts.html) relates to YSH.
 | 
| 126 | 
 |