shell-bash

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shell & Bash Scripting

Shell & Bash Scripting

Overview

概述

Shell scripting patterns for automation, system administration, and CLI tools.

适用于自动化、系统管理和CLI工具的Shell脚本模式。

Script Fundamentals

脚本基础

Script Structure

脚本结构

bash
#!/usr/bin/env bash
#
bash
#!/usr/bin/env bash
#

Script: backup.sh

Script: backup.sh

Description: Backup files to remote server

描述:将文件备份到远程服务器

Usage: ./backup.sh [options] <source> <destination>

使用方法:./backup.sh [选项] <源路径> <目标路径>

set -euo pipefail # Exit on error, undefined vars, pipe failures IFS=$'\n\t' # Safer word splitting

set -euo pipefail # 遇到错误、未定义变量或管道失败时退出 IFS=$'\n\t' # 更安全的单词分割

Constants

常量

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" readonly LOG_FILE="/var/log/${SCRIPT_NAME%.sh}.log"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" readonly LOG_FILE="/var/log/${SCRIPT_NAME%.sh}.log"

Default values

默认值

VERBOSE=false DRY_RUN=false COMPRESS=true
VERBOSE=false DRY_RUN=false COMPRESS=true

Cleanup on exit

退出时清理

cleanup() { local exit_code=$? # Cleanup temporary files rm -f "${TEMP_FILE:-}" exit "$exit_code" } trap cleanup EXIT
cleanup() { local exit_code=$? # 清理临时文件 rm -f "${TEMP_FILE:-}" exit "$exit_code" } trap cleanup EXIT

Logging functions

日志函数

log() { local level="$1" shift echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "$LOG_FILE" }
info() { log "INFO" "$@"; } warn() { log "WARN" "$@" >&2; } error() { log "ERROR" "$@" >&2; } debug() { [[ "$VERBOSE" == true ]] && log "DEBUG" "$@" || true; }
die() { error "$@" exit 1 }
log() { local level="$1" shift echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "$LOG_FILE" }
info() { log "INFO" "$@"; } warn() { log "WARN" "$@" >&2; } error() { log "ERROR" "$@" >&2; } debug() { [[ "$VERBOSE" == true ]] && log "DEBUG" "$@" || true; }
die() { error "$@" exit 1 }

Usage

使用说明

usage() { cat <<EOF Usage: $SCRIPT_NAME [options] <source> <destination>
Options: -v, --verbose Enable verbose output -n, --dry-run Show what would be done -h, --help Show this help message
Examples: $SCRIPT_NAME /data /backup $SCRIPT_NAME -v --dry-run /home/user /mnt/backup EOF }
usage() { cat <<EOF Usage: $SCRIPT_NAME [options] <source> <destination>
Options: -v, --verbose 启用详细输出 -n, --dry-run 显示将要执行的操作 -h, --help 显示此帮助信息
Examples: $SCRIPT_NAME /data /backup $SCRIPT_NAME -v --dry-run /home/user /mnt/backup EOF }

Main function

主函数

main() { parse_args "$@" validate_inputs perform_backup }
main() { parse_args "$@" validate_inputs perform_backup }

Run main if script is executed directly

如果脚本直接执行则运行主函数

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi
undefined
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi
undefined

Argument Parsing

参数解析

bash
undefined
bash
undefined

Using getopts (POSIX)

使用getopts(POSIX标准)

parse_args_getopts() { while getopts ":vnh" opt; do case $opt in v) VERBOSE=true ;; n) DRY_RUN=true ;; h) usage; exit 0 ;; ?) die "Invalid option: -$OPTARG" ;; :) die "Option -$OPTARG requires an argument" ;; esac done shift $((OPTIND - 1))
SOURCE="${1:-}"
DESTINATION="${2:-}"
}
parse_args_getopts() { while getopts ":vnh" opt; do case $opt in v) VERBOSE=true ;; n) DRY_RUN=true ;; h) usage; exit 0 ;; ?) die "Invalid option: -$OPTARG" ;; :) die "Option -$OPTARG requires an argument" ;; esac done shift $((OPTIND - 1))
SOURCE="${1:-}"
DESTINATION="${2:-}"
}

Using manual parsing (supports long options)

使用手动解析(支持长选项)

parse_args() { while [[ $# -gt 0 ]]; do case "$1" in -v|--verbose) VERBOSE=true shift ;; -n|--dry-run) DRY_RUN=true shift ;; -c|--compress) COMPRESS=true shift ;; --no-compress) COMPRESS=false shift ;; -h|--help) usage exit 0 ;; --) shift break ;; -*) die "Unknown option: $1" ;; *) break ;; esac done
# Positional arguments
SOURCE="${1:-}"
DESTINATION="${2:-}"
}
parse_args() { while [[ $# -gt 0 ]]; do case "$1" in -v|--verbose) VERBOSE=true shift ;; -n|--dry-run) DRY_RUN=true shift ;; -c|--compress) COMPRESS=true shift ;; --no-compress) COMPRESS=false shift ;; -h|--help) usage exit 0 ;; --) shift break ;; -*) die "Unknown option: $1" ;; *) break ;; esac done
# 位置参数
SOURCE="${1:-}"
DESTINATION="${2:-}"
}

Validation

验证

validate_inputs() { [[ -z "$SOURCE" ]] && die "Source path is required" [[ -z "$DESTINATION" ]] && die "Destination path is required" [[ -e "$SOURCE" ]] || die "Source does not exist: $SOURCE" }

---
validate_inputs() { [[ -z "$SOURCE" ]] && die "源路径是必填项" [[ -z "$DESTINATION" ]] && die "目标路径是必填项" [[ -e "$SOURCE" ]] || die "源路径不存在:$SOURCE" }

---

Variables and Data

变量与数据

Variable Operations

变量操作

bash
undefined
bash
undefined

Variable assignment

变量赋值

name="John" readonly CONSTANT="immutable"
name="John" readonly CONSTANT="immutable"

Default values

默认值

name="${name:-default}" # Use default if unset or empty name="${name:=default}" # Assign default if unset or empty name="${name:+alternative}" # Use alternative if set and non-empty name="${name:?error message}" # Error if unset or empty
name="${name:-default}" # 如果未设置或为空则使用默认值 name="${name:=default}" # 如果未设置或为空则赋值默认值 name="${name:+alternative}" # 如果已设置且非空则使用替代值 name="${name:?error message}" # 如果未设置或为空则抛出错误

String manipulation

字符串操作

str="Hello, World!" echo "${str:0:5}" # "Hello" (substring) echo "${str: -6}" # "World!" (last 6 chars) echo "${#str}" # 13 (length) echo "${str/World/Bash}" # "Hello, Bash!" (replace first) echo "${str//o/0}" # "Hell0, W0rld!" (replace all) echo "${str#Hello, }" # "World!" (remove prefix) echo "${str%!}" # "Hello, World" (remove suffix) echo "${str^^}" # "HELLO, WORLD!" (uppercase) echo "${str,,}" # "hello, world!" (lowercase)
str="Hello, World!" echo "${str:0:5}" # "Hello"(子字符串) echo "${str: -6}" # "World!"(最后6个字符) echo "${#str}" # 13(长度) echo "${str/World/Bash}" # "Hello, Bash!"(替换第一个匹配项) echo "${str//o/0}" # "Hell0, W0rld!"(替换所有匹配项) echo "${str#Hello, }" # "World!"(移除前缀) echo "${str%!}" # "Hello, World"(移除后缀) echo "${str^^}" # "HELLO, WORLD!"(转为大写) echo "${str,,}" # "hello, world!"(转为小写)

Parameter expansion

参数展开

filename="/path/to/file.tar.gz" echo "${filename##/}" # "file.tar.gz" (basename) echo "${filename%/}" # "/path/to" (dirname) echo "${filename%%.*}" # "/path/to/file" (remove all extensions) echo "${filename%.gz}" # "/path/to/file.tar" (remove last extension)
undefined
filename="/path/to/file.tar.gz" echo "${filename##/}" # "file.tar.gz"(文件名) echo "${filename%/}" # "/path/to"(目录名) echo "${filename%%.*}" # "/path/to/file"(移除所有扩展名) echo "${filename%.gz}" # "/path/to/file.tar"(移除最后一个扩展名)
undefined

Arrays

数组

bash
undefined
bash
undefined

Indexed arrays

索引数组

declare -a fruits=("apple" "banana" "cherry") fruits+=("date") # Append echo "${fruits[0]}" # "apple" (first element) echo "${fruits[-1]}" # "date" (last element) echo "${fruits[@]}" # All elements echo "${#fruits[@]}" # 4 (length) echo "${!fruits[@]}" # 0 1 2 3 (indices)
declare -a fruits=("apple" "banana" "cherry") fruits+=("date") # 追加元素 echo "${fruits[0]}" # "apple"(第一个元素) echo "${fruits[-1]}" # "date"(最后一个元素) echo "${fruits[@]}" # 所有元素 echo "${#fruits[@]}" # 4(数组长度) echo "${!fruits[@]}" # 0 1 2 3(索引)

Iterate

遍历数组

for fruit in "${fruits[@]}"; do echo "$fruit" done
for fruit in "${fruits[@]}"; do echo "$fruit" done

With indices

使用索引遍历

for i in "${!fruits[@]}"; do echo "$i: ${fruits[i]}" done
for i in "${!fruits[@]}"; do echo "$i: ${fruits[i]}" done

Associative arrays (bash 4+)

关联数组(Bash 4+)

declare -A user=( [name]="John" [email]="john@example.com" [age]=30 )
echo "${user[name]}" # "John" echo "${!user[@]}" # Keys: name email age echo "${user[@]}" # Values
declare -A user=( [name]="John" [email]="john@example.com" [age]=30 )
echo "${user[name]}" # "John" echo "${!user[@]}" # 键:name email age echo "${user[@]}" # 值

Iterate key-value

遍历键值对

for key in "${!user[@]}"; do echo "$key: ${user[$key]}" done
for key in "${!user[@]}"; do echo "$key: ${user[$key]}" done

Array slicing

数组切片

echo "${fruits[@]:1:2}" # "banana" "cherry" (from index 1, 2 elements)
echo "${fruits[@]:1:2}" # "banana" "cherry"(从索引1开始,取2个元素)

Array filtering (bash 4+)

数组过滤(Bash 4+)

evens=() for n in "${numbers[@]}"; do (( n % 2 == 0 )) && evens+=("$n") done

---
evens=() for n in "${numbers[@]}"; do (( n % 2 == 0 )) && evens+=("$n") done

---

Control Flow

控制流

Conditionals

条件判断

bash
undefined
bash
undefined

Test operators

测试运算符

Strings

字符串

[[ -z "$str" ]] # Empty [[ -n "$str" ]] # Not empty [[ "$a" == "$b" ]] # Equal [[ "$a" != "$b" ]] # Not equal [[ "$a" < "$b" ]] # Less than (lexicographic) [[ "$a" =~ ^[0-9]+$ ]] # Regex match
[[ -z "$str" ]] # 为空 [[ -n "$str" ]] # 不为空 [[ "$a" == "$b" ]] # 相等 [[ "$a" != "$b" ]] # 不相等 [[ "$a" < "$b" ]] # 小于(字典序) [[ "$a" =~ ^[0-9]+$ ]] # 正则匹配

Numbers

数字

[[ "$a" -eq "$b" ]] # Equal [[ "$a" -ne "$b" ]] # Not equal [[ "$a" -lt "$b" ]] # Less than [[ "$a" -le "$b" ]] # Less than or equal [[ "$a" -gt "$b" ]] # Greater than [[ "$a" -ge "$b" ]] # Greater than or equal
[[ "$a" -eq "$b" ]] # 相等 [[ "$a" -ne "$b" ]] # 不相等 [[ "$a" -lt "$b" ]] # 小于 [[ "$a" -le "$b" ]] # 小于等于 [[ "$a" -gt "$b" ]] # 大于 [[ "$a" -ge "$b" ]] # 大于等于

Files

文件

[[ -e "$file" ]] # Exists [[ -f "$file" ]] # Regular file [[ -d "$dir" ]] # Directory [[ -r "$file" ]] # Readable [[ -w "$file" ]] # Writable [[ -x "$file" ]] # Executable [[ -s "$file" ]] # Size > 0 [[ "$a" -nt "$b" ]] # Newer than [[ "$a" -ot "$b" ]] # Older than
[[ -e "$file" ]] # 存在 [[ -f "$file" ]] # 普通文件 [[ -d "$dir" ]] # 目录 [[ -r "$file" ]] # 可读 [[ -w "$file" ]] # 可写 [[ -x "$file" ]] # 可执行 [[ -s "$file" ]] # 大小大于0 [[ "$a" -nt "$b" ]] # 比...新 [[ "$a" -ot "$b" ]] # 比...旧

If-elif-else

If-elif-else结构

if [[ "$status" == "success" ]]; then echo "Success!" elif [[ "$status" == "pending" ]]; then echo "Still pending..." else echo "Failed" fi
if [[ "$status" == "success" ]]; then echo "成功!" elif [[ "$status" == "pending" ]]; then echo "仍在处理中..." else echo "失败" fi

Case statement

Case语句

case "$command" in start|begin) start_service ;; stop|end) stop_service ;; restart) stop_service start_service ;; *) echo "Unknown command: $command" exit 1 ;; esac
case "$command" in start|begin) start_service ;; stop|end) stop_service ;; restart) stop_service start_service ;; *) echo "未知命令:$command" exit 1 ;; esac

Short-circuit

短路求值

[[ -f "$file" ]] && process_file "$file" [[ -d "$dir" ]] || mkdir -p "$dir"
undefined
[[ -f "$file" ]] && process_file "$file" [[ -d "$dir" ]] || mkdir -p "$dir"
undefined

Loops

循环

bash
undefined
bash
undefined

For loop

For循环

for item in item1 item2 item3; do echo "$item" done
for item in item1 item2 item3; do echo "$item" done

C-style for

C风格For循环

for ((i = 0; i < 10; i++)); do echo "$i" done
for ((i = 0; i < 10; i++)); do echo "$i" done

While loop

While循环

counter=0 while [[ $counter -lt 5 ]]; do echo "$counter" ((counter++)) done
counter=0 while [[ $counter -lt 5 ]]; do echo "$counter" ((counter++)) done

Read file line by line

逐行读取文件

while IFS= read -r line; do echo "$line" done < "$file"
while IFS= read -r line; do echo "$line" done < "$file"

Process command output

处理命令输出

while IFS= read -r file; do echo "Processing: $file" done < <(find . -name "*.txt")
while IFS= read -r file; do echo "正在处理:$file" done < <(find . -name "*.txt")

Until loop

Until循环

until [[ -f "$lockfile" ]]; do sleep 1 done
until [[ -f "$lockfile" ]]; do sleep 1 done

Break and continue

Break和Continue

for i in {1..10}; do [[ $i -eq 5 ]] && continue [[ $i -eq 8 ]] && break echo "$i" done

---
for i in {1..10}; do [[ $i -eq 5 ]] && continue [[ $i -eq 8 ]] && break echo "$i" done

---

Functions

函数

bash
undefined
bash
undefined

Basic function

基础函数

greet() { local name="$1" echo "Hello, $name!" }
greet() { local name="$1" echo "你好,$name!" }

Function with return value

带返回值的函数

is_valid_email() { local email="$1" [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,}$ ]] }
is_valid_email() { local email="$1" [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,}$ ]] }

Check return value

检查返回值

if is_valid_email "test@example.com"; then echo "Valid email" fi
if is_valid_email "test@example.com"; then echo "有效的邮箱" fi

Function with output capture

捕获输出的函数

get_user_count() { wc -l < /etc/passwd } count=$(get_user_count)
get_user_count() { wc -l < /etc/passwd } count=$(get_user_count)

Function with array parameter

带数组参数的函数

process_files() { local -a files=("$@") for file in "${files[@]}"; do echo "Processing: $file" done } process_files file1.txt file2.txt file3.txt
process_files() { local -a files=("$@") for file in "${files[@]}"; do echo "正在处理:$file" done } process_files file1.txt file2.txt file3.txt

Function with named reference (bash 4.3+)

带命名引用的函数(Bash 4.3+)

modify_array() { local -n arr=$1 arr+=("new_element") }
my_array=("a" "b" "c") modify_array my_array echo "${my_array[@]}" # "a b c new_element"
modify_array() { local -n arr=$1 arr+=("new_element") }
my_array=("a" "b" "c") modify_array my_array echo "${my_array[@]}" # "a b c new_element"

Error handling in functions

函数中的错误处理

safe_divide() { local dividend="$1" local divisor="$2"
if [[ "$divisor" -eq 0 ]]; then
    echo "Error: Division by zero" >&2
    return 1
fi

echo $((dividend / divisor))
}
result=$(safe_divide 10 2) && echo "Result: $result"

---
safe_divide() { local dividend="$1" local divisor="$2"
if [[ "$divisor" -eq 0 ]]; then
    echo "错误:除以零" >&2
    return 1
fi

echo $((dividend / divisor))
}
result=$(safe_divide 10 2) && echo "结果:$result"

---

Text Processing

文本处理

bash
undefined
bash
undefined

grep - search patterns

grep - 搜索模式

grep "error" logfile.txt # Find lines with "error" grep -i "error" logfile.txt # Case-insensitive grep -E "error|warning" logfile.txt # Extended regex grep -v "debug" logfile.txt # Invert match grep -c "error" logfile.txt # Count matches grep -l "error" *.log # List files with matches grep -r "TODO" src/ # Recursive search
grep "error" logfile.txt # 查找包含"error"的行 grep -i "error" logfile.txt # 不区分大小写 grep -E "error|warning" logfile.txt # 使用扩展正则表达式 grep -v "debug" logfile.txt # 反向匹配 grep -c "error" logfile.txt # 统计匹配行数 grep -l "error" *.log # 列出包含匹配项的文件 grep -r "TODO" src/ # 递归搜索

sed - stream editor

sed - 流编辑器

sed 's/old/new/' file.txt # Replace first occurrence sed 's/old/new/g' file.txt # Replace all sed -i.bak 's/old/new/g' file.txt # In-place with backup sed '/pattern/d' file.txt # Delete matching lines sed -n '10,20p' file.txt # Print lines 10-20 sed 's/^/prefix: /' file.txt # Add prefix
sed 's/old/new/' file.txt # 替换第一个匹配项 sed 's/old/new/g' file.txt # 替换所有匹配项 sed -i.bak 's/old/new/g' file.txt # 原地替换并生成备份文件 sed '/pattern/d' file.txt # 删除匹配的行 sed -n '10,20p' file.txt # 打印第10到20行 sed 's/^/prefix: /' file.txt # 添加前缀

awk - field processing

awk - 字段处理

awk '{print $1}' file.txt # First field awk -F: '{print $1}' /etc/passwd # Custom delimiter awk '{sum += $1} END {print sum}' data.txt # Sum first column awk 'NR > 1' file.txt # Skip header awk '$3 > 100 {print $1, $3}' data.txt # Conditional print awk '{print NR": "$0}' file.txt # Add line numbers
awk '{print $1}' file.txt # 第一个字段 awk -F: '{print $1}' /etc/passwd # 使用自定义分隔符 awk '{sum += $1} END {print sum}' data.txt # 求和第一列 awk 'NR > 1' file.txt # 跳过表头 awk '$3 > 100 {print $1, $3}' data.txt # 条件打印 awk '{print NR": "$0}' file.txt # 添加行号

cut - extract fields

cut - 提取字段

cut -d: -f1 /etc/passwd # First field cut -c1-10 file.txt # Characters 1-10 cut -d, -f1,3 data.csv # Fields 1 and 3
cut -d: -f1 /etc/passwd # 第一个字段 cut -c1-10 file.txt # 第1到10个字符 cut -d, -f1,3 data.csv # 第1和第3个字段

sort and uniq

sort和uniq

sort file.txt # Sort lines sort -n numbers.txt # Numeric sort sort -r file.txt # Reverse sort sort -t: -k3 -n /etc/passwd # Sort by field uniq file.txt # Remove adjacent duplicates sort file.txt | uniq -c # Count occurrences sort file.txt | uniq -d # Show duplicates only
sort file.txt # 排序行 sort -n numbers.txt # 数值排序 sort -r file.txt # 反向排序 sort -t: -k3 -n /etc/passwd # 按字段排序 uniq file.txt # 移除相邻重复行 sort file.txt | uniq -c # 统计出现次数 sort file.txt | uniq -d # 仅显示重复行

tr - translate characters

tr - 字符转换

echo "hello" | tr 'a-z' 'A-Z' # Uppercase tr -d '\r' < file.txt # Remove carriage returns tr -s ' ' < file.txt # Squeeze spaces
echo "hello" | tr 'a-z' 'A-Z' # 转为大写 tr -d '\r' < file.txt # 移除回车符 tr -s ' ' < file.txt # 压缩连续空格

xargs - build commands

xargs - 构建命令

find . -name ".txt" | xargs wc -l find . -name ".log" -print0 | xargs -0 rm echo "a b c" | xargs -n1 echo cat urls.txt | xargs -P4 -I{} curl {} # Parallel execution

---
find . -name ".txt" | xargs wc -l find . -name ".log" -print0 | xargs -0 rm echo "a b c" | xargs -n1 echo cat urls.txt | xargs -P4 -I{} curl {} # 并行执行

---

Process Management

进程管理

bash
undefined
bash
undefined

Background processes

后台进程

long_running_command & pid=$! # Get PID of last background job wait $pid # Wait for specific process
long_running_command & pid=$! # 获取最后一个后台作业的PID wait $pid # 等待指定进程完成

Run multiple in background and wait

运行多个后台进程并等待完成

for file in *.txt; do process_file "$file" & done wait # Wait for all
for file in *.txt; do process_file "$file" & done wait # 等待所有进程完成

Parallel processing with xargs

使用xargs进行并行处理

find . -name "*.jpg" -print0 | xargs -0 -P4 -I{} convert {} {}.png
find . -name "*.jpg" -print0 | xargs -0 -P4 -I{} convert {} {}.png

Job control

作业控制

jobs # List jobs fg %1 # Bring job 1 to foreground bg %1 # Resume job 1 in background kill %1 # Kill job 1
jobs # 列出作业 fg %1 # 将作业1带到前台 bg %1 # 将作业1恢复到后台 kill %1 # 终止作业1

Process substitution

进程替换

diff <(sort file1.txt) <(sort file2.txt) while read -r line; do echo "$line" done < <(command_that_outputs)
diff <(sort file1.txt) <(sort file2.txt) while read -r line; do echo "$line" done < <(command_that_outputs)

Command groups

命令组

{ cmd1; cmd2; cmd3; } > output.txt # Group and redirect ( cd /tmp && cmd1; cmd2 ) # Subshell (doesn't affect current shell)
{ cmd1; cmd2; cmd3; } > output.txt # 分组并重定向输出 ( cd /tmp && cmd1; cmd2 ) # 子shell(不影响当前shell)

Coprocesses

协进程

coproc my_coproc { while read -r line; do echo "Got: $line"; done; } echo "Hello" >&"${my_coproc[1]}" read -r response <&"${my_coproc[0]}"

---
coproc my_coproc { while read -r line; do echo "Got: $line"; done; } echo "Hello" >&"${my_coproc[1]}" read -r response <&"${my_coproc[0]}"

---

Related Skills

相关技能

  • [[automation-scripts]] - Build automation
  • [[devops-cicd]] - CI/CD pipelines
  • [[development-environment]] - Environment setup
  • [[automation-scripts]] - 构建自动化
  • [[devops-cicd]] - CI/CD流水线
  • [[development-environment]] - 环境搭建