What’s new in ShellCheck v0.7.0?

ShellCheck v0.7.0 has just been released. In addition to the usual “bug fixes and improvements”, there is a set of new features:

Autofixes

A few select warnings now come with auto-fixes. In the most straight-forward case, ShellCheck shows you what it thinks the line ought to be:

In foo line 2:
echo "File size: $(stat -c %s $1)"
                              ^-- SC2086: Double quote to prevent globbing and word splitting.

Did you mean:
echo "File size: $(stat -c %s "$1")"

To actually apply the fixes, you can use ShellCheck’s new diff output format, which outputs standard Unified Diffs that can be piped into tools like git apply and patch:

$ shellcheck -f diff foo
--- a/foo
+++ b/foo
@@ -1,2 +1,2 @@
 #!/bin/sh
-echo "File size: $(stat -c %s $1)"
+echo "File size: $(stat -c %s "$1")"

For example, to apply only SC2086 fixes to all .sh file in a project:

$ shellcheck --include=SC2086 -f diff **/*.sh | git apply

Optional Checks

ShellCheck now includes a small handful of checks that are off by default. These are intended for subjective issues that a project may choose to enforce:

$ cat foo
#!/bin/sh
# shellcheck enable=require-variable-braces
name=World
echo "Hello $name"

$ shellcheck foo
In foo line 4:
echo "Hello $name"
            ^---^ SC2250: Prefer putting braces around variable references even when not strictly required.

Did you mean:
echo "Hello ${name}"

For a list of such checks, run shellcheck --list-optional

source paths

ShellCheck now allows you to specify a list of search locations for sourced scripts using a # shellcheck source-path=/my/dir directive or --source-path flag.

This is useful in several cases:

  • If all the projects’ sourced files are relative to the same directory, you can now specify this directory once instead of having to add source directives everywhere.
  • The special name SCRIPTDIR can be specified in a path to refer to the location of the script being checked, allowing ShellCheck to more conveniently discover included files from the same directory. This also works for any path relative to the script’s directory, such as SCRIPTDIR/../include/
  • Absolute paths are also grounded in the source path, so by specifying source-path=/mnt/chroot, shellcheck will look for . /bin/funcs.sh in /mnt/chroot/bin/funcs.sh. This is useful when targeting a specific system, such as an embedded one.

RC files

Rather than adding directives in each file, you can now set most of the options above in a .shellcheckrc file in the project’s root directory (or your home directory). This allows you to easily apply the same options to all scripts on a per-project/directory basis.

Bats and shflags support

ShellCheck no longer needs any preprocessing to check Bats scripts:

$ cat test.bats
#!/usr/bin/env bats

@test "addition using bc" {
  result="$(echo 2+2 | bc)"
  [ "$result" -eq 4 ]
}

$ shellcheck test.bats && echo "Success"
Success

A bats shebang will be interpreted as “bash”, and @test statements will be correctly parsed.

ShellCheck now also recognizes DEFINE_* statements from the shflags library:

DEFINE_string 'name' 'world' 'name to say hello to' 'n'
              ^----^ SC2034: FLAGS_name appears unused. Verify use (or export if used externally).

For a more extensive list of changes, check out the ChangeLog.

Happy ShellChecking!

2 thoughts on “What’s new in ShellCheck v0.7.0?”

  1. Sir;

    Possible shellcheck issue. I am one of those relative newbies to bash shell scripting (long time retired programmer). I am not experienced enough to know if this syntax is properly quoted. In testing using the following appears to work. PS4 is the system variable that formats the prefix for the -x log messages.

    echo \’$(eval echo “${PS4}”)\'”my custom message”

    FYI my PS4 value came primarily from another internet source (note min one or two embedded spaces depending on whether there is a function name and the first colon is my addition). Clearly there I believe is also the possibility for spaces in BASH_SOURCE)

    export PS4=’+:${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }’

    My requirement is that the sourced code always contains the fully pathed file name in the source statement that is I believe subsequently stored in ${BASH_SOURCE} which means that the ${LINENO} I see in a trap for a non zero return code should in theory allow me assure the saved PS4 value in PS4save has a matching LINENO. Also it means after the fact the log file contains a fully pathed file name and line number to extract the original source code. I may also be able to construct the PS4 value minus the appropriate +’s in the trap without passing the value. Also, at best I might be able to generate the value for a function pass again less the +’s without generating it as the statement is executed possibly using the history information in the system.

    I am working at enhancing the -x debug log. The first enhancement is to be able to add my own messages. To do so I have found the following code works and does not require modification when $PS4 is altered.

    #!bin/bash
    echo \’$(eval echo “${PS4}”)\'”my custom message”

    Saving prior state information in this manner where echo is the statement I want to be able to message about later also appears to be interesting:

    I have tried the following and it appears to work.
    function \’$(eval echo “${PS4}”)\’ …

    I believe the following has a chance to work
    lPS4save=\’$(eval echo “${PS4}”)\’; [target statement] {; lRC=$?}

    Where either in a trap (preferred where the trailing lRC=$? would not be required) or a subsequent statement (where it would) I could provide custom messages into the -x debug trace log could be the key to tying messages especially for non zero return codes back to their statements with the proper prefix information.

    After the fact the log files could be processed to insert the original text before the statement execution and to strip out the traced statements from the trap execution (method still tbd) to create what I see as an elegant trace of a bash script’s execution. This logged execution’s output would be compared using a diff utility with prior version of the same enhanced log to see the effect of coding changes.

    While the above on the surface does not appear to be shellcheck related, it is. Shellcheck clearly has value and is helping me. However it flags \’$(eval echo “${PS4}”)\’ as being in need of quotes. I think I have provided said (single) quotes appropriately in a delayed manner as I believe I have successfully passed this into functions where the embedded spaces do not result in multiple arguments. When calling a function I tend to pass the PS4 values for the calling statement into the function. Thereby allowing the calling function to reference the calling statement when making error log entries about argument/parameter issues.

    Does this require a change to shellcheck, if valid, or am I making another newbie mistake I have just not found out about yet?

    Thanks

    1. Please disregard the prior message. I kept attempting other options and this one now seems to be the answer

      $(eval echo ‘”‘”${PS4}”‘”‘)

      where s is a single quote and d is a double quote.
      SDSD${PS$}DSDS

      Thanks for your effort, it really helped.

Leave a Reply to David Shuman Cancel reply