CrackOps
Interview PrepTracksLinuxAWS LabSoonInterview RoomSoon
Sign inStart free
CrackOps

Interview prep for the people who keep production up.

[email protected]
ProductInterview PrepTracksDashboardInterview Room
RolesDevOpsSREPlatformCloud
CompanyAboutContactPrivacyTerms
© CrackOps
Linux / Shell Scripting / bash: arguments

bash: arguments

Script arguments: $1, $#, "$@", shift and getopts

Intermediatebashscriptingcligetopts

A script receives its command-line arguments as positional parameters: $1, $2 and so on, $# for the count and "$@" for all of them, each kept whole. shift moves through them, ${1:?} makes one required, and getopts parses short options like -n 3 -v the standard way.

Syntax$0 $1..$9 ${10} $# "$@" "$*" shift [N] ${1:?msg} while getopts "n:vh" opt; do case $opt in ...; esac; done

Watch it run

Before writing argument handling, see exactly how the shell hands words to a script.

devops@devops-lab: ~/scripts — zsh — 96×28
➜ ~/scripts
Step 1 of 3
cat args.sh

Prints the special parameters, then loops over "$@" with brackets around each argument so you can see where each one starts and ends.

Anatomy of a command

$
command./rotate.sh

The script. Inside it this is $0, the name it was called by.

How it works

When bash starts a script it receives the argument vector built by the caller's execve(): argv[0] becomes $0 and argv[1..] become the positional parameters $1, $2... (two-digit ones need braces: ${10}). Quoting was already handled by the calling shell, so "orders api" arrives as one argument with a space in it and the script never sees the quotes. $# is the count. "$@" expands to every parameter as a separate word, exactly as received; "$*" joins them into one word using the first character of IFS; unquoted $@ and $* are re-split on spaces. shift N discards the first N parameters and renumbers the rest. set -- a b c replaces them, which is handy for testing. getopts is a builtin that reads one option per call, puts its letter in a variable and any value in OPTARG, and keeps its position in OPTIND; it handles bundled flags (-vn3) and stops at the first non-option or at --. It does not support long options like --keep; for those, loop with case and shift.

Options you'll actually use

$0The script's name as it was called (./rotate.sh)
$1 ... $9, ${10}Positional parameters; braces needed from 10 on
$#Number of arguments
"$@"All arguments, each one a separate word: pass them on unchanged
"$*"All arguments joined into one string (by the first char of IFS)
shift / shift NDrop the first (N) arguments; $2 becomes $1
${1:?usage...}Exit with a message if the first argument is missing or empty
${2:-default}Use a default for an optional argument
getopts "n:vh" optParse -n VALUE, -v and -h; a colon means 'takes a value'
getopts ":n:v"Leading colon: silent mode, you print the errors (opt is : or ?)
$OPTARG / $OPTINDValue of the current option / index of the next argument
set -- a b cReplace the positional parameters (test parsing interactively)
--End of options: everything after is an argument, even -x

Common mistakes

exec ./real.sh $* in a wrapper

Unquoted, arguments with spaces are split apart and globs expand.

exec ./real.sh "$@"
echo $10

This is ${1} followed by the character 0, not the tenth argument.

echo "${10}"
Using $1 deep inside a function to mean the script's first argument

Inside a function $1 is the function's own first argument.

Copy script arguments into named variables at the top: env=$1
Forgetting shift $((OPTIND - 1)) after getopts

$1 is still the first option (-v), not the first real argument.

while getopts ...; done; shift $((OPTIND - 1))
No check on argument count under set -u

$1 with no arguments aborts with 'unbound variable' and no helpful message.

dir=${1:?usage: $0 DIR} # or: (( $# == 1 )) || usage

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Write a script hello.sh that prints 'Hello, NAME' for its first argument and 'Hello, stranger' when none is given. Run it both ways.

  2. 2

    Write count.sh that prints how many arguments it got and then each one in brackets. Call it with: a "b c" d

  3. 3

    Using set --, set the arguments to one two three, then print $1 after one shift and $# after two shifts.

  4. 4

    Write a getopts loop (in your shell, using set --) that accepts -v and -n VALUE, and prints the parsed values for: -v -n 5 file.txt

Related commands

bash: functionsFunctions: name a block, pass arguments, return a statusbash: variablesShell variables: assign, quote, default and exportbash: arraysArrays: lists and key-value maps in bashbash: ifConditions: if, elif, else, test brackets and case
Previousbash: redirectionNextssh-keygen
Linux / Shell Scripting / bash: arguments

bash: arguments

Script arguments: $1, $#, "$@", shift and getopts

Intermediatebashscriptingcligetopts

A script receives its command-line arguments as positional parameters: $1, $2 and so on, $# for the count and "$@" for all of them, each kept whole. shift moves through them, ${1:?} makes one required, and getopts parses short options like -n 3 -v the standard way.

Syntax$0 $1..$9 ${10} $# "$@" "$*" shift [N] ${1:?msg} while getopts "n:vh" opt; do case $opt in ...; esac; done

Watch it run

Before writing argument handling, see exactly how the shell hands words to a script.

devops@devops-lab: ~/scripts — zsh — 96×28
➜ ~/scripts
Step 1 of 3
cat args.sh

Prints the special parameters, then loops over "$@" with brackets around each argument so you can see where each one starts and ends.

Anatomy of a command

$
command./rotate.sh

The script. Inside it this is $0, the name it was called by.

How it works

When bash starts a script it receives the argument vector built by the caller's execve(): argv[0] becomes $0 and argv[1..] become the positional parameters $1, $2... (two-digit ones need braces: ${10}). Quoting was already handled by the calling shell, so "orders api" arrives as one argument with a space in it and the script never sees the quotes. $# is the count. "$@" expands to every parameter as a separate word, exactly as received; "$*" joins them into one word using the first character of IFS; unquoted $@ and $* are re-split on spaces. shift N discards the first N parameters and renumbers the rest. set -- a b c replaces them, which is handy for testing. getopts is a builtin that reads one option per call, puts its letter in a variable and any value in OPTARG, and keeps its position in OPTIND; it handles bundled flags (-vn3) and stops at the first non-option or at --. It does not support long options like --keep; for those, loop with case and shift.

Options you'll actually use

$0The script's name as it was called (./rotate.sh)
$1 ... $9, ${10}Positional parameters; braces needed from 10 on
$#Number of arguments
"$@"All arguments, each one a separate word: pass them on unchanged
"$*"All arguments joined into one string (by the first char of IFS)
shift / shift NDrop the first (N) arguments; $2 becomes $1
${1:?usage...}Exit with a message if the first argument is missing or empty
${2:-default}Use a default for an optional argument
getopts "n:vh" optParse -n VALUE, -v and -h; a colon means 'takes a value'
getopts ":n:v"Leading colon: silent mode, you print the errors (opt is : or ?)
$OPTARG / $OPTINDValue of the current option / index of the next argument
set -- a b cReplace the positional parameters (test parsing interactively)
--End of options: everything after is an argument, even -x

Common mistakes

exec ./real.sh $* in a wrapper

Unquoted, arguments with spaces are split apart and globs expand.

exec ./real.sh "$@"
echo $10

This is ${1} followed by the character 0, not the tenth argument.

echo "${10}"
Using $1 deep inside a function to mean the script's first argument

Inside a function $1 is the function's own first argument.

Copy script arguments into named variables at the top: env=$1
Forgetting shift $((OPTIND - 1)) after getopts

$1 is still the first option (-v), not the first real argument.

while getopts ...; done; shift $((OPTIND - 1))
No check on argument count under set -u

$1 with no arguments aborts with 'unbound variable' and no helpful message.

dir=${1:?usage: $0 DIR} # or: (( $# == 1 )) || usage

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Write a script hello.sh that prints 'Hello, NAME' for its first argument and 'Hello, stranger' when none is given. Run it both ways.

  2. 2

    Write count.sh that prints how many arguments it got and then each one in brackets. Call it with: a "b c" d

  3. 3

    Using set --, set the arguments to one two three, then print $1 after one shift and $# after two shifts.

  4. 4

    Write a getopts loop (in your shell, using set --) that accepts -v and -n VALUE, and prints the parsed values for: -v -n 5 file.txt

Related commands

bash: functionsFunctions: name a block, pass arguments, return a statusbash: variablesShell variables: assign, quote, default and exportbash: arraysArrays: lists and key-value maps in bashbash: ifConditions: if, elif, else, test brackets and case
Previousbash: redirectionNextssh-keygen