[Toybox] [PATCH] toysh: fix -Wuse-after-free

Rob Landley rob at landley.net
Wed Mar 13 08:08:33 PDT 2024


On 3/8/24 21:22, Oliver Webb via Toybox wrote:
> TL;DR: Rant about sh.c's variable names I forgot to include in main email, I
> have a patch to start fixing it but it conflicts with other stuff and I have
> to re-do it
> 
> Reading through sh.c, most of the variable names are 2 letters long (repeating the same letter),

I switched from single character local variables to double character ones
because they're easier to search for without an elaborate IDE trying to parse
the code.

> for short functions (<20 lines) this isn't a problem. For example, I don't need a more
> descriptive name for the variable "c" in getvar() to understand it because I can look at the uses
> of it and infer from context it's about variable types. Longer functions where there are a lot of variables
> interacting with each other are a lot harder to keep track of.
> 
> For example, what does "cc" do in sh_main?

Hold the -c argument. The first assignment to it is:

    cc = TT.sh.c;

> From the looks of it. It's the name
> of command/file being processed, but to _know_ that's the case I have to audit
> 50 lines of code, and check over seeing if that's what it really does.

Variables sometimes get reused to mean slightly different things, and their use
can change over time when a lot of editing is done to a function. Especially for
code that isn't finished yet, there can be unpolished bits.

Although in this case, "-c is held in the variable c but I generally double such
variable names to give vi's text search at least SOME chance of finding it"
seems reasonable to me?

If you want to complain, I do tend to have "s", "ss", and "sss" as an
alternative to "s1", "s2", and "s3" when the long descriptive name for the
variable would otherwise be just "string". That gets a bit awkward at times and
I've looked at renaming some but not come up with better names.

> When a name
> like "in_source", "inputsrc", or just "inname" would tell you that without having to look
> over the code and make sure that's the case.

No, you still have to look at the code. Especially when the code is not
finished, as evidenced by it still being in pending/ and not passing half its
test suite. (Let alone my giant pile of "turn these into proper shell tests",
attached...)

And "c" holding the argument of "-c" is still probably a bad example to base
your rant on. And a command that's _flagrantly_ not finished yet may also be a
bad example...

> The problem isn't the length, the problem is that there isn't any convention to it and a lot
> of names convey little to no information ("ss" is usually a string, but "what does the string do?"
> is a question you need to commonly ask to find out what it's doing). It'd be like if run_lines()
> was called "rr()" or "expand_arg" was called "ea()", we name functions just fine (which makes
> this code somewhat readable, at least it's general structure)

Would you like me to stay out of your way while you take over toysh and refactor
it to your heart's content? I note that I will never touch it again if I hand it
over.

> "ff" is a common name for file descriptors.

In this idiom it's "f, but more searchable".

You're basically complaining there was an "i" variable that wasn't a loop index.

> So working down from sh_main, One could assume that 
> "TT.ff" must have something to do with file management, right?... No, it's a doubly linked list
> of overhead for shell functions,

In this case it sounds like f stands for function, yes. Many words start with f.

> and "struct sh_fcall" is often called "ff" in functions that use
> it, we have 2 common names that do completely different things...

In multiple places, f stands for function, and there's more than one aspect to
functions.

> The problem isn't the length as I said, the problem is that there is no convention for the naming
> of these.

Maybe I should move the file to "pending"?

> No pattern that you can follow, you usually have to go to the start of the function
> (which can be hundreds of lines up, depending on what you are looking through) and see how it gets
> assigned (And interacts with other variables that have the exact same problem)
> 
> I'd try to replace a lot of these names in a patch (I have already done so for sh_main in a patch
> that I didn't send because getting the compiler to shut up was more important, will send the patch
> when I'm sure it won't conflict with anything)

And as I said: if I were to I apply an aesthetic patch which does nothing but
make the code smell like you, toysh would be all yours and I would never touch
it again.

> but to be accurate in renaming I have to understand
> what they are, (The problem I'm trying to fix) which requires either better names to begin with or
> extensive auditing and debug printf-ing of the code that takes up a lot of time.

Clearly I have been too slow to finish this, and it's time for you to take over.
Your aesthetic sense is empirically superior to mine, I stand chastized.

> - Oliver Webb aquahobbyist at proton.me

Rob
-------------- next part --------------
https://bash.cyberciti.biz/guide/Select_loop

/*
arrays can be sparse
cd ulimit kill exit set export unset exit exec function source
jobs wait
if while for case
alias bg command fc fg getopts hash read type umask unalias trap

  TOY_MAYFORK: echo test printf
    no memory allocations, filehandles open... if signaled, no cleanup.
    maybe: help ulimit

  .toysh_history file
  {abc,def}
  $LINENO $RANDOM
  "source" and "." check current dir after $PATH
  job control: CTRL-Z, CTRL-C, fg, bg, kill %jobspec, wait
  $(command)
  $((math))  # integer attribute for variables, assignment does that
    $((abc"def)) the " doesn't take effect so )) is recognized?
  if then elif else fi
  for while until do done
  case esac
  function () { commands ; }
    local variables in functions
  set -x (and -x command line option) to trace
  export -n (unexports)
  readonly
  read set unset umask
  time (whole pipeline)
  command trap type times getopts

  todo: echo | (cat) # as far as cat's concerned previous end was NULL

  What to do about builtin vs $PATH:
    check $PATH first _then_ builtin?
    config opt?

if files exist use 'em, otherwise:
  /dev/fd/fd stdin stdout stderr tcp/host/port udp/host/port
exec redirects in current shell

 n<word
 n>word
 n>>word (append)
 &>word  # stdout and stderr, same as >word 2>&1
 &>>word (append)
 >&word  # note: >&$BLAH when $BLAH is number or -
 n<&word # number = duplicate, - = close, number- = move 
 n>&word # special case: no n and word not number or -, redirect stderr & out.
 n<<word  # only quote removal, no expansion
         # unquoted: parameter ex, command subst, arith ex, \\ \$ \` \newline
 <<-word # strips leading tabs
 <<<word # all expansions _except_ pathname expansion and word splitting
 n<>word # open file word for reading & writing, create if necessary, n def 0

// &> or >& = >word 2>&1
// &>> append stdout & stderr == >> word 2>&1
// <&# copy input fd (error if # not open for read)
// >&# copy output fd (error if # not open for write)
// <&#- move fd# to n
// >&#- move fd# to n


What does this mean?
  env {abcdef}>&2 | grep abcdef

Instead of number, precede with {varname}?

brace  expansion,  tilde expansion,  parameter  and  variable  expansion,  command substitution,
       arithmetic expansion,  quote  removal,  pathname  expansion,  and  word
       splitting.
  << no path ex or word split

test should work as a nofork, handle [[
what does $(( ))
handle ( ( echo hello ) ) | cat and (echo)|cat

      if (then)
        else elif (then)
        fi
      for select while until (do)
      done
      case (esac)
      ( )
      { }
      [[ ]]
    }

// function
// HERE document
// ( ) - ) is always last argument of line
// | & && || |&

      // "if", "for", "while", "until", "select"
      // "do" : "then";
      // then else elif fi
      // continue break case esac return do done function
      // in time { } ]] ]]
      // ! coproc
      // function ()
      // . source [ test [[ ( { ((

      // [ pwd ulimit -- kill time
      // cd exit -- pushd popd logout umask getopts eval exec
      // fg bg jobs disown wait suspend
      // alias unalias set export unset let local readonly read shift trap



# Because of HERE documents we can't re-parse, have to retain state.
# -- either that or our caller has to know about here document servicing?
# -c input must be split into lines for HERE documents
< > >> <<< WORD
<< HERE [HERE] # eat lines until HERE as _only_ word on line
if [then]
for while until select [do]
case [esac]
( [)] # funky because ) not first word
{ [}]
[[ []]]
function NAME ( ) { # note expect { doesn't eat { be because need to expect }

/*
4 cases:
redirect now: from -> to, saving displaced to (+, +) = hfd to
explicitly close (now), saving displaced to (-1, +) = hfd to
close in child only: (+, -1)
redirect saving to, close saving from: (+, +) = hfd to then (-1, +) = hfd to
{var} leak - nothing to save? Except the from open is deferred (+, +) but to == hfd
*/


// TODO job control: & backgrounding
// TODO && ||
// TODO | |&
// TODO add redir segment, redir pipes[1] to pipes[0] with close, pop again later
// TODO pipe segments are subshells ala ( )
// TODO: don't () around single process pipeline, shell otherwise
//       this turns into exec for sh -c "echo"
//       single pipe segments that _aren't_ builtins run directly,
//       compound pipe segments and builtins run via subshell.
//       I.E. subshell implicit exec
// TODO we're not running a command, we're running a block stack?
//    echo one two three | while read i; do echo hello; done
// TODO free/cleanup partial pipeline on NULL return?
// TODO -o pipefail
// TODO pipes don't connect commands, they connect arbitrary function chunks.

Job control:
  ok I basically just need to get it working again
  jobs pid: cache of old pids, when to flush?
  jobs %job: when to flush? ; != newline, in blocks? cmdline vs script?


*/

# can't have space before first : but arguments can have lead/trail
$ BLAH=abcdefghi; echo ${BLAH: 1 : 3 }
# leading assignments don't affect current command line
$ VAR=12345 echo $VARa
$ bash -c 'ls $('
bash: -c: line 0: unexpected EOF while looking for matching `)'
bash: -c: line 1: syntax error: unexpected end of file
$ ;
bash: syntax error near unexpected token `;'
$ ABC= ; env | grep ABC= ; unset ABC ; env | grep ABC=
ABC=
$ cat blah<(echo hello)thing
cat: blah/dev/fd/63thing: No such file or directory
$ "AB"="CD" echo $AB
bash: AB=CD: command not found
landley at driftwood:~/toybox/toybox$ AB="CD" echo $AB
$ 'AB'="CD" echo $AB
bash: AB=CD: command not found
$ \AB="CD" echo $AB
bash: AB=CD: command not found
$ \AB="CD" echo $AB
$ if echo hello; then fi
bash: syntax error near unexpected token `fi'
$ echo )
bash: syntax error near unexpected token `)'
$ ( echo hello ) | cat
hello
$ if
> true
> then
> echo hello
> fi
hello
$ if true; then echo hello; fi
hello
$ if false; then echo hello; fi
$ false; X=47; echo $?
0
$ if;
bash: syntax error near unexpected token `;'
$ if
> true
> echo
> hello
> fi
bash: syntax error near unexpected token `fi'
$ echo )))
bash: syntax error near unexpected token `)'
$ echo &&&
bash: syntax error near unexpected token `&'
$ echo |||
bash: syntax error near unexpected token `|'
$ echo hello | if true; then read i; echo i=$i; fi
i=hello
$ if true && false; then echo hi; fi
while true ; do echo hello; done | tee blah
if { echo hello; }; then echo hi; fi
if if true; then true; fi; then echo hello; fi
boom(){ echo hello; }; boom
( ( echo; thingy ) ) | cat
$ echo hello | X=y env | grep -w X; echo $X
X=y
;|x
;x|
(echo)also
(echo)(echo)
$ if true esac; then echo hi; fi
hi
# note: exits top level shell anyway!
$ (exit walrus)
bash: exit: walrus: numeric argument required
$ echo $?
2
$ exit walrus 2>blah.txt
$ echo ${abc:?error m essage}
bash: abc: error m essage
$ if blah () { echo bang; true; }; blah; then echo hello; fi; blah
bang
hello
bang
$ meep() { echo hello; klarg () { echo helloier; }; klarg; }
$ meep
hello
helloier
  $ meep() { echo hello; klarg() { echo helloier; } ; }
  $ klarg
  bash: klarg: command not found
  $ meep
  hello
  $ klarg
  helloier
  $ 

landley at driftwood:~/toybox/toybox$ thingy ()
> ^C
landley at driftwood:~/toybox/toybox$ echo (
bash: syntax error near unexpected token `newline'
landley at driftwood:~/toybox/toybox$ function
bash: syntax error near unexpected token `newline'
landley at driftwood:~/toybox/toybox$ function name () {
> }
bash: syntax error near unexpected token `}'
landley at driftwood:~/toybox/toybox$ function name () { ;}
bash: syntax error near unexpected token `;'
landley at driftwood:~/toybox/toybox$ if ; then; echo hello; fi
bash: syntax error near unexpected token `;'
landley at driftwood:~/toybox/toybox$ func (); { echo hello; }
bash: syntax error near unexpected token `;'
$ if true |
> then
bash: syntax error
echo one && if true; then echo hello; fi
one
hello
$ echo one two \
> three
$ bash -c "$(echo -e 'cat << HERE\none two\nHERE')"
one two
$ cat << HERE; echo hello
> boing
> HERE
boing
hello
$ cat << one << two
> abc
$ ls > file 2>&1 #redirects both
$ ls 2>&1 >file # redirects only stdout, stderr goes to original stdout
$ echo hello < nowhere
bash: nowhere: No such file or directory
$ cat << E"O"F
> $PATH
> EOF
$PATH
$ cat << HERE filename
> HERE
cat: filename: No such file or directory
$ cat <<< here filename
cat: filename: No such file or directory


landley at driftwood:~/toybox/toybox$ bash -c "cat <<< HERE; echo hello; HERE"
HERE
hello
bash: HERE: command not found
landley at driftwood:~/toybox/toybox$ bash -c "cat << HERE; echo hello; HERE"
bash: warning: here-document at line 0 delimited by end-of-file (wanted `HERE')
hello
bash: HERE: command not found
landley at driftwood:~/toybox/toybox$ bash -c "$(echo 'cat << HERE\necho hello\nHERE')"
bash: warning: here-document at line 0 delimited by end-of-file (wanted `HEREnecho')
cat: hellonHERE: No such file or directory
landley at driftwood:~/toybox/toybox$ bash -c "$(echo -e 'cat << HERE\necho hello\nHERE')"
echo hello
landley at driftwood:~/toybox/toybox$ X=3; echo $((X))
3
landley at driftwood:~/toybox/toybox$ X=3; echo $((X))^C
landley at driftwood:~/toybox/toybox$ echo |
> 
> cat

landley at driftwood:~/toybox/toybox$ echo &&
> &&
bash: syntax error near unexpected token `&&'
landley at driftwood:~/toybox/toybox$ echo &&
> ;
bash: syntax error near unexpected token `;'
landley at driftwood:~/toybox/toybox$ if
> true
> then
> echo hello
> fi
hello
landley at driftwood:~/toybox/toybox$ << EOF
> echo hello
> EOF
$ echo &<2
[1] 17850

bash: 2: No such file or directory
landley at driftwood:~/toybox/toybox$ 
[1]+  Done                    echo
landley at driftwood:~/toybox/toybox$ { echo hello; }
hello
landley at driftwood:~/toybox/toybox$ { { echo hello; };}
hello
landley at driftwood:~/toybox/toybox$ {{ echo hello; };}
bash: syntax error near unexpected token `}'
landley at driftwood:~/toybox/toybox$ { {echo hello; };}
bash: syntax error near unexpected token `}'
landley at driftwood:~/toybox/toybox$ { { echo hello; };}
hello
landley at driftwood:~/toybox/toybox$ {
> {
> echo hello
> }
> }
hello
$ if cat << EOF ; then
> one two three
> EOF
> echo hello
> fi
one two three
hello
cat <(ls "$PWD"/a{b,c}*) &> /dev/tcp/127.0.0.1/80
$ while grep -q << EOF walrus; do
> walrus
> EOF
> echo hello; done | head -n 3
hello
hello
hello
$ if echo hello; then echo also; fi | tee and.txt # both get redirected
hello
also
$ cat - << EOF <(echo
> hello)
> boing
> EOF
bash: hello: command not found
boing

$ ( ( ( echo abc ) echo ) )
bash: syntax error near unexpected token `echo'
$ ( ( ( echo abc )
> echo also ) )
abc
also
$ ( ( echo one ) > file )
$ ( ( echo one ) > two ) > three
$ ( echo ) >> blah
$ if true; then echo; fi >> blah
$ cat - /proc/self/fd/3 << BOING 3<<MEEP
> one
> BOING
> aha
> MEEP
one
aha
$ cat - << BOING $(    
echo hello)
abc
BOING
abc
blah
$ ( cat << EOF
> hello
> EOF
> )
hello
$ if cat << EOF
> hello
> EOF
> then
> echo fi
> fi
hello
fi
$ echo 2>&1
$ echo 2 >&1
$ echo 2 >& 1
$ if while true; do echo hello; done; then echo hi; fi
$ (echo &&)
$ (echo ;)
$ if true; then echo $X; fi {X}</dev/null
10
$ X=2; {X}<&-; boing # does not close stderr?
bash: boing: command not found
$ ls /proc/$$/fd
$ exec 10<& -
# >&; ;&>
# prompts before echoing
$ echo hello; if
> echo two; then echo three
> fi
hello
two
three
$ echo )hello
bash: syntax error near unexpected token `)'
$ echo {abc
{abc
$ echo {abc</dev/null
{abc
# innermost redirect wins
$ if cat <<< moo ; then cat <<< also; fi <<< potato
moo
also
$ (echo &&)
bash: syntax error near unexpected token `)'
$ funkiness() { env ; }
$ POTATO=blah funkiness | grep POTATO
POTATO=blah
$ echo $POTATO
$ chicken() { echo hello; chicken() { echo also ;}; chicken;}
$ chicken
hello
also
$ chicken
also
$ thingy() { echo hello; } | cat
$ thingy
bash: thingy: command not found
$ echo hello > >(sed 's/.*/abc&def/') > >(sed 's/.*/ghi&jkl/')
abcghihellojkldef
$ echo hello > one > two
$ yes | head -n 3 > >(wc -l >two) > >(wc -l >one)
landley at driftwood:~$ cat one
3
landley at driftwood:~$ cat two
0
$ </dev/null echo hello
hello
$ echo {+}</dev/null
{+}
$ echo {}</dev/null
{}
$ echo <
bash: syntax error near unexpected token `newline'
$ murgle() { echo > "$@" ;}
$ murgle one two three
bash: "$@": ambiguous redirect
$ murgle one
$ cat << "   "
> abc
>    
abc
$ cat << \"
> boing
> "
boing
$ echo hello &>> blah.txt
$ potato() { echo "$@"; }; IFS=1234 potato one two three
one two three
$ cat <<< {one,two,three}
{one,two,three}
$ cat <<< one two three
$ echo hello >&2-x && ls 2-x
2-x
$ while true; do sleep 1; read a; echo $a; [ -z "$a" ] && break; done << EOF &
> one
> two
> three
> EOF
[1] 30590
$ (set -o noclobber; echo > /dev/null)
$ (ln -s /dev/null blah; set -o noclobber; echo > blah)
$ (touch walrus; ln -s walrus penguin; set -o noclobber; echo > penguin)
$ funky() { A="$@" ;}; funky one two three; echo $A
one two three
$ export "()=42"
bash: export: `()=42': not a valid identifier
$ set hello="$@" > walrus
$ X=$(false) || echo true
true
$ for((i=1;i<5;i++));do echo $i;done
1
2
3
4
# when is (( a token vs ( (?
$ ((echo a) | sed s/a/b/)
b
$ ((1
> +2<1)); echo $?
1
$ X=ii;for $X in a b c; do echo $ii; done
bash: `$X': not a valid identifier
$ for i in 1 2 3 & do echo hello; done
bash: syntax error near unexpected token `&'
$ if true; then false; else true; elif true; then false; fi
bash: syntax error near unexpected token `elif'
$ while false; true; do echo hello; done | head -n 3
hello
hello
hello
$ せ=42
bash: せ=42: command not found
$ function abc=def () {echo hello;} ; "abc=def"
hello
$ ((echo hello) )
hello
$ abc() { for name; do echo $name; done;}; abc X Y Z
X
Y
Z
# In a for loop ;; breaks down to ; ;
$ for((i=0;;i++)); do echo $i; done | head -n 30
1
2
$ (()); echo $?
1
$ ((x=12)); echo $x
12
$ (echo 123)  # redir prefix not used for non-redir end tokens
123
$ x=42; ! ((x<3)) && ((x<43)) && echo yes
yes
# redirects aren't in math
$ echo $((2<3))
1
$ echo $((2&3))
2
$ ((3<2)); echo $?
1
$ X=X; echo $((X+2))
bash: X: expression recursion level exceeded (error token is "X")
$ X=3 ((3<2)) || echo hello
bash: syntax error near unexpected token `('
$ echo $((`echo 1`))
1
$ X=3; (($X<3)) || echo no; (($X<4)) && echo yes
no
yes
$ cat <(echo "$(((1<2)) && echo "hello")")
hello
$ THINGY=whoami; echo "$("${THINGY}")"
landley
$ burble() #comment
> { echo hello;};burble
hello
$ for i=3
> do echo hello; done
bash: `i=3': not a valid identifier
$ ((1<2)) > blat
$ boing () { for name do echo $name; done }; boing one two three
one
two
three
$ cat <( "(" )
bash: (: command not found
$ break > potato 2>/dev/null || ls potato
potato

# too many error messages
$ break 1 37
bash: break: only meaningful in a `for', `while', or `until' loop
$ for i in 1; do break walrus; done
bash: break: walrus: numeric argument required
$ for i in 1; do break 37 walrus; done
bash: break: too many arguments
$ for i in 1; do break 0; done
bash: break: 0: loop count out of range
$ if true; then if false; then echo one; elif ! echo two; then echo three; else echo four; fi; fi
two
four
$ cat boing |& sed s/boing/walrus/
cat: walrus: No such file or directory
# runs printing "hi" until segfault
bash -c "abc() { echo hi; def() { abc;echo lo;};def;}; abc"
# word splitting test
$ X="one two"; printf '%s\n' $X 2 3 | while read a; do echo $a; done
one
two
2
3
$ fff() { echo abc$*def;}; fff one two three | xargs -n 1 echo
abcone
two
threedef
$ ABC=abcdefg
$ echo ${ABC:+"}"}
}
$ for i in a b c d e; do break & done
[1] 25533
[2] 25534
[3] 25535
[4] 25536
[5] 25537
$ walrus=1; echo ${walrus+blah}
blah
$ unset walrus; echo ${walrus+blah}
$
$ echo echo hello | sh
hello
$ { { { echo hello;} ;} >blat ;} ; echo one; cat blat
one
hello

$ ABC=1
$ echo ${"AB"C}
bash: ${"AB"C}: bad substitution
$ echo ${"ABC"}
bash: ${"ABC"}: bad substitution
$ X='*'; echo $X

$ <&2-; ls boing  # redirect undone at end of command
ls: cannot access 'boing': No such file or directory
$ ls boing <&2-   # redirect applies to command
$ X=42 </dev/null ; echo $X # local assignment persists even with redirect
42
$ bash -c 'read i >/dev/null <<< boing; echo $i'
boing
# The expansions unquoted << _doesn't_ do.
$ cat << TEST
> ~landley
> *
> potato/{one,two,three}
> "hello"
> TEST
~landley
*
potato/{one,two,three}
"hello"
$ X="|"; echo hello $X and
hello | and
$ for i in a b c; do for j in d e f; do for k in g h i; do echo $i $j $k; continue 3; done done done
a d g
b d g
c d g
$ sh -c '<&3-'
sh: 1: Syntax error: Bad fd number
$ {abc}<&2- # leaves $abc open even when 2- unwound
$ {def}<&2  # doesn't close 2
$ {bcd}<walrus
$ {cde}<<EOF
hello
EOF
$ echo {fgh}<<<potato another; echo hi; cat <&$fgh
another
hi
potato
$ <&2- <&37 ; echo $?
1
$ echo hello {var}<nonexistent # doesn't set var on error
$ var=wurble; echo hello {var}<nope # doesn't blank either
$ X="echo hello"; $X
hello
$ > /does/not/exist && echo $?
1
$ { echo -e "one\ntwo" ;} | { if true; then read i; echo a=$i;fi;if true; then read i; echo b=$i;fi;}
a=one
b=two
$ if false; then if false; then echo one; else echo two; else echo three; fi
# disabled on block entry disables entire block: else doesn't revive
$ if false; then if false; then echo one; else echo two; fi; else echo three; fi
three
$ { { echo hello ;} | }
bash: syntax error near unexpected token `}'
$ {|{ echo hello;};}
$ echo hello | if false; then cat; fi | cat
$ blah() { echo hello; } | echo ha; blah
ha
bash: blah: command not found
$ X=42 | true; echo $X

$ if echo hello; then true; fi | tr l x
hexxo
# assignment persists, redirect does not
$ X=1 >woot; echo did not persist; echo $X
did not persist
1
$ echo <(if)
bash: command substitution: line 22: syntax error near unexpected token `)'
bash: command substitution: line 22: `if)'
# this shows error immediately but doesn't exit for 10 seconds...
$ sleep 10 | cat < notit
bash: notit: No such file or directory
$ echo {"on,t"w}e
{on,tw}e
$ func() { echo $0,$1; shift; echo $0,$1;}; func one two three
bash,one
bash,two
$ X=123 cat <(echo $X)
$ env -i bash --norc --noprofile -c env | sort
PWD=$(pwd -P)
SHLVL=1
_=/usr/bin/env
$ echo {~,~root}/pwd
/home/landley/pwd /root/pwd
$ echo \{~,~root}/pwd
{~,~root}/pwd
$ echo ""{~,~root}/pwd
~/pwd ~root/pwd
$ A= ABC=123; echo $A{BC,""}
123
$ A=; echo $A''C
C
$ echo $((x=3)); echo $x
3
3
$ y=-4; echo $((x=y)); echo $y
-4
-4
$ x=x; echo $((x+1))
bash: x: expression recursion level exceeded (error token is "x")
$ expr 1 + 2x
expr: non-integer argument
$ echo $((1+_)) # because $_
bash: 2x: value too great for base (error token is "2x")
$ echo hello | cat <(read i; echo $i)
hello
# errors propagate up but return code doesn't
$ echo -n thingy $(if true)
bash: command substitution: line 2: syntax error near unexpected token `)'
bash: command substitution: line 2: `if true)'
$ echo $?
1
$ echo -n $(false)
$ echo $?
0
$ echo -n $(true < walroid)
bash: walroid: No such file or directory
landley at driftwood:~/toybox/toybox$ echo $?
0
$ walrus=42; readonly walrus; walrus=7; echo $?
bash: walrus: readonly variable
1
$ walrus=42; readonly walrus; walrus=7 echo hello; echo $?
bash: walrus: readonly variable
hello
0
$ ./bash -c 'echo $_'
./bash
$ PATH=$PWD:$PATH bash -c 'echo $_'
$PWD/bash
$ PATH=$PWD:$PATH PWD=/bin bash -c 'echo $_'
$OLDPWD/bash
$./sh -c 'cat <(echo hello 2>&1)'
hello
$
$ chicken() { for i in a"$@"b;do echo =$i=;done;}; chicken 123 456 789
=a123=
=456=
=789b=
# I hate IFS
$ THINGY=$'potatoxsalad\tand this\n\n\n' IFS='x '; for i in $THINGY; do echo =$i=; done
=potato=
=salad	and=
=this


=
$ echo $LINENO
1
$ banana() { echo $LINENO; }
$ banana
0
$ trap "echo hello" INT
$ kill -SIGINT $$
hello

$ a=b exec nothing >/dev/null; ls -l
bash: exec: nothing: not found
$ if :; then true; fi {abc[42]}</dev/null; echo ${abc[42]}; declare -p abc
10
declare -a abc=([42]="10")
# export -n assigns new local variables
$ export -n walrus=123; echo $walrus; declare -p walrus
123
declare -- walrus="123"
$ ABC=DEF bash -c 'export -n ABC; declare -p ABC'
declare -- ABC="DEF"
$ ./sh -c 'echo hello < /does/not/exist || echo bang'
sh: /does/not/exist: No such file or directory
bang
$ ./sh -c 'readonly abc=123; abc=def echo hello && echo two'
sh: readonly: No such file or directory
hello
two
$ abc=def echo hello && echo also
bash: abc: readonly variable
hello
also
$ abc=def > /does/not/exist
bash: /does/not/exist: No such file or directory
$ echo $abc
def
$ rm -f walrosity; abc=def > walrosity
$ ls walrosity
walrosity
$ abc=BLAT > /does/not/exist; echo $abc
bash: /does/not/exist: No such file or directory
BLAT
$ { abc=murgle > /does/not/exist; } 2>/dev/null; echo $abc
murgle
$ abc=${d?ef} echo hello
bash: d: ef
$ readonly walrus=123; walrus=456 echo hello
bash: walrus: readonly variable
hello
$ bash -c 'for i in one two three; do for j in A B C; do echo $j$i; done; done'
Aone
Bone
Cone
Atwo
Btwo
Ctwo
Athree
Bthree
Cthree
$ for i in {*..,}""; do echo =$i=; done
=*..=
==
$ echo $(echo one
echo two)
one two
$ source <(echo 'echo hello\')
hello
$ source <(echo -n 'echo hello\')
hello\
$ ls nothing 2>(wc)
$ export -n abc+=def; echo $abc
def
$ echo $((2,3)) # comma in math, x=1,y=2
3
$ echo $(( 1 + 3)) # spaces
4
$ let i=37; declare -p i  # not VAR_INT
declare -- i="37"
$ let x=43+@
bash: let: x=43+@: syntax error: operand expected (error token is "@")
$ echo ${PATH:1+@}
bash: PATH: 1+@: syntax error: operand expected (error token is "@")
$ SECONDS=0; x() { local SECONDS; echo $SECONDS; }; x; echo $SECONDS
$ declare -un x=florp; x+=boing; echo $FLORP
boing
$ declare -ui x; A=42; x=1+A; echo $x
43
$ declare -u x; declare -n y=x; y=abc; echo $x
ABC
$ for i in one ${j?x} two; do echo $i; done; echo next
bash: j: x
$ bash -c 'IFS=@; echo one at two'
one at two
$ bash -c 'IFS=@; X='one at two'; echo $X'
one two
$ bash -c 'abc=$(echo one >&2) 2>/dev/null; echo $abc'
one
# syntax err abort granularity
>       $ cat one
>       #!/bin/bash
>       source two
>       echo hello
>       $ cat two
>       syntax error)
>       $ bash one
>       two: line 1: syntax error near unexpected token `)'
>       two: line 1: `syntax error)'
>       hello
$ eval $'echo \'abc\\\ndef\''
abc\
def
$ echo ${PATH//":"/xxx}
/home/landley/binxxx/usr/local/binxxx/usr/binxxx/binxxx/usr/local/gamesxxx/usr/games
$ echo "${PATH//':'/xxx}"
/home/landley/binxxx/usr/local/binxxx/usr/binxxx/binxxx/usr/local/gamesxxx/usr/games
$ echo "${PATH//"/"/xxx}"
xxxhomexxxlandleyxxxbin:xxxusrxxxlocalxxxbin:xxxusrxxxbin:xxxbin:xxxusrxxxlocalxxxgames:xxxusrxxxgames
$ for i in one two three; do echo a=$i; continue& b=$i; done
a=one
[1] 30698
a=two
[2] 30699
a=three
[3] 30700
[1]   Done                    continue
[2]-  Done                    continue
$ for i in one two three; do echo a=$i; continue | echo b=$i; done
a=one
b=one
a=two
b=two
a=three
b=three
$ for i in one two three; do echo a=$i; continue | echo b=$i; echo c=$i; done
a=one
b=one
c=one
a=two
b=two
c=two
a=three
b=three
c=three
$ cat<<EOF
> \a \b \c \$ \\ \d \
> EOF
> EOF
\a \b \c $ \ \d EOF
$ cat<<EOF
> <(echo hello)
> EOF
<(echo hello)
$ cat<<EOF
> <(echo $(echo potato))
> EOF
<(echo potato)
$ echo $((1?2:(1/0)))
2
$ echo $((1&(1/0)))
bash: 1&(1/0): division by 0 (error token is "0)")
$ echo $((1|(1/0)))
bash: 1|(1/0): division by 0 (error token is "0)")
$ echo $((1&&(1/0)))
bash: 1&&(1/0): division by 0 (error token is "0)")
$ echo $((1||(1/0)))
1
$ cat<<EOF
> \"
> EOF
\"
$ cat<<EOF
> echo "
> EOF
echo "
$ echo "abc\""
abc"
$ echo ${one:+}

landley at driftwood:~$ echo${one:+:}

landley at driftwood:~$ echo ${one:]} two
two
$ echo ${one:0/0}

$ echo ${PATH:1+2}
me/landley/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
$ echo ${PATH::1+2}
/ho
$ echo ${PATH::0/0}
bash: PATH: 0/0: division by 0 (error token is "0")
$ cat <<EOF
> abc\
> def
> EOF
abcdef
$ cat<<EOF
> abc\
> def
> E\
> OF
abcdef
$ bash -c $'echo \'abc\\\ndef\''
abc\
def
$ bash -c '<<0;echo hello'
bash: warning: here-document at line 0 delimited by end-of-file (wanted `0')
hello
$ echo 'echo $LINENO' > weeb; bash -c '. weeb'
1
$ bash -c '. weeb;. weeb;echo $LINENO'
1
1
0
$ bash -c $'. weeb;. weeb;echo $(eval $\'echo $LINENO\\necho $LINENO\');echo $LINENO'
1
1
1 2
0
$ bash -c $'. weeb\n. weeb\necho $(eval $\'echo $LINENO\\necho $LINENO\');echo $LINENO'
1
1
3 4
2
$ bash -c $'. weeb\n. weeb\n(eval $\'echo $LINENO\\necho $LINENO\');echo $LINENO'
1
1
2
3
2
$ bash -c $'. weeb\n. weeb\neval $\'echo $LINENO\\necho $LINENO\';echo $LINENO'
1
1
2
3
2
$ bash -c 'echo $LINENO'
0
$ bash -c $'\n\n\necho $LINENO'
3
$ cat<<\
> hello
> and
> hello
and
$ cat<<EOF
> ${PATH
> EOF
bash: ${PATH
: bad substitution
$ cat<<EOF
> "
> EOF
"
$ cat<<EOF
> '$PATH'
> EOF
'/home/landley/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games'
$ x() { cat<<EOF
> ${PATH
> EOF
> }
landley at driftwood:~/toybox/toybox$ x
bash: ${PATH
: bad substitution
$ bash -c 'echo ${PATH'
bash: -c: line 0: unexpected EOF while looking for matching `}'
bash: -c: line 1: syntax error: unexpected end of file
$ if cat << EOF; then
> blah
> EOF
> echo hello; fi
blah
hello
$ if [ $(cat) == blah ]; then echo hello
> fi << EOF
> blah
> EOF
hello
$ cat << EOF1 << EOF2
> one
> EOF2
> two
> EOF1
> three
> EOF2
three
$ echo \x "\x" "\\" "\$"
x \x \ $
$ for i in a b c; do break -1; done
bash: break: -1: loop count out of range
$ break
bash: break: only meaningful in a `for', `while', or `until' loop
$ x() { echo one; eval "return 37"; echo two; }; x; echo $?
one
37
$ echo hello; return; echo $?
hello
bash: return: can only `return' from a function or sourced script
1
$ for i in a b c; do eval 'if [ $i == b ]; then break; fi'; done; echo $i
b
$ eval 'eval "echo hello"'
hello
$ [ -e <(sleep 999) ] && echo true
true
$ cat<<EOF''
> $PATH
> EOF
$PATH
$ cat<<EOF
> $PATH
> EOF
/home/landley/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
$ cat<<\EOF
> $PATH
> EOF
$PATH
$ echo \ |wc -c
2
$ if cat << EOF
> hello
> EOF
> then echo true; fi
hello
true
$ if true; then cat << EOF
> echo hello
> EOF
> echo next
> fi
echo hello
next
$ x() { (echo hello; return; echo next); echo two; return;}; x; echo three
hello
two
three
$ cat << EOF
> abc\
> EOF
> EOF
abcEOF
$ cat << EOF
> ${ABC
> EOF
bash: ${ABC
: bad substitution
$ cat << EOF
> 123
> xyz ${ABC
> EOF
bash: 123
xyz ${ABC
: bad substitution
$ bash -c 'cat<<EOF\nabc\\def\nEOF'
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOFnabc\defnEOF')
$ bash -c $'cat<<EOF\nabc\\def\nEOF'
abc\def
$ bash -c $'cat<<EOF\nabc\\\ndef\nEOF'
abcdef
$ bash -c $'cat<<EOF\nabc\\\\\ndef\nEOF'
abc\
def
$ bash -c $'cat<<"EOF"\nabc\\\\\ndef\nEOF'
abc\\
def
$ bash -c $'cat<<"EOF"\nabc\\\ndef\nEOF'
abc\
def
$ cat << ONE << TWO
> one
> TWO
> two
> ONE
> three
> ONE
> four
> TWO
three
ONE
four
$ echo $((1.1+2.3))
bash: 1.1+2.3: syntax error: invalid arithmetic operator (error token is ".1+2.3")
$ bash -c 'set -e;x() { echo one;false;echo two; }; x || echo three; x'
one
two
one
$ jobs
$ wait 7534
$ wait
$ wait 7534
bash: wait: pid 7534 is not a child of this shell
$ bash -c ". <(echo 'local abc=123; echo \$abc'); echo \$abc"
/dev/fd/63: line 1: local: can only be used in a function
$ return 37
bash: return: can only `return' from a function or sourced script
$ x() { source <(echo "echo blah;return 3"); echo $? echo hi;}; x; echo $?
blah
3 echo hi
0
$ x() { false; return;};x;echo $?
1
$ x() { false; return 3;};x;echo $?
3

$ bash -c 'cat <<< <(echo hello)'
/dev/fd/63
$ toybox sh -c 'cat <<< <(echo hello)'
<(echo hello)
$ toybox sh -c 'echo <(echo hello)'
/proc/self/fd/3


More information about the Toybox mailing list