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#
- 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!