How to Use Command-Line Arguments in Bash Scripts

Abstract Bash script pipeline receiving multiple log files as command-line inputs

What You’ll Learn

In this lesson, you will learn how Bash scripts receive information from the command line. These values, called command-line arguments, allow one script to process different log files without changing its source code.

  • Understand positional parameters such as $1 and $0.
  • Check how many arguments a script received with $#.
  • Pass a log file name to a reusable Bash script.
  • Process multiple command-line arguments with "$@".

The Concept

A command-line argument is a value written after a command or script name. For example:

./count-errors.sh access.log

Here, access.log is an argument passed to count-errors.sh. The script can read that value and use it as the name of the file to process.

Bash stores command-line arguments in special variables called positional parameters:

  • $0 is the name used to run the script.
  • $1 is the first argument.
  • $2 is the second argument.
  • $# is the number of arguments.
  • "$@" represents all arguments, with each argument kept as a separate value.

Arguments make scripts reusable. Instead of creating one script for every log file, you can create one script and provide a different file name each time you run it.

Basic Example

Suppose an access log contains lines like these:

192.168.1.25 - - [18/Aug/2026:10:14:22] "GET / HTTP/1.1" 200 1250
192.168.1.30 - - [18/Aug/2026:10:15:03] "GET /missing.html HTTP/1.1" 404 512
192.168.1.42 - - [18/Aug/2026:10:16:17] "POST /login HTTP/1.1" 200 842
192.168.1.51 - - [18/Aug/2026:10:17:09] "GET /old-page HTTP/1.1" 404 488

Save this script as count-errors.sh. It expects exactly one argument: the log file to inspect.

#!/usr/bin/env bash

if [[ $# -ne 1 ]]; then
    printf 'Usage: %s LOG_FILE\n' "$0"
    exit 1
fi

log_file=$1

if [[ ! -f "$log_file" ]]; then
    printf 'Error: file not found: %s\n' "$log_file"
    exit 1
fi

error_count=$(grep -c ' 404 ' "$log_file")
printf '404 responses in %s: %s\n' "$log_file" "$error_count"

Make the script executable, then run it with the log file as its argument:

chmod +x count-errors.sh
./count-errors.sh access.log

Expected Output

404 responses in access.log: 2

How the Code Works

A process flow showing command-line log file arguments entering a Bash script, being validated for the expected count, checked for existing files, and then processed safely. A multiple-file path uses each quoted argument separately before producing log results.
Bash validates command-line arguments, checks each log file, and safely processes one or more files using positional parameters and quoted arguments.

The first line tells the operating system to use Bash to run the script:

#!/usr/bin/env bash

This check makes sure the user supplied one argument:

if [[ $# -ne 1 ]]; then
    printf 'Usage: %s LOG_FILE\n' "$0"
    exit 1
fi

$# contains the number of arguments. The condition means “if the number of arguments is not equal to one.” The script then prints a usage message and stops with exit 1. The %s placeholder is replaced by $0, which is the script name.

Next, the script saves the first argument in a descriptive variable:

log_file=$1

Using a variable such as log_file makes the rest of the script easier to read. The file check prevents grep from trying to process a file that does not exist.

The command substitution stores the result of grep in error_count:

error_count=$(grep -c ' 404 ' "$log_file")

The -c option tells grep to count matching lines. The pattern includes spaces around 404 so it looks for the HTTP status field rather than every occurrence of those characters. Quoting "$log_file" helps the script handle file names that contain spaces.

Another Example

A script can accept more than one argument. The special variable "$@" gives you each argument separately, which is useful when checking several log files in one command.

The following script searches each supplied log file for server errors with status code 500:

#!/usr/bin/env bash

if [[ $# -eq 0 ]]; then
    printf 'Usage: %s LOG_FILE [LOG_FILE ...]\n' "$0"
    exit 1
fi

for log_file in "$@"; do
    if [[ ! -f "$log_file" ]]; then
        printf '\nFile not found: %s\n' "$log_file"
        continue
    fi

    printf '\n500 responses in %s:\n' "$log_file"
    grep -n ' 500 ' "$log_file"
done

You could run it with two files:

./find-server-errors.sh access.log archived-access.log

The loop processes the first file, then the second file. Quoting "$@" is important because it preserves each command-line argument as its own value.

Common Mistakes

  • Forgetting the argument: Running a script that expects $1 without providing a value can produce confusing results. Check $# before using required arguments.
  • Using the wrong positional parameter: In ./script.sh access.log, $0 is ./script.sh, while $1 is access.log.
  • Leaving file arguments unquoted: Use "$log_file" when passing a file variable to a command. Without quotes, a file name containing spaces can be treated as multiple arguments.
  • Using $* instead of "$@" for multiple values: For a simple loop over arguments, for log_file in "$@" preserves each original argument separately.

Try It Yourself

Create a script named count-status.sh that accepts one log file and one HTTP status code. It should:

  • Print a usage message unless exactly two arguments are provided.
  • Store the first argument as the log file name.
  • Store the second argument as the status code.
  • Count lines containing that status code and print the result.

For example, running ./count-status.sh access.log 200 should report how many successful responses appear in the log.

Challenge

Build a reusable script named show-log-matches.sh. It should accept a log file and a search term as command-line arguments.

  • Require exactly two arguments.
  • Print a helpful usage message when the arguments are missing or extra.
  • Print an error and stop if the log file does not exist.
  • Use grep -n to display matching lines with their line numbers.

For example, ./show-log-matches.sh access.log /login should display log lines containing /login.

Solution

#!/usr/bin/env bash

if [[ $# -ne 2 ]]; then
    printf 'Usage: %s LOG_FILE SEARCH_TERM\n' "$0"
    exit 1
fi

log_file=$1
search_term=$2

if [[ ! -f "$log_file" ]]; then
    printf 'Error: file not found: %s\n' "$log_file"
    exit 1
fi

printf 'Matches for "%s" in %s:\n' "$search_term" "$log_file"
grep -n "$search_term" "$log_file"

The script uses $1 for the log file and $2 for the search term. It validates the argument count before using either value, checks that the requested file exists, and then passes both arguments to grep. The -n option adds line numbers to matching log entries.

Key Takeaways

  • Bash places the first command-line argument in $1, the second in $2, and so on.
  • $# tells a script how many arguments it received.
  • $0 is useful for displaying the script’s name in usage messages.
  • Quote argument variables, such as "$1" or "$log_file", when passing them to commands.
  • Use "$@" to process all command-line arguments while keeping each one separate.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top