iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
In Bash, the script name is $0, the arguments are $1, $2, and so on, and $# tells you how many arguments were supplied. Quote "$@" when forwarding or looping over arguments, use shift as you consume them, and use the POSIX getopts builtin for conventional short options such as -v and -o file.
How Linux passes arguments to a shell script
When you run ./report.sh weekly.csv email@example.com, the shell starts the interpreter named by the script’s shebang and assigns the command-line words to positional parameters. In a normal Bash script, $0 is the script name and the first user-supplied word is $1. The next words become $2, $3, and later parameters. If no argument is supplied, the corresponding positional parameter expands to an empty value.
Recommended Free Tools
The assignment is based on shell words, not on visible spaces. For example, ./report.sh 'Q4 sales.csv' supplies one argument containing a space. The quotes used at the prompt tell the invoking shell to keep that text together; they are not part of the value received by the script.
Special parameters you use most often
| Parameter | Meaning | Typical use |
|---|---|---|
$0 |
Name of the script in a normal Bash script invocation | Usage and error messages |
$1, $2, and later parameters |
Positional arguments in the order supplied | Reading fixed operands |
$# |
Number of positional parameters | Checking required or maximum argument counts |
"$@" |
One quoted word for each positional argument | Forwarding or iterating without merging arguments |
"$*" |
One quoted word containing all arguments joined with the first character of IFS |
Deliberately creating one combined string |
OPTARG |
Argument associated with the option currently processed by getopts |
Reading the value after an option such as -o |
OPTIND |
Index of the next argument examined by getopts |
Shifting past options to reach operands |
Read a required argument safely
For a script with a fixed operand list, direct positional access is the clearest approach. Validate the count before assigning or using a required value. The following script requires one file name and prints a usage message to standard error when it is missing:
#1 Best Overall
#!/usr/bin/env bash
if (( $# < 1 )); then
printf 'usage: %s FILEn' "$0" >&2
exit 2
fi
file=$1
printf 'file=%sn' "$file"
$# is evaluated numerically in the arithmetic condition. Exit status 2 conventionally signals incorrect command-line usage. The assignment file=$1 is safe as an assignment, but quote the variable whenever you pass its value to another command:
cat -- "$file"
The -- tells programs that support it to stop interpreting subsequent text as options. It is useful when a user supplies a file whose name begins with a hyphen. Whether a particular utility supports -- is utility-specific, so check that command’s documentation when needed.
Require exactly one argument
Use an exact count when extra operands would indicate a mistake:
if (( $# != 1 )); then
printf 'usage: %s FILEn' "$0" >&2
exit 2
fi
file=$1
Require a minimum while allowing the rest
For a command that needs at least two operands, test the lower bound and then process all remaining values with "$@" or a controlled loop:
if (( $# < 2 )); then
printf 'usage: %s SOURCE... DESTn' "$0" >&2
exit 2
fi
Do not test a required value only after shifting it away. Check the count first, then consume parameters in a known order.
Why quoting matters: "$@", "$*", and unquoted values
Unquoted parameter expansion can undergo word splitting and pathname expansion. That can turn one argument containing spaces into several words, or replace wildcard characters with matching file names in the current directory. Treat command-line values as data and quote them when expanding them.
Free tools Windows power users keep installed
One-click scans. No signup required.
Forward every original argument
Use a quoted "$@" when calling another command or script:
Rank #2
#!/usr/bin/env bash
/usr/local/bin/process-files "$@"
If the caller supplied three arguments, the called program receives three arguments, including any spaces or wildcard characters inside each original value. A loop has the same property:
for arg in "$@"; do
printf 'arg=%sn' "$arg"
done
With an invocation such as ./show.sh 'annual report.pdf' '*.csv' '', the loop runs three times. The first value contains a space, the second contains a literal asterisk, and the third is an empty argument.
What quoted "$*" does
Quoted "$*" deliberately produces one word. The positional parameters are joined using the first character of IFS (normally a space). Thus, arguments one, two words, and three become one combined string. This is appropriate only when a single display or log string is what you want; it must not replace "$@" when forwarding arguments.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches| Expansion | Result when arguments are alpha, two words, and gamma |
Use it for |
|---|---|---|
"$@" |
Three separate words | Passing or iterating over original arguments |
"$*" |
One word joined with the first IFS character |
Intentional aggregation into one string |
$@ or $* without quotes |
Subject to splitting and pathname expansion | Avoid for data values |
Quote individual parameters too: use "$1" rather than $1 when passing the first argument to a command, and use "$OPTARG" for an option’s value.
Access the tenth and later arguments
A positional parameter is a parameter denoted by one or more digits, except for the special parameter zero. Because a bare $10 is parsed as $1 followed by the literal character 0, use braces for two-digit and longer parameters:
printf 'tenth=%sn' "${10}"
printf 'twelfth=%sn' "${12}"
Braces also make boundaries explicit in ordinary expansions, for example "${1}.bak". In most scripts, looping over "$@" is easier to maintain than naming a large number of individual parameters.
Consume arguments with shift
shift removes the leading positional parameters and renumbers the rest. After shift, the old $2 becomes the new $1, the old $3 becomes $2, and so forth. The count in $# decreases by the number shifted.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #3
A simple command that accepts a mode followed by any number of files can process one value at a time:
#!/usr/bin/env bash
if (( $# < 1 )); then
printf 'usage: %s MODE [FILE...]n' "$0" >&2
exit 2
fi
mode=$1
shift
printf 'mode=%sn' "$mode"
for file in "$@"; do
printf 'file=%sn' "$file"
done
Validate before shifting. Calling shift when there are not enough positional parameters is an error in many shells and can make the remaining logic misleading.
Use a loop for custom syntax
A manual while/case parser is useful when a project intentionally supports long options or syntax that getopts does not describe. Each branch must consume exactly the parameters it recognizes and reject malformed input:
#!/usr/bin/env bash
verbose=false
output=
while (( $# > 0 )); do
case $1 in
-v|--verbose)
verbose=true
shift
;;
-o|--output)
if (( $# < 2 )); then
printf 'option %s requires an argumentn' "$1" >&2
exit 2
fi
output=$2
shift 2
;;
--)
shift
break
;;
-* )
printf 'unknown option: %sn' "$1" >&2
exit 2
;;
* )
break
;;
esac
done
for operand in "$@"; do
printf 'operand=%sn' "$operand"
done
The -- case gives callers an explicit way to mark the end of options. The parser leaves the first non-option and everything after it in "$@", so the final loop handles operands without losing their boundaries.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteReplace the positional-parameter list with set --
set -- one two replaces the current positional parameters with one and two. This is the supported way to rebuild an argument list after filtering or transforming values. Do not try to assign to a positional parameter with syntax such as $2=value; positional parameters are not ordinary assignable variables.
set -- 'first value' second
printf 'count=%sn' "$#"
printf 'first=%sn' "$1"
printf 'second=%sn' "$2"
Because set -- changes the same parameters that your parser may still need, save any values you must retain before replacing the list.
Parse short options with POSIX getopts
getopts is the portable shell interface for short options and their option-arguments. Put the option letters in an optstring: a letter followed by a colon requires a value. In ':vo:', -v is a flag and -o requires an argument. The leading colon requests explicit error handling for unknown options and missing option arguments.
#!/bin/sh
verbose=false
output=
while getopts ':vo:' opt; do
case $opt in
v) verbose=true ;;
o) output=$OPTARG ;;
:) printf 'option -%s requires an argumentn' "$OPTARG" >&2; exit 2 ;;
?) printf 'unknown option: -%sn' "$OPTARG" >&2; exit 2 ;;
esac
done
shift $((OPTIND - 1))
for operand do
printf 'operand=%sn' "$operand"
done
How the parser advances
During each iteration, getopts places the option letter in opt. For an option that takes a value, its value is in OPTARG. OPTIND records the index of the next argument to inspect. Once the loop ends, shift $((OPTIND - 1)) removes the options and their values, leaving the remaining operands in "$@".
For example, an invocation such as ./copy.sh -v -o 'nightly backup' source.txt destination.txt sets verbose to true, stores nightly backup in output, and leaves source.txt and destination.txt as operands after the shift.
Handle errors explicitly
With a leading colon in the optstring, a missing option argument produces : in the case statement and the option letter in OPTARG. An unrecognized option produces ? and its letter in OPTARG. This lets the script print a precise message and exit with a usage error instead of continuing with an empty setting.
Always quote "$OPTARG" when assigning or passing an option value. A value such as reports/final copy.txt must remain one shell word.
Choosing between direct access, a manual loop, and getopts
| Approach | Portability | Short options | Option arguments | Long options | Error handling and clarity |
|---|---|---|---|---|---|
Direct $1, $2 access |
Works in Bash and POSIX shells | Must be implemented by your script | Must be validated by your script | Must be implemented by your script | Clearest for a fixed operand list |
Manual while/case with shift |
Can remain POSIX when written with POSIX syntax | Supports any syntax you code | Requires explicit count checks and consumption | Supports long options and custom forms | Flexible, but every malformed case needs validation |
getopts |
Portable POSIX interface | Designed for short options | Built in through the optstring and OPTARG |
Not a general long-option parser | Consistent option scanning; a leading colon enables explicit missing/unknown-option messages |
Use direct access when the interface is simply “one input file” or “source and destination.” Choose getopts for conventional short flags and option values. Choose a manual loop when long options or a project-specific grammar are requirements, and budget code for validation, the -- terminator, and every shift.
The shebang determines which shell reads the script
The first line selects the interpreter when the file is executed directly. #!/usr/bin/env bash requests Bash; #!/bin/sh requests the system’s POSIX shell. A script advertised as /bin/sh should use POSIX syntax and utilities, including the POSIX form of getopts. Bash-only arithmetic syntax such as (( $# < 1 )), arrays, or other Bash extensions should be kept in a Bash script.
The way you invoke the file also matters. Running ./script.sh after making it executable uses the shebang. Running bash script.sh explicitly asks Bash to interpret it, while running sh script.sh asks the system’s sh even if the file contains a Bash shebang. Keep the shebang, the syntax, and the documented invocation consistent.
Best Value
Common mistakes and their fixes
Using $10 for the tenth argument
Symptom: The script prints the first argument followed by a zero. Cause: The shell parsed $10 as $1 plus the character 0. Fix: Use "${10}", or iterate over "$@".
Forwarding $@ without quotes
Symptom: A filename containing spaces arrives as multiple arguments, or a wildcard expands unexpectedly. Cause: Unquoted expansion permits splitting and pathname expansion. Fix: Use "$@" when forwarding and "$arg" inside a loop.
Using "$*" when separate arguments are required
Symptom: A called command receives one long argument instead of the original list. Cause: Quoted "$*" joins all positional parameters using the first IFS character. Fix: Replace it with "$@" unless one combined string is intentional.
Reading $1 before checking $#
Symptom: The script proceeds with an empty path or emits a confusing downstream error. Cause: The required operand was never validated. Fix: Check the count first and print a usage line that includes "$0".
Shifting too far
Symptom: A parser reports a shift error or silently loses the value it needed. Cause: The code shifted without confirming that the required value followed an option. Fix: Check $# before shift 2, and handle a missing option argument as an error.
Expecting getopts to parse long options
Symptom: An argument such as --output is rejected or split into unexpected option letters. Cause: getopts is intended for short options. Fix: Use a manual case loop for a deliberately supported long-option interface, or redesign the interface around short options.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Mixing Bash syntax with a /bin/sh shebang
Symptom: The script works when run with Bash but fails when run as sh script.sh. Cause: The invoking shell does not implement the Bash-specific syntax. Fix: Either use POSIX syntax throughout or change the shebang and documented command to Bash.
A complete pattern for options followed by operands
The following POSIX-style script combines validation, option parsing, safe quoting, and operand processing. It accepts -v, requires a value for -o, reports malformed options, and then prints every remaining operand separately:
#!/bin/sh
verbose=false
output=
while getopts ':vo:' opt; do
case $opt in
v)
verbose=true
;;
o)
output=$OPTARG
;;
:)
printf '%s: option -%s requires an argumentn' "$0" "$OPTARG" >&2
exit 2
;;
?)
printf '%s: unknown option -%sn' "$0" "$OPTARG" >&2
exit 2
;;
esac
done
shift $((OPTIND - 1))
if [ "$#" -lt 1 ]; then
printf 'usage: %s [-v] [-o FILE] INPUT...n' "$0" >&2
exit 2
fi
if [ "$verbose" = true ]; then
printf 'output=%sn' "$output"
fi
for input in "$@"; do
printf 'input=%sn' "$input"
done
The option loop runs before the required-operand check because getopts must remove the options first. The count test then applies to the remaining operands, not to the original complete command line.
Quick Recap
A practical checklist before shipping a script
- Choose and document the interpreter in the shebang.
- Use
$0in usage and error messages so the displayed command name matches the invocation. - Check
$#before reading required operands or shifting them away. - Use braces for positional parameters numbered 10 or higher.
- Quote
"$1","$2","$OPTARG", and loop variables when they are passed as data. - Use
"$@"to preserve one word per original argument; use"$*"only for intentional joining. - Use
shiftonly after confirming that the parameters you intend to consume exist. - Use
set --when replacing the positional-parameter list instead of attempting to assign to$1or$2. - Use
getoptsfor portable short-option parsing and shift byOPTIND-1before processing operands. - If you implement long options manually, handle unknown options, missing values, and the
--end-of-options marker. - Test arguments containing spaces, wildcard characters, empty strings, leading hyphens, and no arguments at all.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →

