brush como /bin/sh: pasa el banco POSIX entero y no construye una sola receta autotools
Medido en el worker, con el único cambio de /bin/sh en una copia por hardlinks del lab (sed/grep/awk siguen siendo busybox) y stores tirables. Control busybox: 3/3 recetas selladas. brush: 0/3. Lo que pasa: 56/57 del banco POSIX (igual que busybox), 0 rechazos sobre las 1130 fases de shell del corpus, parsea el configure de ffmpeg, funciona como argv[0]=sh y con el patrón de las cards de arje. Lo que falla: el configure corre entero pero el libtool que GENERA sale con un nivel de escapado de menos en 25 líneas, y ese fichero ya no lo parsea ningún shell. El parser de brush está bien — parsea sin quejarse el libtool del control. Mecanismo exacto no determinado: echo, sustitución de comando y el propio func_quote_for_eval de libtool dan idéntico en ambos shells aislados. Y un dato independiente del bug: 813us de arranque contra 91us de busybox (8,9x), sobre decenas de miles de shells por configure. El binario son 7,25 MB contra los 1,23 MB del busybox con sus 401 applets. De paso: brush selló con el mismo hash que traía build-state del otro hub. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# Evidencia del veredicto del paso 3 de docs/plan-botar-busybox.md (2026-09-21).
|
||||
# libtool generado por el mismo configure de pkgconf, con dos shells distintos como /bin/sh.
|
||||
# < = control, busybox 1.36.1 sh | > = brush 0.4.0
|
||||
# 25 líneas difieren, todas por un nivel de escapado de menos en el lado de brush.
|
||||
# El fichero de la derecha ya no lo parsea NINGÚN shell (rc=2 en ambos, línea 368).
|
||||
|
||||
62c62
|
||||
< ECHO="printf %s\\n"
|
||||
---
|
||||
> ECHO="printf %s\n"
|
||||
111c111
|
||||
< SP2NL="tr \\040 \\012"
|
||||
---
|
||||
> SP2NL="tr \040 \012"
|
||||
114c114
|
||||
< NL2SP="tr \\015\\012 \\040\\040"
|
||||
---
|
||||
> NL2SP="tr \015\012 \040\040"
|
||||
132c132
|
||||
< file_magic_cmd="\$MAGIC_CMD"
|
||||
---
|
||||
> file_magic_cmd="$MAGIC_CMD"
|
||||
144c144
|
||||
< sharedlib_from_linklib_cmd="printf %s\\n"
|
||||
---
|
||||
> sharedlib_from_linklib_cmd="printf %s\n"
|
||||
163c163
|
||||
< old_postinstall_cmds="chmod 644 \$oldlib~\$RANLIB \$tool_oldlib"
|
||||
---
|
||||
> old_postinstall_cmds="chmod 644 $oldlib~$RANLIB $tool_oldlib"
|
||||
176c176
|
||||
< global_symbol_pipe="/bin/sed -n -e 's/^.*[ ]\\([ABCDGIRSTW][ABCDGIRSTW]*\\)[ ][ ]*\\([_A-Za-z][_A-Za-z0-9]*\\)\$/\\1 \\2 \\2/p' | /bin/sed '/ __gnu_lto/d'"
|
||||
---
|
||||
> global_symbol_pipe="/bin/sed -n -e 's/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$/\1 \2 \2/p' | /bin/sed '/ __gnu_lto/d'"
|
||||
179c179
|
||||
< global_symbol_to_cdecl="/bin/sed -n -e 's/^T .* \\(.*\\)\$/extern int \\1();/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(.*\\)\$/extern char \\1;/p'"
|
||||
---
|
||||
> global_symbol_to_cdecl="/bin/sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(.*\)$/extern char \1;/p'"
|
||||
185c185
|
||||
< global_symbol_to_c_name_address="/bin/sed -n -e 's/^: \\(.*\\) .*\$/ {\"\\1\", (void *) 0},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(.*\\)\$/ {\"\\1\", (void *) \\&\\1},/p'"
|
||||
---
|
||||
> global_symbol_to_c_name_address="/bin/sed -n -e 's/^: \(.*\) .*$/ {"\1", (void *) 0},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(.*\)$/ {"\1", (void *) \&\1},/p'"
|
||||
188c188
|
||||
< global_symbol_to_c_name_address_lib_prefix="/bin/sed -n -e 's/^: \\(.*\\) .*\$/ {\"\\1\", (void *) 0},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(lib.*\\)\$/ {\"\\1\", (void *) \\&\\1},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(.*\\)\$/ {\"lib\\1\", (void *) \\&\\1},/p'"
|
||||
---
|
||||
> global_symbol_to_c_name_address_lib_prefix="/bin/sed -n -e 's/^: \(.*\) .*$/ {"\1", (void *) 0},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(lib.*\)$/ {"\1", (void *) \&\1},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(.*\)$/ {"lib\1", (void *) \&\1},/p'"
|
||||
261c261
|
||||
< libname_spec="lib\$name"
|
||||
---
|
||||
> libname_spec="lib$name"
|
||||
265c265
|
||||
< library_names_spec="\$libname\$release\$shared_ext\$versuffix \$libname\$release\$shared_ext\$major \$libname\$shared_ext"
|
||||
---
|
||||
> library_names_spec="$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext"
|
||||
268c268
|
||||
< soname_spec="\$libname\$release\$shared_ext\$major"
|
||||
---
|
||||
> soname_spec="$libname$release$shared_ext$major"
|
||||
280c280
|
||||
< finish_cmds="PATH=\\\"\\\$PATH:/sbin\\\" ldconfig -n \$libdir"
|
||||
---
|
||||
> finish_cmds="PATH="\$PATH:/sbin" ldconfig -n $libdir"
|
||||
317c317
|
||||
< reload_cmds="\$LD\$reload_flag -o \$output\$reload_objs"
|
||||
---
|
||||
> reload_cmds="$LD$reload_flag -o $output$reload_objs"
|
||||
320c320
|
||||
< old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs~\$RANLIB \$tool_oldlib"
|
||||
---
|
||||
> old_archive_cmds="$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $tool_oldlib"
|
||||
350c350
|
||||
< export_dynamic_flag_spec="\$wl--export-dynamic"
|
||||
---
|
||||
> export_dynamic_flag_spec="$wl--export-dynamic"
|
||||
353c353
|
||||
< whole_archive_flag_spec="\$wl--whole-archive\$convenience \$wl--no-whole-archive"
|
||||
---
|
||||
> whole_archive_flag_spec="$wl--whole-archive$convenience $wl--no-whole-archive"
|
||||
365,369c365,369
|
||||
< archive_cmds="\$CC -shared \$pic_flag \$libobjs \$deplibs \$compiler_flags \$wl-soname \$wl\$soname -o \$lib"
|
||||
< archive_expsym_cmds="echo \\\"{ global:\\\" > \$output_objdir/\$libname.ver~
|
||||
< cat \$export_symbols | \$SED -e \\\"s/\\\\(.*\\\\)/\\\\1;/\\\" >> \$output_objdir/\$libname.ver~
|
||||
< echo \\\"local: *; };\\\" >> \$output_objdir/\$libname.ver~
|
||||
< \$CC -shared \$pic_flag \$libobjs \$deplibs \$compiler_flags \$wl-soname \$wl\$soname \$wl-version-script \$wl\$output_objdir/\$libname.ver -o \$lib"
|
||||
---
|
||||
> archive_cmds="$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib"
|
||||
> archive_expsym_cmds="echo "{ global:" > $output_objdir/$libname.ver~
|
||||
> cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
|
||||
> echo "local: *; };" >> $output_objdir/$libname.ver~
|
||||
> $CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib"
|
||||
387c387
|
||||
< hardcode_libdir_flag_spec="\$wl-rpath \$wl\$libdir"
|
||||
---
|
||||
> hardcode_libdir_flag_spec="$wl-rpath $wl$libdir"
|
||||
426c426
|
||||
< export_symbols_cmds="\$NM \$libobjs \$convenience | \$global_symbol_pipe | \$SED 's/.* //' | sort | uniq > \$export_symbols"
|
||||
---
|
||||
> export_symbols_cmds="$NM $libobjs $convenience | $global_symbol_pipe | $SED 's/.* //' | sort | uniq > $export_symbols"
|
||||
+101
-2
@@ -141,8 +141,9 @@ frente C** y se pueden hacer hoy.
|
||||
2. **Declarar la munición que ya existe.** `uutils`, `findutils`, `findutils-xargs`, `diffutils`,
|
||||
`brush`, `gzip`, `libarchive` a los perfiles de `targets.toml`. Terminado cuando `build-state.py`
|
||||
los marca con perfil y la imagen los lleva.
|
||||
3. **Medir `brush` como `/bin/sh`.** Correr con él los `configure` de tres recetas autotools del
|
||||
corpus y el `product-boot-test.sh`. Terminado con un veredicto escrito: sirve, o falla en X.
|
||||
3. ~~**Medir `brush` como `/bin/sh`.**~~ **HECHO, 2026-09-21 — veredicto abajo: no sirve todavía
|
||||
para el sandbox.** Queda pendiente medirlo como `/bin/sh` del PRODUCTO (cards + `product-boot-test.sh`),
|
||||
que es un consumidor distinto y mucho más blando.
|
||||
4. **Las dos recetas de adopción baratas** (`uutils/procps`, `uutils/util-linux`), previa
|
||||
verificación de madurez upstream. Cubren ~60 applets de los 148 «ya cubiertos por C».
|
||||
5. **`grep` y `sed` POSIX en Rust.** Es la escritura de mejor relación coste/beneficio y desbloquea
|
||||
@@ -174,3 +175,101 @@ aparecería en boot, en otra máquina, semanas después.
|
||||
eligió; cuáles se ejecutan en una imagen viva no está medido. Eso abarataría el paso 1.
|
||||
- **`uutils/procps` y `uutils/util-linux` no se verificaron upstream.** Los ~60 applets que les
|
||||
atribuyo son una hipótesis de trabajo.
|
||||
|
||||
---
|
||||
|
||||
# Veredicto del paso 3 — `brush` como `/bin/sh`, MEDIDO
|
||||
|
||||
**Fecha:** 2026-09-21 · **Dónde:** `dev.gioser.net`, todo bajo `flock -o work/.farm-build.lock` ·
|
||||
**Contra qué:** busybox 1.36.1 `sh` (ash) como control, en la misma caja y la misma corrida.
|
||||
|
||||
> **No sirve todavía como `/bin/sh` del SANDBOX DE BUILD.** Pasa el banco POSIX igual que busybox,
|
||||
> parsea todo el corpus, y aun así **0 de 3 recetas autotools construyen** (control: 3/3). Lo que
|
||||
> falla no es su parser: es que el `libtool` que su `configure` genera sale con un nivel de escapado
|
||||
> de menos y después no lo parsea ningún shell, ni él ni busybox.
|
||||
|
||||
## Lo que se construyó
|
||||
|
||||
`takana build recipes/brush.toml` en el worker selló **`b3:715237c78edd…`** — **el mismo hash que
|
||||
`build-state.json` traía del otro hub.** Dos máquinas distintas, bytes idénticos: reproducibilidad
|
||||
confirmada de paso, que no era el objetivo de la prueba.
|
||||
|
||||
Binario: ELF x86-64 **estático** (sin loader), `brush 0.4.0`, **7,25 MB** — contra **1,23 MB** del
|
||||
busybox que trae los 401 applets. Botar busybox por brush, hoy, **engorda** la imagen.
|
||||
|
||||
## Lo que PASA
|
||||
|
||||
| prueba | brush | control busybox |
|
||||
|---|---|---|
|
||||
| **Banco POSIX** — 57 casos (`scripts/sh-banco-posix.sh`) | **56/57** | 56/57 |
|
||||
| **Las 1130 fases de shell del corpus** (`sh -n` sobre las fases de las 549 recetas que las tienen) | **0 rechazadas** | 0 rechazadas |
|
||||
| **`configure` de ffmpeg** — 8345 líneas, `sh -n` | rc=0 | rc=0 |
|
||||
| **Invocado como `sh`** (argv[0]) | ✅ | ✅ |
|
||||
| **El patrón de las cards de arje** (`sh -c '…; exec …'`) | ✅ | ✅ |
|
||||
|
||||
La única divergencia del banco: **`shift` más allá de `$#` devuelve 2**, y POSIX/busybox/bash
|
||||
devuelven 1. Cosmético, pero un `set -e` que haga `shift` de más se comporta distinto.
|
||||
|
||||
⚠ **Y el banco NO es prueba de suficiencia** — esto es lo que hay que recordar del ejercicio:
|
||||
**brush lo pasa entero y aun así no construye una sola receta autotools.** Un banco de casos
|
||||
sintéticos mide lo que a uno se le ocurre preguntar; el consumidor real mide lo que hay.
|
||||
|
||||
## Lo que FALLA
|
||||
|
||||
**Tres recetas autotools reales, con el único cambio de `/bin/sh` en una copia del lab:**
|
||||
|
||||
```sh
|
||||
cp -al /opt/takana/.dev-fs/alpine $L/alpine # copia con hardlinks, el lab compartido intacto
|
||||
cp …-brush/usr/bin/brush $L/alpine/bin/brush
|
||||
ln -sfn /bin/brush $L/alpine/bin/sh # ← lo ÚNICO que cambia; sed/grep/awk siguen siendo busybox
|
||||
flock -o work/.farm-build.lock env TAKANA_ROOTFS=$L/alpine \
|
||||
./target/release/takana --store <store-tirable> build recipes/<r>.toml
|
||||
```
|
||||
|
||||
| receta | control (busybox sh) | brush |
|
||||
|---|---|---|
|
||||
| `flex` | ✅ sellada | ❌ `make[1]: Error 2` |
|
||||
| `pkgconf` | ✅ sellada | ❌ `./libtool: syntax error at line 368 col 30` |
|
||||
| `oniguruma` | ✅ sellada | ❌ `make[1]: Error 2` |
|
||||
|
||||
### La causa, hasta donde está medida
|
||||
|
||||
**El `configure` corre entero y bien bajo brush.** Lo que sale mal es el `libtool` que ese
|
||||
`configure` GENERA. Regenerado el mismo fichero con cada shell y comparado, **25 líneas difieren, y
|
||||
todas en el mismo sentido: falta un nivel de escapado.**
|
||||
|
||||
```
|
||||
control (busybox) brush
|
||||
línea 62 ECHO="printf %s\\n" ECHO="printf %s\n"
|
||||
línea 132 file_magic_cmd="\$MAGIC_CMD" file_magic_cmd="$MAGIC_CMD"
|
||||
línea 176 …'s/^.*[ ]\\([ABCDGIRSTW]… …'s/^.*[ ]\([ABCDGIRSTW]…
|
||||
```
|
||||
|
||||
Ese fichero ya no lo parsea **nadie** — ni brush ni busybox lo aceptan (`rc=2` los dos, misma línea
|
||||
368). Y el contraprueba cierra el caso: **el `libtool` del control lo parsea brush sin quejarse
|
||||
(`rc=0`)**. ⇒ El parser de brush está bien; lo que difiere es lo que su shell PRODUCE.
|
||||
|
||||
⚠ **El mecanismo exacto no está determinado, y conviene decirlo así en vez de inventarlo.** Las tres
|
||||
sospechas obvias se probaron aisladas y **las tres dan idéntico** en ambos shells: `echo` con barras
|
||||
invertidas, sustitución de comando preservando `\\`, y el propio `func_quote_for_eval` de libtool
|
||||
(`sed_quote_subst`) aplicado a mano. Lo medido es el diff de 25 líneas y que el `configure` termina
|
||||
bien; el resto es hipótesis.
|
||||
|
||||
## Y el otro dato, que no es un fallo pero decide
|
||||
|
||||
**Latencia de arranque: 813 µs contra 91 µs de busybox — 8,9×** (300 invocaciones de `sh -c :`).
|
||||
Un `configure` de autotools levanta decenas de miles de shells: a ese factor, el sandbox entero se
|
||||
vuelve medible más lento. Es un argumento independiente del bug, y no se arregla arreglando el bug.
|
||||
|
||||
## Qué queda de esto
|
||||
|
||||
1. **brush NO entra al sandbox de build.** El frente A sigue con busybox hasta que esto se cierre.
|
||||
2. **El frente del producto sigue abierto**: las cards, la getty y la shell interactiva no generan
|
||||
libtool. Ahí brush no tiene ninguna evidencia en contra — pero tampoco se probó, y el
|
||||
`product-boot-test.sh` es la prueba que falta.
|
||||
3. **Hay un bug upstream que reportar** (reubeno/brush): `configure` de autotools genera un `libtool`
|
||||
con un nivel de escapado de menos. El reproductor es una receta autotools cualquiera, y el
|
||||
artefacto es el diff de 25 líneas de arriba.
|
||||
4. **El paso 5 del plan (grep/sed POSIX en Rust) sube de prioridad**, porque no depende del shell.
|
||||
5. **No se tocó el lab compartido**: copia con hardlinks y stores tirables. El worker siguió
|
||||
compilando su cola en paralelo, serializado por el `flock`.
|
||||
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/bin/sh
|
||||
# Banco POSIX para candidatos a /bin/sh (SDD: docs/plan-botar-busybox.md §paso 3).
|
||||
# Uso: scripts/sh-banco-posix.sh <ruta-al-shell>
|
||||
# 57 casos POSIX ejecutados con `$SH -c`. Baseline medido 2026-09-21: busybox sh 56/57,
|
||||
# brush 0.4.0 56/57 (diverge sólo en `shift` más allá de $#: devuelve 2, POSIX/busybox 1).
|
||||
# NO es una prueba de suficiencia: el banco pasa y aun así brush no construye autotools.
|
||||
SH="$1"; [ -n "$SH" ] || { echo "uso: $0 <ruta-al-shell>"; exit 2; }
|
||||
ok=0; ko=0; KOLIST=""
|
||||
t() { # t <nombre> <esperado> <codigo>
|
||||
nombre="$1"; esperado="$2"; codigo="$3"
|
||||
got=$("$SH" -c "$codigo" 2>&1)
|
||||
if [ "$got" = "$esperado" ]; then ok=$((ok+1))
|
||||
else ko=$((ko+1)); KOLIST="$KOLIST
|
||||
✗ $nombre
|
||||
esperado: [$esperado]
|
||||
obtenido: [$got]"
|
||||
fi
|
||||
}
|
||||
|
||||
t "echo" "hola" 'echo hola'
|
||||
t "pipeline" "2" 'printf "a\nb\n" | wc -l | tr -d " "'
|
||||
t "redireccion" "x" 'echo x > /tmp/_p1 && cat /tmp/_p1'
|
||||
t "aqui-doc" "linea" 'cat <<EOF
|
||||
linea
|
||||
EOF'
|
||||
t "aqui-doc-guion" "sin-tab" 'cat <<-EOF
|
||||
sin-tab
|
||||
EOF'
|
||||
t "aqui-doc-comillado" '$NOEXPAND' 'cat <<"EOF"
|
||||
$NOEXPAND
|
||||
EOF'
|
||||
t "sust-comando" "hola" 'x=$(echo hola); echo $x'
|
||||
t "sust-backtick" "hola" 'echo `echo hola`'
|
||||
t "estado-salida" "1" 'false; echo $?'
|
||||
t "and-or" "b" 'false && echo a || echo b'
|
||||
t "for" "1 2 3" 'r=; for i in 1 2 3; do r="$r $i"; done; echo ${r# }'
|
||||
t "while-read" "a|b" 'printf "a\nb\n" | { r=; while read l; do r="$r|$l"; done; echo ${r#|}; }'
|
||||
t "case" "si" 'case abc in a*) echo si;; *) echo no;; esac'
|
||||
t "funcion" "5" 'f() { echo $(($1+$2)); }; f 2 3'
|
||||
t "local" "fuera" 'x=fuera; f() { local x=dentro; }; f; echo $x'
|
||||
t "expansion-defecto" "def" 'unset v; echo ${v:-def}'
|
||||
t "expansion-asigna" "def def" 'unset v; echo ${v:=def} $v'
|
||||
t "expansion-error" "0" 'unset v; ( echo ${v:?falta} ) 2>/dev/null; [ $? -ne 0 ] && echo 0'
|
||||
t "expansion-largo" "3" 'v=abc; echo ${#v}'
|
||||
t "quita-sufijo" "arch" 'v=arch.tar.gz; echo ${v%%.*}'
|
||||
t "quita-prefijo" "gz" 'v=arch.tar.gz; echo ${v##*.}'
|
||||
t "aritmetica" "7" 'echo $((3+4))'
|
||||
t "aritmetica-var" "6" 'x=2; echo $((x*3))'
|
||||
t "test-corchete" "si" '[ 1 -lt 2 ] && echo si'
|
||||
t "test-cadena" "si" '[ "a" = "a" ] && [ -n "a" ] && echo si'
|
||||
t "posicionales" "a:b:c" 'set -- a b c; IFS=:; echo "$*"'
|
||||
t "todos-arroba" "a b c" 'set -- a b c; echo "$@"'
|
||||
t "shift" "c" 'set -- a b c; shift 2; echo $1'
|
||||
t "getopts" "v=1 f=x" 'set -- -v -f x; v=0; while getopts vf: o; do case $o in v) v=1;; f) f=$OPTARG;; esac; done; echo "v=$v f=$f"'
|
||||
t "source-punto" "sourced" 'echo "echo sourced" > /tmp/_p2; . /tmp/_p2'
|
||||
t "subshell-aisla" "fuera" 'x=fuera; ( x=dentro ); echo $x'
|
||||
t "trap-exit" "adios" 'trap "echo adios" EXIT; true'
|
||||
t "set-e-corta" "antes" 'set -e; echo antes; false; echo despues'
|
||||
t "set-u" "0" 'set -u; ( echo $NODEF ) 2>/dev/null; [ $? -ne 0 ] && echo 0'
|
||||
t "exec-redir" "z" 'exec 3>/tmp/_p3; echo z >&3; exec 3>&-; cat /tmp/_p3'
|
||||
t "glob" "si" 'mkdir -p /tmp/_g && : > /tmp/_g/f1 && for f in /tmp/_g/f*; do [ -e "$f" ] && echo si; done'
|
||||
t "glob-sin-match" "/tmp/_nada*" 'for f in /tmp/_nada*; do echo $f; done'
|
||||
t "IFS-split" "a b c" 'IFS=,; set -- $(echo "a,b,c"); IFS=" "; echo "$*"'
|
||||
t "comillas-preservan" "a b" 'x="a b"; echo "$x"'
|
||||
t "sin-comillas-parte" "a b" 'x="a b"; echo $x'
|
||||
t "escape-barra" 'a\b' 'printf "%s\n" "a\\b"'
|
||||
t "printf-formato" "007" 'printf "%03d\n" 7'
|
||||
t "read-var" "dato" 'echo dato | { read v; echo $v; }'
|
||||
t "eval" "ev" 'c="echo ev"; eval $c'
|
||||
t "dollar-dollar" "si" '[ -n "$$" ] && echo si'
|
||||
t "PATH-busca" "si" 'command -v echo >/dev/null && echo si'
|
||||
t "anidado-sust" "ab" 'echo "$(echo a)$(echo b)"'
|
||||
t "multi-cmd-punto-coma" "a b" 'echo -n "a "; echo b'
|
||||
t "negacion" "si" 'if ! false; then echo si; fi'
|
||||
t "bucle-until" "3" 'i=0; until [ $i -ge 3 ]; do i=$((i+1)); done; echo $i'
|
||||
t "break-continue" "1 3" 'r=; for i in 1 2 3 4; do [ $i = 2 ] && continue; [ $i = 4 ] && break; r="$r $i"; done; echo ${r# }'
|
||||
t "return-funcion" "7" 'f() { return 7; }; f; echo $?'
|
||||
t "here-string-noposix" "SKIP" 'echo SKIP'
|
||||
t "wait-bg" "si" '(sleep 0.05) & wait; echo si'
|
||||
t "unset-func" "si" 'f() { echo no; }; unset -f f; command -v f >/dev/null || echo si'
|
||||
t "export-hijo" "val" 'export V=val; "'"$SH"'" -c "echo \$V"'
|
||||
t "shift-sin-args" "1" 'set --; shift 2>/dev/null; echo $?'
|
||||
|
||||
echo "$SH: OK=$ok FALLO=$ko"
|
||||
[ -n "$KOLIST" ] && echo "$KOLIST"
|
||||
exit $ko
|
||||
Reference in New Issue
Block a user