Repository navigation
v0.8.0
Strings that a command runs as a script and bats test files are explained, conditions that the script's own values decide say which code never runs, and worked examples read and print as the tools do.
New
- A string that a command runs as a script is explained as one:
bash -c '…',sh -c,eval, a trap's action,su -c,ssh host '…'andwatch '…', also undersudo,xargs,find -exec,docker execandkubectl exec. Its commands and expansions appear at their own characters, its notes are those of the shell that runs it (ansh -cstring gets the POSIX checks), and the Problems list shows its dialect issues. What a staticevalstring or a trap's action sets counts after it. A string with an expansion (eval "$cmd") says its text is known only when it runs. Before, such a string was one opaque argument. - New notes on conditions that the values the script set decide: "
$modeisfasthere, so this condition is always false: the commands after itsthennever run", or "so onlyfast)of thiscaseruns". Expansions such as${x:-default},${x#prefix}and$(( ))are read as bash expands them. Conditions of literals only (while true,if false) say nothing more. - bats test files (
.bats, or abatsshebang) are explained:@test "…" {,run,load,skipand the other bats commands, with pitfalls for a! cmdthat cannot fail a test and forrunin a pipeline. VS Code, the Language Server, the command-line tool (--kind bats) and the web page (with a bats sample) recognise them. Before,@testwas a syntax error andrunan unknown command. env -Sis read as GNU env reads it. A shebang#!/usr/bin/env -S bash -erunsbash -ewith the script.#!/usr/bin/env bash -ewarns that Linux passesbash -eto env as one word, which fails. Anenv -Sstring is split as GNU env splits it (quotes,\_,#comments,${NAME}), and a string env refuses gets a warning that it exits with status 125.ksh93,mksh,lksh,pdksh,oksh,posh,yashandash(also by path) take the options ofsh:mksh -c '…'runs the string as a script. Before, such a name was an unknown command.- A note says when a command does not read the standard input the line gives it, because it reads the files it names:
jq . a.json < b.jsonreads onlya.json. - The command-line tool takes
--sarif <file>with--check: it also writes the problems to that file as SARIF. The GitHub Action uses it, so it checks the scripts once for both the annotations and the SARIF file.
Changed
- VS Code settings: a setting set below its minimum (
bashExplainer.inline.maxLengthunder 20,bashExplainer.hover.maxDepthorhover.followDelayunder 0) now uses its default, as the command-line tool already did. Before, VS Code used the number as given. - A branch that cannot run, because the script's own values decide its condition, no longer changes how later lines are explained: after
x=1; if [ "$x" = 2 ]; then set -f; fi, a glob is a glob again. Code afterbreak,continue,returnorexitno longer counts either. Before, both sides of every branch counted, so such lines said "may" or "on some paths". In a script that runs code that cannot be read (source,eval "$s",$cmd,${!v}), both sides still count. - The worked examples' sections say what the command reads and where from: "Input:
a.json(sample contents)", "Input: stdin (sample output of the previous command)", "Input: files matching*.json". A section that is not read is no longer called input:date +%Fsays "When it runs (made up, UTC)". Before, every such section was called "Input" (or "Date and time (UTC)"). - The colours of the parts of a line (command, option, variable…) are readable as text: light themes use darker colours of the same hues (at least 4.5:1), high contrast themes their own (7:1), and errors a red that passes too. The web page has the same colours. Before, they were VS Code's chart colours, as low as 2.9:1 on white.
- On the web page, changing the file kind keeps the text, also an untouched sample. "Load the sample" loads the current kind's sample, with an "Undo" button when it replaces your own text. Before, choosing a kind replaced an untouched sample, and "Load the sample" could be undone only with Ctrl+Z.
Fixed
- Worked examples print the output as the tools print it. jq uses two spaces a level (one line with
-c;--tab,--indent nand-Sare followed), and reads-R,inputandinputs. mikefarah/yq prints JSON as yq does, reads a.jsonfile as JSON, prints strings bare with-r, and shows with-iwhat the first file becomes. awk givesFILENAMEthe name of each file.sort -oanduniq in outshow what is written to the file. Before, jq and yq printed every result on one line, andFILENAMEwas empty. - Worked examples read what the command really reads. A here-string, a here-document or a pipe of known text is the input itself (
echo '{"a":1}' | jq .a).jq -nand an awk program ofBEGINrules only show no input. Several files (sort a b,head -n 1 a b) show a section each.catandnawkget real examples. Before, the example made up one input for all. - Worked examples follow the command's options.
grep -e -v foosearches for-v,sed -ie 's/a/b/' -e 's/c/d/' fruns onlys/c/d/, andfind … -exec cmd {} '+'runs the command once. A line the tool refuses (grep -E -F,tar -c -x,seq 1 -w 3) says so and gets no example. A function or alias of the script named like a program (sort() { tac; }) no longer shows that program's output. Before, these examples showed what the tool does not do. - Options are read as each tool reads them. GNU tools (
grep,sed, coreutils,diff,gzip,tar) accept a long option's unique prefix (ls --almostis--almost-all) and know every long option of their--help.testreads no options,xargs --max-lines echorunsecho,perl -0neis-0 -n -e, andbash -O extglob -c '…'andbash --posixare known. Global options are told apart from a subcommand's (docker -H h run,kubectl -o yaml get pods). Before, many of these were "not in the dictionary" or taken for values. - Explanations match bash in more places:
x=1 ''does not setx,exec {fd}>file,wait -p varandcoproc nameset their variables, and a division by 0 in$(( ))discards the rest of the line.unset fremoves the function when there is no variablef.exec cmdin( )ends only the subshell.cmd &> >(tee log)sends both outputs to the process substitution; before, it was read as an argument, or as a syntax error after a block or a loop. In the body offor (( i = 0; … )),$_is no longer said to be empty. [,unsetand declarations are explained as what bash runs, however they are written:\[ -f x ],command [ -f x ],x=1 unset x,\declare a=$v.[ -f xwith no], and wordstestcannot read ([ a b c d ]), say that the command fails with status 2.unset 'a[1]'removes an element, and an unquoteda[1]is a glob first. Where aliases are expanded, an alias named like a builtin runs the alias. Before, these forms were "not in the dictionary" or explained as working, andunsetof a read-only variable said it removed it.- Redirections are read wherever they are written: before the command's name, among its words, and between assignments (
a=1 >f b=2). A command of only redirections and assignments has the status of the last$( )in its redirections, sox=1 >f$(false)fails. A pipeline stage that sends its output to a file (>out grep x in | sort) gets no example of the whole pipeline. Before, a line such asa=1 >f b=2left the whole script unexplained, and such commands had the status 0. - The ⚠ notes follow what bash does here. After
IFS=; set -f,cp $a bno longer says$ais split. A variable set byprintf -vormapfileat the end of a pipeline is reported as lost. The pitfall for a variable another shell does not have sees\$ninbash -c "echo \$n", and knows thatenv -u xremovesx.egrep -qcounts asgrep -q, and an alias ofrmasrm. - The "Idiom" notes are read from the commands, not from the text, so other spacing, order and quoting are found (
while true ; do,done <f,trap cleanup 0). They are no longer given where bash does something else:trap - EXITsets no clean-up,while [ $# > 0 ]writes to a file named0, and text in quotes is not run. - The shell a script runs in is read in more places. In a GitHub Actions workflow,
shell:counts aboverun:, anddefaults:orruns-on:count after the steps. A Dockerfile or MakefileSHELLand a Pythonexecutable=are read as the same shell everywhere:SHELL ["/bin/dash","-c"]is dash. Busybox shells (RUN ["busybox", "sh", "-c", …],# shellcheck shell=busybox) are ash. Before, these were ignored or misread, and the script was explained as bash or sh. - A script is in POSIX mode only when its shebang really starts bash in it (
#!/bin/bash --posix, orenv -Swithbash --posix). Before,--posixanywhere on the shebang line counted, also in#!/usr/bin/env bash --posix, where bash does not start. - With
notes.pitfallsoff, more ⚠ notes are hidden: those for distribution aliases,\bin[[ =~ ]], a DockerfileRUNwithoutpipefail, Pythonsubprocess, GitHub Actions${{ … }}injection and bats files. Before, these ignored the setting. - Quick fixes are offered in every language of the
bashExplainer.languagessetting, as the hover is. Before, they were offered inshellscriptdocuments only. - On the web page's compare view, the Problems list shows the script's problems. Before, it stayed empty and the page raised an error.
- Scripts that assign an element before a function (
n[0]=5 f) are explained; before, explaining them failed. A long chain of functions that call each other in$( )is explained at once; before, 40 of them froze the explanation for about a minute.