Introduction

Hello! Today I’ll explain the shift command.

The shift command shifts positional parameters ($1, $2, $3…). Super useful when you want to process multiple arguments in order or parse options.

What is the shift Command?

The shift command is a Bash built-in that shifts positional parameters to the left.

When executed, $2 becomes $1, $3 becomes $2, and so on - arguments move forward one by one. The original $1 disappears.

Basic Syntax

1
shift [n]
  • Without n: shifts by 1
  • With n: shifts by n positions

Usage Examples

Example 1: Basic Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash

echo "Initial state"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"

shift

echo "After shift"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"

Run:

1
./script.sh apple banana orange

Output:

1
2
3
4
5
6
7
8
Initial state
$1 = apple
$2 = banana
$3 = orange
After shift
$1 = banana
$2 = orange
$3 =

Example 2: Process All Arguments

1
2
3
4
5
6
7
#!/bin/bash

echo "Received arguments:"
while [ $# -gt 0 ]; do
    echo "- $1"
    shift
done

Run:

1
./script.sh file1.txt file2.txt file3.txt

Output:

1
2
3
4
Received arguments:
- file1.txt
- file2.txt
- file3.txt

$# is the argument count. Loop until no arguments remain.

Example 3: Shift Multiple

1
2
3
4
5
6
7
#!/bin/bash

echo "Initial: $1 $2 $3 $4 $5"

shift 2  # Shift by 2

echo "After shift 2: $1 $2 $3"

Run:

1
./script.sh one two three four five

Output:

1
2
Initial: one two three four five
After shift 2: three four five

Example 4: Option Parsing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/bash

verbose=false
output_file=""

while [ $# -gt 0 ]; do
    case $1 in
        -v|--verbose)
            verbose=true
            shift
            ;;
        -o|--output)
            output_file="$2"
            shift 2  # Shift both option and value
            ;;
        -h|--help)
            echo "Usage: $0 [-v] [-o FILE] [files...]"
            exit 0
            ;;
        -*)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
        *)
            # Remaining arguments are filenames
            break
            ;;
    esac
done

echo "Verbose: $verbose"
echo "Output: $output_file"
echo "Files: $@"

Run:

1
./script.sh -v -o output.txt file1.txt file2.txt

Output:

1
2
3
Verbose: true
Output: output.txt
Files: file1.txt file2.txt

Example 5: Process After First Argument

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

command=$1
shift  # Remove command name

case $command in
    add)
        echo "Adding: $@"
        ;;
    delete)
        echo "Deleting: $@"
        ;;
    list)
        echo "Listing: $@"
        ;;
    *)
        echo "Unknown command: $command"
        exit 1
        ;;
esac

Run:

1
./script.sh add item1 item2 item3

Output:

1
Adding: item1 item2 item3

Example 6: Batch File Processing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Usage: $0 file1 file2 ..." >&2
    exit 1
fi

total=0

while [ $# -gt 0 ]; do
    if [ -f "$1" ]; then
        lines=$(wc -l < "$1")
        echo "$1: $lines lines"
        total=$((total + lines))
    else
        echo "$1: File not found" >&2
    fi
    shift
done

echo "Total: $total lines"

Run:

1
./script.sh file1.txt file2.txt file3.txt

Tips & Notes

shift vs for Loop

For processing arguments, for loops can be simpler.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Using shift
while [ $# -gt 0 ]; do
    echo $1
    shift
done

# Using for (simpler)
for arg in "$@"; do
    echo $arg
done

However, shift is essential for option parsing.

$@ vs $*

1
2
3
4
5
6
7
8
9
# $@ - Treat each argument individually (recommended)
for arg in "$@"; do
    echo "Arg: $arg"
done

# $* - Treat all arguments as one string
for arg in "$*"; do
    echo "Arg: $arg"
done

Use "$@" in general.

Error Checking for shift

Shift errors when no arguments exist.

1
2
3
4
5
6
7
8
#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Arguments required" >&2
    exit 1
fi

shift

Practical Usage

Git-Style Subcommand Processing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/bash

SCRIPT_NAME=$(basename $0)

if [ $# -eq 0 ]; then
    echo "Usage: $SCRIPT_NAME <command> [options]"
    exit 1
fi

subcommand=$1
shift

case $subcommand in
    init)
        echo "Initializing..."
        # Initialization
        ;;
    deploy)
        environment=${1:-production}
        echo "Deploying to $environment..."
        ;;
    status)
        echo "Checking status..."
        ;;
    *)
        echo "Unknown command: $subcommand"
        echo "Available: init, deploy, status"
        exit 1
        ;;
esac

Flexible Option Parsing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/bin/bash

# Defaults
mode="normal"
input_file=""
output_file=""
verbose=false

while [ $# -gt 0 ]; do
    case $1 in
        -m|--mode)
            mode="$2"
            shift 2
            ;;
        -i|--input)
            input_file="$2"
            shift 2
            ;;
        -o|--output)
            output_file="$2"
            shift 2
            ;;
        -v|--verbose)
            verbose=true
            shift
            ;;
        -h|--help)
            cat << EOF
Usage: $0 [OPTIONS]

Options:
  -m, --mode MODE       Operation mode (normal/debug/test)
  -i, --input FILE      Input file
  -o, --output FILE     Output file
  -v, --verbose         Verbose output
  -h, --help            Show this help
EOF
            exit 0
            ;;
        -*)
            echo "Error: Unknown option: $1" >&2
            exit 1
            ;;
        *)
            echo "Error: Unexpected argument: $1" >&2
            exit 1
            ;;
    esac
done

# Verify settings
$verbose && echo "Mode: $mode"
$verbose && echo "Input: $input_file"
$verbose && echo "Output: $output_file"

Backup Script

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
set -euo pipefail

if [ $# -eq 0 ]; then
    echo "Usage: $0 <file1> [file2] [file3] ..." >&2
    exit 1
fi

backup_dir="$HOME/backups/$(date +%Y%m%d)"
mkdir -p "$backup_dir"

echo "Backup destination: $backup_dir"

while [ $# -gt 0 ]; do
    if [ -f "$1" ]; then
        cp -v "$1" "$backup_dir/"
    elif [ -d "$1" ]; then
        cp -rv "$1" "$backup_dir/"
    else
        echo "Warning: $1 not found" >&2
    fi
    shift
done

echo "Backup completed"

Summary

Key points about the shift command:

  • Shifts positional parameters to the left
  • $1 disappears, $2 becomes new $1
  • shift n can shift multiple positions
  • Essential for option parsing
  • Often combined with while loops to process all arguments
  • Frequently used with $# (argument count)

When writing scripts that handle multiple arguments or options, shift is indispensable!