### Using wc -c for file size Source: https://www.shellcheck.net/wiki/SC2012 This example demonstrates using the portable `wc -c` command to get file size, noting potential performance differences and whitespace issues. ```shell wc -c $(( $(wc -c < "filename") )) ``` -------------------------------- ### Example of space collapsing Source: https://www.shellcheck.net/wiki/SC2291 Demonstrates how unquoted multiple spaces collapse into a single space when passed as arguments. ```shell $ echo Hello World Hello World ``` -------------------------------- ### Get ShellCheck Help Information Source: https://www.shellcheck.net/wiki/directive Run this command to display the help information for ShellCheck, including a list of supported options and their usage. ```shell shellcheck --help ``` -------------------------------- ### List Optional ShellCheck Checks Source: https://www.shellcheck.net/wiki/directive Run this command to see a list of all optional checks supported by ShellCheck, along with examples. ```shell shellcheck --list-optional ``` -------------------------------- ### Example of preserved spaces with quoting Source: https://www.shellcheck.net/wiki/SC2291 Shows how quoting preserves multiple spaces in the output. ```shell $ echo "Hello World" Hello World ``` -------------------------------- ### Conditional function definition and call Source: https://www.shellcheck.net/wiki/SC2218 This example demonstrates defining a function conditionally based on the operating system and then calling it. Ensure the definition is complete before the call. ```sh case "$(uname -s)" in Linux) hi() { echo "Hello from Linux"; } ;; Darwin) hi() { echo "Hello from macOS"; } ;; *) hi() { echo "Hello from something else"; } ;; esac hi ``` -------------------------------- ### Problematic sudo redirection examples Source: https://www.shellcheck.net/wiki/SC2024 These examples demonstrate incorrect usage of sudo with shell redirections, which can lead to permission denied errors. ```shell # Write to a file sudo echo 3 > /proc/sys/vm/drop_caches # Append to a file sudo echo 'export FOO=bar' >> /etc/profile # Read from a file sudo wc -l < /etc/shadow ``` -------------------------------- ### Handling process substitution failures with wait Source: https://www.shellcheck.net/wiki/SC2312 This example demonstrates how to handle failures in process substitution by using file descriptor duplication and `wait` to retain and check the exit codes of the substituted processes. ```shell generate_data() { declare i for (( i = 0 ; i < 5 ; ++i )) do date -d "$RANDOM hours" done } consume_data() { declare line while IFS= read -r line do echo Consuming line: "$line" done } declare \ input_file_descriptor \ process # The following statement # # - uses process substitution to allow us to read the output of `generate_data` # via a filename and # - duplicates the file descriptor for that file so that it is not # immediately closed # # Note that process substitution uses either `pipe(2)` or named pipes (FIFOs) # with `O_RDONLY` or `O_WRONLY`, and so the file descriptor that is duplicated # via `[N]<&WORD` is only opened for reads exec {input_file_descriptor}< <( generate_data ) process=$! # Returns non-zero if `consume_data` does consume_data <&"$input_file_descriptor" # Returns non-zero if `generate_data` does wait "$process" ``` -------------------------------- ### Client-side variable expansion example Source: https://www.shellcheck.net/wiki/SC2029 This code demonstrates how a variable is expanded on the client side before being sent to the server. This is often not the intended behavior for remote command execution. ```shell ssh host "echo clienthostname" ``` -------------------------------- ### Problematic code with `=` starting a command Source: https://www.shellcheck.net/wiki/SC2275 This code demonstrates the problematic syntax where a variable assignment or command starts with an equals sign, which ShellCheck flags as an error. ```shell my_variable =value ``` -------------------------------- ### Correct sudo redirection with tee and cat Source: https://www.shellcheck.net/wiki/SC2024 These examples show the correct way to perform file operations with sudo by piping data through commands like `tee` for writing/appending and `cat` for reading. ```shell # Write to a file echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null # Append to a file echo 'export FOO=bar' | sudo tee -a /etc/profile > /dev/null # Read from a file sudo cat /etc/shadow | wc -l ``` -------------------------------- ### Demonstrate Character Class Case Conversion Source: https://www.shellcheck.net/wiki/SC2018 This example demonstrates the correct behavior of the 'tr' command using character classes to convert accented characters to uppercase. ```shell $ tr '[:lower:]' '[:upper:]' <<< "My fiancée ordered a piña colada." MY FIANCÉE ORDERED A PIÑA COLADA. ``` -------------------------------- ### Understanding `&&` and `||` operator associativity Source: https://www.shellcheck.net/wiki/SC2015 This example illustrates the left-associativity of `&&` and `||` operators, showing how they are evaluated as nested conditional commands. ```shell ((([[ $dryrun ]]) && echo "Would delete file") || rm file) ``` -------------------------------- ### Localized String Literal Example Source: https://www.shellcheck.net/wiki/SC2247 These examples demonstrate how `echo $'(cmd)'` and `echo $'{var}'` are interpreted as localized string literals, not command substitutions. If command substitution was intended, flip the `"` and `$`. ```shell echo $'(cmd)' # Supposed to be "$(cmd)" ``` ```shell echo $'{var}' # Supposed to be "${var}" ``` -------------------------------- ### printf format string interpretation example Source: https://www.shellcheck.net/wiki/SC2059 This example demonstrates how `printf` interprets format specifiers and escape sequences when a variable is included in the format string, potentially leading to errors. ```shell coverage='96%' printf "Unit test coverage: %s\n" "$coverage" ``` ```shell coverage='96%' printf "Unit test coverage: $coverage\n" ``` -------------------------------- ### Find command with exception for tar.gz Source: https://www.shellcheck.net/wiki/SC2146 This example demonstrates an exception to the SC2146 rule, showing how to decompress all gz files except tar.gz by using grouping and an explicit execution. ```shell find . -name '*.tar.gz' -o \( -name '*.gz' -exec gzip -d {} + \) ``` -------------------------------- ### Parsing units in shell scripting Source: https://www.shellcheck.net/wiki/SC2262 These examples illustrate different scenarios of shell command parsing units. Understanding these units is crucial for correctly predicting when aliases and other expansions will take effect. ```bash # A single command followed by a linefeed is one unit unit 1 ``` ```bash # These commands are in the same parsing unit because # there is no line feed between them unit 2; unit 2; ``` ```bash # These commands are in the same parsing unit because # they are part of the same top level brace group { unit 3 unit 3 } ``` ```bash # These commands are in the same parsing unit because # there is no linefeed between the groups. { unit 4 }; { unit 4 } ``` -------------------------------- ### Multiline String Exception Source: https://www.shellcheck.net/wiki/SC1078 This example shows how to correctly define a multiline string using single quotes, which is an exception to the SC1078 warning when intended. ```shell var='multiline value' ``` -------------------------------- ### Example of ls output with special characters Source: https://www.shellcheck.net/wiki/SC2012 Demonstrates how `ls` can display filenames with special characters, showing potential ambiguity in output. ```shell $ ls -l total 0 -rw-r----- 1 me me 0 Feb 5 20:11 foo?bar -rw-r----- 1 me me 0 Feb 5 2011 foo?bar -rw-r----- 1 me me 0 Feb 5 20:11 foo?bar ``` -------------------------------- ### Correct 'do' placement in shell loop Source: https://www.shellcheck.net/wiki/SC1063 Use a semicolon or line feed before 'do' to correctly start a loop block. This ensures proper shell syntax. ```shell for file in *; do echo "$file" done # or for file in * dO echo "$file" done ``` -------------------------------- ### Truncating a file with redirection Source: https://www.shellcheck.net/wiki/SC2189 This example demonstrates a technically valid but potentially confusing use case where a redirection is used to truncate a file. It is recommended to rewrite such code for clarity. ```shell echo foo | > "$(cat)" ``` -------------------------------- ### Parameter expansion starts with unexpected quotes Source: https://www.shellcheck.net/wiki/SC2301 Use correct syntax for parameter expansion to avoid unexpected quotes. Ensure translated strings are properly formatted. ```shell echo ${"Hello World"} ``` ```shell echo $"Hello World" ``` -------------------------------- ### Get ShellCheck Help Information Source: https://www.shellcheck.net/wiki/Directive Displays the manual page for ShellCheck, providing comprehensive information about its usage and options. Alternatively, use 'shellcheck --help'. ```shell man shellcheck ``` ```shell shellcheck --help ``` -------------------------------- ### Demonstrate ASCII Range Case Conversion Issues Source: https://www.shellcheck.net/wiki/SC2018 This example shows how the 'tr' command with a simple ASCII range fails to convert accented characters to uppercase. ```shell $ tr 'a-z' 'A-Z' <<< "My fiancée ordered a piña colada." MY FIANCéE ORDERED A PIñA COLADA. ``` -------------------------------- ### find command with basename filter for absolute paths Source: https://www.shellcheck.net/wiki/SC2012 When using an absolute path for `find`, this example shows how to use `basename` in the filter to exclude the directory itself. ```shell $ theDir="$HOME/.snapshot" $ find "$theDir" -maxdepth 1 ! -name "$(basename $theDir)" /home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0005 /home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0405 /home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0805 /home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_1605 /home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_2005 /home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_1205 /home/matt/.snapshot/snapmirror.1501b4aa-3f82-11e8-9c31-00a098cef13d_2147868328.2019-04-01_190000 ``` -------------------------------- ### Variable name starts with a number Source: https://www.shellcheck.net/wiki/SC2282 Variable names in shell scripts cannot start with a digit. Use a letter or underscore instead. ```shell 411toppm=true ``` ```shell _411toppm=true ``` -------------------------------- ### Corrected code without cat Source: https://www.shellcheck.net/wiki/SC2002 These examples show the corrected versions of the problematic code, utilizing input redirection (`<`) or passing the filename directly to the command. This avoids the unnecessary `cat` process. ```shell < file tr ' ' _ | nl ``` ```shell while IFS= read -r i; do echo "${i%?}"; done < file ``` -------------------------------- ### Incorrect command starting with '===' Source: https://www.shellcheck.net/wiki/SC2274 This code is flagged by SC2274. It's a command that starts with '===', which may have been intended as a comment but is missing the '#' prefix. ```shell ===================== MAIN SECTION ======================= ``` -------------------------------- ### Bash parameter expansion vs sed for prefix replacement Source: https://www.shellcheck.net/wiki/SC2001 Demonstrates equivalent prefix replacement using sed and Bash parameter expansion. ```bash var="foo foo" # the following two echo's should be equivalent: echo "$var" | sed 's/^foo/bar/g' echo "${var/#foo/bar}" ``` -------------------------------- ### Find character index and extract substring with parameter expansion Source: https://www.shellcheck.net/wiki/SC2308 Use parameter expansion to find the index of a character and extract substrings. This example demonstrates finding the position of the first colon and calculating the index based on the length of the part before it. ```shell # Find character index in string pos=${input%%:*} pos=$((${#pos}+1)) ``` -------------------------------- ### Corrected code for piping issues Source: https://www.shellcheck.net/wiki/SC2216 These corrected examples show how to properly handle command output when the target command does not read from stdin. This includes using `while read` loops, `find -exec`, or logical OR operators. ```shell ls ``` ```shell cat files | while IFS= read -r file; do rm -- "$file"; done ``` ```shell find . -type f -exec cp {} dir \; ``` ```shell rm file || true ``` -------------------------------- ### Quoting command names starting with a dash Source: https://www.shellcheck.net/wiki/SC2215 If a command name must start with a dash, quote it to explicitly indicate it's a command name and not a flag. ```shell command "-with-dash" ``` -------------------------------- ### Use `--` to end options for safety Source: https://www.shellcheck.net/wiki/SC2035 Use `-- *glob*` to ensure that globbed filenames, even those starting with a dash, are not interpreted as command-line options. This is a safer alternative for commands like `tar`. ```shell rm -- * ``` -------------------------------- ### SC1134 Error Message Example Source: https://www.shellcheck.net/wiki/SC1134 This is an example of the error message generated by ShellCheck when it encounters issues parsing the .shellcheckrc file. Ensure the .shellcheckrc file is correctly formatted. ```shell SC1134 (error): Failed to process foo, line bar: Fix any mentioned problems and try again. ``` -------------------------------- ### Correct way to set directory permissions Source: https://www.shellcheck.net/wiki/SC2174 This code shows the correct approach to ensure all created directories have the specified permissions by separating directory creation and permission setting. ```bash mkdir -p foo/bar/baz chmod 0755 foo/bar/baz foo/bar foo ``` -------------------------------- ### Use semicolon or linefeed before 'done' Source: https://www.shellcheck.net/wiki/SC1010 Ensure shell keywords like 'done' are correctly interpreted by placing them at the start of a line, preceded by a semicolon or linefeed. This prevents them from being treated as literal strings. ```shell for f in *; do echo "$f" done ``` ```shell echo $f is done ``` ```shell for f in *; do echo "$f"; done ``` ```shell echo "$f is done" ``` -------------------------------- ### Illustrate command substitution capturing output Source: https://www.shellcheck.net/wiki/SC2091 Demonstrates how command substitution captures output, which can lead to errors if the captured output is then treated as a command. ```shell sayhello() { echo "hello world"; } $(sayhello) ``` -------------------------------- ### Resolve merge conflict markers Source: https://www.shellcheck.net/wiki/SC2273 This example shows typical merge conflict markers left in a script. Ensure merge conflicts are resolved and these markers are removed to avoid syntax errors or unexpected behavior. ```shell <<<<<<< HEAD echo "Goodbye World" ======= echo "Hello World!" >>>>>>> mybranch ``` -------------------------------- ### Get substring by index with expr Source: https://www.shellcheck.net/wiki/SC2308 Avoid using `expr substr` for getting substrings by index as it has unspecified results and is not portable. Use shell parameter expansion (Bash/Ksh) or `cut` (POSIX) for reliable substring extraction. ```shell # Get substring by index col2=$(expr substr "foo bar baz" 8 5) ``` -------------------------------- ### Use parameter expansion for simple pattern matching Source: https://www.shellcheck.net/wiki/SC3057 Demonstrates extracting a substring using parameter expansion to remove a prefix and suffix of a specified length. ```sh #!/usr/bin/env sh part="zyxwvutsrqponmlkjihgfedcba" #echo "${part:5:3}" part="${part%${part#????????}}" part="${part#?????}" printf '%s\n' "${part?}" ``` -------------------------------- ### printf behavior with a placeholder Source: https://www.shellcheck.net/wiki/SC2182 Demonstrates the correct usage of printf with a placeholder and an argument. ```bash $ printf "hello %s\n" "world" hello world ``` -------------------------------- ### Calculate character index using parameter expansion and string length Source: https://www.shellcheck.net/wiki/SC2308 Calculate the index of a delimiter by using parameter expansion to get the part of the string before the delimiter and then finding its length. Add 1 to get the 1-based index. ```shell str="mykey=myvalue" x=${str%%=*} # Assign x="mystr" index=$((${#x}+1)) # Add 1 to length of x ``` -------------------------------- ### Code walkthrough of parameter expansion for substring extraction Source: https://www.shellcheck.net/wiki/SC3057 Explains the steps involved in extracting a substring using parameter expansion, detailing prefix and suffix removal. ```sh #!/usr/bin/env sh part="zyxwvutsrqponmlkjihgfedcba" #echo "${part:5:3}" part="${part%${part#????????}}" ## Removed smallest prefix from text where prefix matches pattern ???????? ## in this case : "rqponmlkjihgfedcba" ## "${part%${part#????????}}" = "${part%"rqponmlkjihgfedcba"}" = "zyxwvuts" ## Removed smallest suffix from part of text where suffix matches pattern ${part#????????} (i.e. "rqponmlkjihgfedcba") part="${part#?????}" ## Removed smallest prefix from text where prefix matches ????? (i.e. "zyxwv") printf '%s\n' "${part?}" ``` -------------------------------- ### Incorrect Shebang Format Source: https://www.shellcheck.net/wiki/SC1084 The shebang line must start with `#!` followed by the interpreter path. Using `!#` will cause an error. ```shell !#/bin/sh echo "Hello World" ``` -------------------------------- ### ShellCheck Optional Check: require-double-brackets Source: https://www.shellcheck.net/wiki/optional Enforces the use of `[[` and warns against `[` in Bash/Ksh. Example shows the use of single brackets. ```shell [ -e /etc/issue ] ``` -------------------------------- ### Bash brace expansion for multiple file extensions Source: https://www.shellcheck.net/wiki/SC2102 Demonstrates using brace expansion in Bash to match multiple specific file extensions. This is a more readable and efficient alternative to multiple globs for common cases. ```shell cat *.{dev,prod,test}.conf ``` -------------------------------- ### Alternative `cd` failure handling Source: https://www.shellcheck.net/wiki/SC2164 Demonstrates alternative ways to handle `cd` failures, including custom messages and conditional execution. ```shell cd foo || { echo "Failure"; exit 1; } ``` ```shell cd foo || ! echo "Failure" ``` ```shell if cd foo; then echo "Ok"; else echo "Fail"; fi ``` ```shell <(cd foo && cmd) ``` -------------------------------- ### ShellCheck Optional Check: quote-safe-variables Source: https://www.shellcheck.net/wiki/optional Recommends quoting variables that do not contain metacharacters. Example shows an unquoted variable. ```shell var=hello; echo $var ``` -------------------------------- ### ShellCheck Optional Check: deprecate-which Source: https://www.shellcheck.net/wiki/optional Suggests using `command -v` as a modern alternative to `which`. Example shows the usage of `which`. ```shell which javac ``` -------------------------------- ### ShellCheck Optional Check: avoid-negated-conditions Source: https://www.shellcheck.net/wiki/optional Recommends removing unnecessary comparison negations. Example demonstrates a negated condition. ```shell [ ! "$var" -eq 1 ] ``` -------------------------------- ### Correct find -exec with sh -c Source: https://www.shellcheck.net/wiki/SC2150 These examples show the correct way to execute shell commands with find -exec by invoking 'sh -c'. The first is insecure, while the second demonstrates secure handling of filenames using positional parameters. ```shell find . -type f -exec sh -c 'cat {} | wc -l' \; # Insecure ``` ```shell find . -type f -exec sh -c 'cat "$1" | wc -l' _ {} \; # Secure ``` -------------------------------- ### Incorrect numeric comparison Source: https://www.shellcheck.net/wiki/SC2210 Use (( )) for arithmetic comparisons in bash. This example shows an incorrect attempt to compare numbers. ```shell if x > 5; then echo "true"; fi ``` -------------------------------- ### Use `#!`, not just `!`, for the shebang Source: https://www.shellcheck.net/wiki/SC1104 The shebang line in shell scripts must start with `#!` to specify the interpreter. Using only `!` is incorrect and will cause errors. ```shell #!/bin/sh echo "Hello" ``` ```shell !/bin/sh echo "Hello" ``` -------------------------------- ### Demonstrate sh -c with Arguments Source: https://www.shellcheck.net/wiki/SC2014 Illustrates how sh -c can be used with arguments, where '_' becomes $0 and the subsequent argument becomes $1 within the inlined script. This is a common pattern when using sh -c with find -exec. ```shell $ sh -c 'echo "$1 is in $(dirname "$1")"' _ "mydir/myfile" ``` -------------------------------- ### Incorrect file descriptor redirection Source: https://www.shellcheck.net/wiki/SC2210 Ensure file descriptor redirections are correctly formatted. This example shows a malformed redirection. ```shell foo > /dev/null 2>1 ``` -------------------------------- ### Apply Directives to Entire Script with Dummy Command Source: https://www.shellcheck.net/wiki/directive Use a dummy command like `true` or `:` with directives to apply them to the entire script when they cannot be placed immediately after the shebang. ```shell # This directive applies to the entire script # shellcheck disable=2086 true # This directive only applies to this function # shellcheck disable=2043 f() { ... } ``` -------------------------------- ### Problematic ash script Source: https://www.shellcheck.net/wiki/SC2187 This is an example of an ash script that will trigger the SC2187 warning because ShellCheck checks ash scripts as dash. ```bash #!/bin/ash echo "Hello World" ``` -------------------------------- ### Demonstrate $ expansion issues in arithmetic Source: https://www.shellcheck.net/wiki/SC2004 Illustrates how wrapping a variable in $ within an arithmetic context can lead to incorrect evaluation. The variable's content is treated as a string expression rather than its numerical value. ```shell $ a='1+1' $ echo $(($a * 5)) # becomes 1+1*5 6 $ echo $((a * 5)) # evaluates as (1+1)*5 10 ``` -------------------------------- ### Command substitution with variable Source: https://www.shellcheck.net/wiki/SC2091 Example of command substitution used with a variable holding a command name, leading to potential errors. ```shell x=sayhello; $($x) ``` -------------------------------- ### POSIX sh for multiple file extensions Source: https://www.shellcheck.net/wiki/SC2102 Illustrates the POSIX-compliant way to match multiple file extensions by listing each glob pattern separately. This approach is compatible with all POSIX shells. ```shell cat *.dev.conf *.prod.conf *.test.conf ``` -------------------------------- ### ShellCheck Optional Check: useless-use-of-cat Source: https://www.shellcheck.net/wiki/optional Identifies 'Useless Use Of Cat' (UUOC) patterns. Example demonstrates UUOC with `cat` and `grep`. ```shell cat foo | grep bar ``` -------------------------------- ### Correct parameter expansion `${x}` Source: https://www.shellcheck.net/wiki/SC2298 Use the standard ${VAR:-default} syntax for parameter expansion with a default value. ```shell retries=${RETRIES:-3} ``` -------------------------------- ### Use mktemp instead of deprecated tempfile Source: https://www.shellcheck.net/wiki/SC2186 Avoid using the deprecated `tempfile` command. Use `mktemp` for creating temporary files as it is more portable and widely supported. ```shell tmp=$(tempfile) ``` ```shell tmp=$(mktemp) ``` -------------------------------- ### ShellCheck Optional Check: check-unassigned-uppercase Source: https://www.shellcheck.net/wiki/optional Warns if uppercase variables are used without being assigned. Example shows an unassigned uppercase variable. ```shell echo $VAR ``` -------------------------------- ### Illustrate for loop word splitting and globbing Source: https://www.shellcheck.net/wiki/SC2013 Demonstrates how a `for` loop processes a file containing words and a wildcard, showing the resulting word splitting and glob expansion. ```shell foo * bar ``` -------------------------------- ### ShellCheck Optional Check: avoid-nullary-conditions Source: https://www.shellcheck.net/wiki/optional Suggests using `-n` explicitly with `[` for non-empty string checks. Example shows a nullary condition. ```shell [ "$var" ] ``` -------------------------------- ### Illustrate direct command execution Source: https://www.shellcheck.net/wiki/SC2091 Shows how directly calling a function or command executes it without capturing its output. ```shell sayhello() { echo "hello world"; } sayhello ``` -------------------------------- ### ShellCheck Optional Check: add-default-case Source: https://www.shellcheck.net/wiki/optional Suggests adding a default case to `case` statements. Example shows a basic `case` statement. ```shell case $? in 0) echo 'Success';; esac ``` -------------------------------- ### Correct 'ln' command with specified destination Source: https://www.shellcheck.net/wiki/SC2226 This snippet demonstrates the correct way to use the 'ln' command, explicitly specifying both the source file and the destination directory. For linking into the current directory, use '.' as the destination. ```shell ln "$file" "$dir" ``` ```shell ln /foo/bar/baz . ``` -------------------------------- ### Correct usage of `command -v` and `hash` Source: https://www.shellcheck.net/wiki/SC2230 Use `command -v` for checking command existence and path, or `hash` for checking existence without obtaining a path. These are POSIX standard alternatives to `which`. ```shell # For the path of a single, unaliased, external command, # or to check whether this will just "run" in this shell: command -v grep # To check whether commands exist, without obtaining a reusable path: hash grep ``` -------------------------------- ### Missing Space Before Comment Marker Source: https://www.shellcheck.net/wiki/SC1099 Ensure a space precedes the '#' character when starting a comment. This is necessary for the shell to correctly interpret the line as a comment. ```shell while sleep 1 do# show time date done ``` ```shell while sleep 1 do # show time date done ``` -------------------------------- ### Bash extglob for multiple file extensions Source: https://www.shellcheck.net/wiki/SC2102 Shows how to use Bash's extended globbing feature (`extglob`) with an OR pattern to match multiple file extensions. Ensure `shopt -s extglob` is enabled for this to work. ```shell cat *.@(dev|prod|test).conf ``` -------------------------------- ### Get last argument in POSIX sh Source: https://www.shellcheck.net/wiki/SC3057 This method retrieves the last argument passed to a script and works in very old Bourne shell versions. ```sh #!/bin/sh for argument in "$@"; do : # :, also called as `true`, is a no-op here done printf '%s\n' "${argument-}" ``` -------------------------------- ### Alternative methods for writing to files with sudo Source: https://www.shellcheck.net/wiki/SC2024 These snippets offer alternative commands like `dd` and `sed` to achieve file truncation with sudo, similar to `tee`. ```shell echo 'data' | sudo dd of=file ``` ```shell echo 'data' | sudo sed 'w file' ``` -------------------------------- ### Use `elif` instead of `elseif` in shell scripts Source: https://www.shellcheck.net/wiki/SC1131 Use `elif` to start another branch in shell conditional statements. `elseif` is not a valid keyword. ```shell if false then echo "hi" elseif true then echo "ho" fi ``` ```shell if false then echo "hi" elif true then echo "ho" fi ``` -------------------------------- ### ShellCheck Optional Check: check-set-e-suppressed Source: https://www.shellcheck.net/wiki/optional Notifies when `set -e` is suppressed during function invocation. Example demonstrates `set -e` suppression within a function. ```shell set -e; func() { cp *.txt ~/backup; rm *.txt; }; func && echo ok ``` -------------------------------- ### Simple while read loop for file input Source: https://www.shellcheck.net/wiki/SC2013 The most straightforward way to loop through a file line by line using a `while read` loop, without any filtering. Also shows an alternative for reading from a variable. ```shell while IFS= read -r line do echo "Line: $line" done < file ``` ```shell # or: done <<< "$variable" ``` -------------------------------- ### ShellCheck Optional Check: check-extra-masked-returns Source: https://www.shellcheck.net/wiki/optional Checks for additional scenarios where exit codes might be masked. Example shows a potentially masked exit code. ```shell rm -r "$(get_chroot_dir)/home" ``` -------------------------------- ### Using /usr/bin/env for interpreter path Source: https://www.shellcheck.net/wiki/SC2239 When the exact path to an interpreter is unknown or varies, use '/usr/bin/env' followed by the interpreter name. This searches the user's PATH for the executable, offering flexibility while adhering to best practices. ```shell #!/usr/bin/env bash echo "Hello World" ``` -------------------------------- ### Identical HTML output with and without unnecessary quotes Source: https://www.shellcheck.net/wiki/SC2140 These examples demonstrate that unnecessary quotes around HTML attributes do not change the output, but can trigger SC2140. ```shell echo "" > file.html ``` ```shell echo "" > file.html ``` -------------------------------- ### Correctly running commands as another user with `sudo` Source: https://www.shellcheck.net/wiki/SC2117 This code shows the correct way to execute a command as another user using `sudo`. `sudo` is preferred as it requires less quoting and can be configured for passwordless execution. ```shell whoami sudo whoami ``` -------------------------------- ### Alternative using `hash` Source: https://www.shellcheck.net/wiki/SC2230 The `hash` command can be used as an alternative to `command -v` to observe standard failure behavior when checking for command existence. ```shell $ hash ``` -------------------------------- ### Distinguish Directives from Comments Source: https://www.shellcheck.net/wiki/SC1126 If a comment mentions ShellCheck but is not a directive, rewrite or capitalize it to avoid misinterpretation. This example shows a comment about variable naming conventions. ```shell var=1 # ShellCheck encourages lowercase variable names ``` -------------------------------- ### Find command with redirection applied to entire command Source: https://www.shellcheck.net/wiki/SC2227 Illustrates how a redirection in a find command applies to the entire command, not just a specific action. ```shell { find . -name '*.ppm' -exec pnmtopng {} \; } > {}.png ``` -------------------------------- ### Incorrect 'do' placement in shell loop Source: https://www.shellcheck.net/wiki/SC1063 Ensure 'do' starts a new line or statement in shell loops. Incorrect placement can lead to syntax errors. ```shell for file in * do echo "$file" done ``` -------------------------------- ### Performance Comparison: Subshell vs. Direct Condition Source: https://www.shellcheck.net/wiki/SC2233 Demonstrates the significant performance difference between using subshells with parentheses and direct conditional checks in a loop. ```shell $ i=0; time while ( [ "$i" -lt 10000 ] ); do i=$((i+1)); done real 0m6.998s user 0m3.453s sys 0m3.464s ``` ```shell $ i=0; time while [ "$i" -lt 10000 ]; do i=$((i+1)); done real 0m0.055s user 0m0.054s sys 0m0.001s ``` -------------------------------- ### Variable assignment with inner quotes Source: https://www.shellcheck.net/wiki/SC2223 When quoting default assignments, ensure inner quotes are correctly handled. This example shows assigning 'foo' with and without quotes. ```shell : ${var:='foo'} # Assigns foo without quotes ``` ```shell : "${var:='foo'}" # Assigns 'foo' with quotes ``` -------------------------------- ### Compare string length methods in Bash Source: https://www.shellcheck.net/wiki/SC2000 Demonstrates three ways to check if the length of the first argument is greater than 1: using `wc -c`, `wc -m`, and `${#1}`. Prefer `${#1}` for efficiency. ```bash #!/usr/bin/env bash if [ "$( echo \"$1\" | wc -c )" -gt 1 ]; then echo "greater than 1" fi if [ "$( echo \"$1\" | wc -m )" -gt 1 ]; then echo "greater than 1" fi if [ "${#1}" -gt 1 ]; then echo "greater than 1" fi ``` -------------------------------- ### Common cases where SC2086 warning may seem unnecessary Source: https://www.shellcheck.net/wiki/SC2086 These examples show cases where the SC2086 warning might appear, but quoting is still recommended for robustness or to avoid potential issues like Denial of Service (DoS) with specific patterns. ```shell cmd <<< $var # Requires quoting on Bash 3 (but not 4+) ``` ```shell : ${var=default} # Should be quoted to avoid DoS when var='*/*/*/*/*/*' ``` -------------------------------- ### Problematic code with literal carriage return Source: https://www.shellcheck.net/wiki/SC1017 This example shows a shell script with Windows-style line endings, where `^M` indicates a literal carriage return character. ```shell $ cat -v myscript #!/bin/sh^M echo "Hello World"^M ``` -------------------------------- ### Iterate over files using globs Source: https://www.shellcheck.net/wiki/SC2045 Correct and robust way to iterate over files using shell globs. Includes a check for the case where no matching files are found. ```shell for f in *.wav do [[ -e "$f" ]] || break # handle the case of no *.wav files echo "$f" done ``` -------------------------------- ### Incorrect unary condition syntax Source: https://www.shellcheck.net/wiki/SC1019 Unary test operators require an operand. This example shows incorrect usage where '-x' is not followed by a file or other valid operand. ```shell [ -x ] ``` -------------------------------- ### Simple POSIX find -exec Source: https://www.shellcheck.net/wiki/SC2044 For simple commands without aggregation or shell script bodies, `find -exec` is a POSIX-compatible and efficient solution. It directly executes a command for each found file. ```shell # Simple and POSIX find mydir -name '*.mp3' -exec play {} \; ``` -------------------------------- ### Get substring by index with POSIX cut Source: https://www.shellcheck.net/wiki/SC2308 Use `cut -c` for POSIX-compliant substring extraction by character index. Be cautious if the input can contain multiple lines. ```shell # Get substring by index (POSIX) col2="$(printf 'foo bar baz\n' | cut -c 8-12)" ``` -------------------------------- ### ls -c1 output for directory listing Source: https://www.shellcheck.net/wiki/SC2012 Shows the output of `ls -c1` for a directory, listing filenames with potential formatting differences. ```shell $ ls -c1 .snapshot rnapdev1-svm_4_05am_6every4hours.2019-04-01_1605 rnapdev1-svm_4_05am_6every4hours.2019-04-01_2005 rnapdev1-svm_4_05am_6every4hours.2019-04-02_0005 rnapdev1-svm_4_05am_6every4hours.2019-04-02_0405 rnapdev1-svm_4_05am_6every4hours.2019-04-02_0805 rnapdev1-svm_4_05am_6every4hours.2019-04-02_1205 snapmirror.1501b4aa-3f82-11e8-9c31-00a098cef13d_2147868328.2019-04-01_190000 ``` -------------------------------- ### SC1041 and SC1042 error messages Source: https://www.shellcheck.net/wiki/SC1041 Companion SC1041 and SC1042 errors may appear together, pointing to the start of the here document rather than the specific line with the terminator issue. ```sh In foo line 4: Hello ^-- SC1041: Found 'eof' further down, but not on a separate line. ^-- SC1042: Close matches include '-eof' (!= 'eof'). ``` -------------------------------- ### Buffer output with temporary file and cat Source: https://www.shellcheck.net/wiki/SC2005 Use a temporary file and 'cat' to buffer potentially large output without using more memory or modifying the content. This is an alternative to 'echo "$(cmd)"' when dealing with significant amounts of data. ```shell # Buffer up potentially large output without using more memory or modifying the content in any way cmd > file.tmp cat file.tmp ``` -------------------------------- ### Intentional Fork Bomb Example Source: https://www.shellcheck.net/wiki/SC2264 ShellCheck does not warn about intentional fork bombs. If a fork bomb triggers this warning, add a leading command or condition to ignore the issue. ```bash :() { true && :|: & } ``` -------------------------------- ### `command -v` behavior with multiple arguments Source: https://www.shellcheck.net/wiki/SC2230 Demonstrates that `command -v` exits with 0 if any of the provided commands exist, unlike `which` which might be expected to fail if not all exist. This check is opt-in. ```shell # grep is in /usr/bin/grep # foobar is not in path # $ command -v -- grep foobar; echo $? ``` -------------------------------- ### Problematic glob range expression Source: https://www.shellcheck.net/wiki/SC2102 This example shows a glob range expression that incorrectly attempts to match a range of numbers by repeating characters. This will only match a single digit. ```shell echo [100-999].txt ``` -------------------------------- ### Correct Shebang Placement Source: https://www.shellcheck.net/wiki/SC1128 This code shows the correct way to place a shebang line, ensuring it is the first line of the script. Comments should follow the shebang. ```bash #!/bin/bash # Copyright 2018 Foobar, All rights reserved ``` -------------------------------- ### Example of `eval` exploit with unquoted variables Source: https://www.shellcheck.net/wiki/SC2089 Demonstrates a severe security vulnerability that can arise from using `eval` with unquoted variables, especially within loops processing filenames. ```shell for f in *.txt; do args="-lh '$1'" # Example security exploit eval ls "$args" # Do not copy and use done ``` -------------------------------- ### Handle multiple files from command substitution with a loop Source: https://www.shellcheck.net/wiki/SC2046 If a command substitution outputs multiple files, use a while loop to process each line individually and prevent word splitting. ```shell getfilename | while IFS='' read -r line do ls -l "$line" done ``` -------------------------------- ### Correct unary condition syntax Source: https://www.shellcheck.net/wiki/SC1019 Unary test operators must be followed by an operand. This example demonstrates the correct usage by providing 'myfile' as the operand for the '-x' test. ```shell [ -x "myfile" ] ``` -------------------------------- ### Indirect expansion using bash Source: https://www.shellcheck.net/wiki/SC3053 Switch to a shell like bash that supports indirect expansion for a straightforward solution. ```bash #!/bin/bash name="PATH" echo "${!name}" ```