False positive: SC2119 and SC2120 - "$#" is not an argument / positional parameter.
For bugs
- Rule Id: SC2119 and SC2120
- My shellcheck version: 0.9.0
- [x] The rule's wiki page does not already cover this:
- https://github.com/koalaman/shellcheck/wiki/SC2120
- https://github.com/koalaman/shellcheck/wiki/SC2119
- Does not mention correct use of
$#even if those were relevant checks to the use of it.
- [x] I tried on https://www.shellcheck.net/ and verified that this is still a problem on the latest commit
For new checks and feature suggestions
- [x] https://www.shellcheck.net/ (i.e. the latest commit) currently gives no useful warnings about this
- [x] I searched through https://github.com/koalaman/shellcheck/issues and didn't find anything related
Here's a snippet or screenshot that shows the problem:
#!/usr/bin/env bash
function temp::create_dir() {
check::arg_count 0 "$#"
echo "This errors."
}
function temp::init() {
check::arg_count 0 "$#"
temp::create_dir
echo "This does not error."
echo "Because it's not called here."
}
Here's what shellcheck currently says:
Line 2: function temp::create_dir() { ^-- SC2120 (warning): temp::create_dir references arguments, but none are ever passed.
Line 9: temp::create_dir ^-- SC2119 (info): Use temp::create_dir "$@" if function's $1 should mean script's $1.
Here's what I wanted or expected to see:
The Bash special parameter "$#" expands to the number of positional parameters in decimal. (manual)
- This variable is function local and does not require any parameters to be passed to the function to be used correctly. With no parameters passed it expands to 0.
-
SC2120 and SC2119 both complain about references to the script's parameters, as in
$@,$1, etc. I am not referencing these parameters. I am referencing the local count of these parameters, which legally can be 0 when no parameters are passed. Therefore, these checks for the use of positional parameters ($@,$1, etc.) don't necessarily apply to the use of this special parameter ('$#') for the COUNT of the positional parameters. This behavior needs decoupled and made into its own check. - Within my function I have not referenced any positional parameters, yet shellcheck insists that for my code to be correct I need to pass the script's
$@as$1. This is wrong and$# != $@. Positional parameters do not need to be passed for correct use of$#since it is function local and simply a decimal with no reference to anything outside the function, hence it is not a reference to a positional parameter, simply a count of those if they were used. - Context: I use this to check if a function was given the correct number of arguments shown in
check::arg_count
All of the above is repeating the same concept rather exhaustively but I wanted to make sure my point was made. lol
Thanks for the great tool and I hope this gets patched soon!
An example of how referencing of $# can be useful:
For bugs
- Rule Id (if any, e.g. SC1000): SC2120, SC2119
- My shellcheck version (
shellcheck --versionor 'online'): online - [x] I tried on shellcheck.net and verified that this is still a problem on the latest commit
- [ ] It's not reproducible on shellcheck.net, but I think that's because it's an OS, configuration or encoding issue
For new checks and feature suggestions
- [x] shellcheck.net (i.e. the latest commit) currently gives no useful warnings about this
Here's a snippet or screenshot that shows the problem:
#!/bin/bash
f1() {
[ $# = 1 ]
x=$1
echo "$x"
}
f0a() {
[ $# = 0 ]
echo
}
f0b() {
echo
}
f1 1
f0a
f0b
Here's what shellcheck currently says:
Line 9 SC2120: f0a references arguments, but none are ever passed.
Line 19 SC2119: Use f0a "$@" if function's $1 should mean script's $1.
Here's what I wanted or expected to see:
No warning.
I'm curious, what's the reason for/value of testing for zero arguments passed in a function that never gets/uses any arguments?
Personally, I don't think I disagree that checking $# probably shouldn't trigger these warnings but I'm not sure I see why having the line that is triggering them is of value in the first place.
Thank you for the question. In my scripts, I usually use only positional arguments like this:
#!/bin/bash
set -eux -o pipefail
[ $# = 2 ] || [ $# = 3 ]
input=$1
output=$2
something_optional=${3:-}
If a script doesn't take arguments, I specify that explicitly with [ $# = 0 ] (so without reading the whole script, I can be sure that variables like $1 and $@ are not used).
I prefer scripts to functions (for modularity), but function are also used sometimes. When they are used, they follow the same conventions.
On June 17, 2023, Jake Turner wrote:
"SC2120 https://www.shellcheck.net/wiki/SC2120 and SC2119 https://www.shellcheck.net/wiki/SC2119 both complain about references to the script's parameters, as in $@, $1, etc. I am not referencing these parameters. I am referencing the local count of these parameters, which legally can be 0 when no parameters are passed. Therefore, these checks for the use of positional parameters ($@, $1, etc.) don't necessarily apply to the use of this special parameter ('$#') for the COUNT of the positional parameters. This behavior needs decoupled and made into its own check."
When a function references, "$#", the hashtag special parameter, or, "${#@}", the hashtag parameter expansion of the ampersand special parameter, the result of the expansion is the number of positional parameters given as operands to the function call, and not the number of pos-parms given to the script: "The positional parameters are temporarily replaced when a shell function is executed," and elsewhere, "when a function completes, the values of the positional parameters and the special parameter # are restored to the values they had prior to the function's execution. (Manual, v.5.2.32(1)-release)"
Referencing the syntax "$#" may not be the exact same thing as referencing the "$1" or "$@" syntactical forms, but the parameters in question each report on some quality or other of the same singular set of data: the positional parameters as a whole. If any of the pos-parms are changed, at least one of the related parameters [ * @ # 0 BASH_ARGC BASH_ARGV BASH_ARGV0 (etc) ] will reflect that event.
On June 17, 2023, Jake Turner wrote:
"...yet shellcheck insists that for my code to be correct..."
A suggestion from Shellcheck really doesn't render any code "correct" or "incorrect," necessarily. There are many styles in use. Shellcheck supports a lot of them.
Cheers, Wiley
On Mon, Feb 3, 2025 at 12:23 AM Kirill Bulygin @.***> wrote:
In my scripts, I usually use only positional arguments like this:
#!/bin/bash set -eux -o pipefail
[ $# = 2 ] || [ $# = 3 ] input=$1 output=$2 something_optional=${3:-}
If a script doesn't take arguments, I specify that explicitly with [ $# = 0 ] (so without reading the whole script, I can be sure that variables like $1 and $@ are not used).
I prefer scripts to functions (for modularity), but function are also used sometimes. When they are used, they follow the same conventions.
— Reply to this email directly, view it on GitHub https://github.com/koalaman/shellcheck/issues/2781#issuecomment-2630260196, or unsubscribe https://github.com/notifications/unsubscribe-auth/AUF2F24TANX4JZVJAIRKLL32N4RP5AVCNFSM6AAAAABU3R4RXGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMMZQGI3DAMJZGY . You are receiving this because you are subscribed to this thread.Message ID: @.***>
So I ran across this today. Since I create functions that can be used by a number of different other scripts, I like to be defensive when checking that the call has the correct number of inputs, and provide error messages when someone is doing something odd. (Note: this message seems to persist even if the function is exported, when it is impossible for the tool to actually be aware of all of the calling sites, as I could source the file in an interactive shell for example.)
Ideally today, the documentation for SC2120 would list exceptions for checking against 0 and if the function is exported.
Eventually, it would be nice for those special cases to be identified and not flagged, but I understand if that's too difficult.