Linux bash命令

什么是bash?

Bash(Bourne Again Shell)是Linux和Unix系统中最流行的shell,是Bourne shell的增强版。它不仅是命令解释器,还是一种强大的脚本编程语言。

Bash的历史: Bash由Brian Fox在1989年为GNU项目创建,作为Bourne shell的自由软件替代品。现在它是大多数Linux发行版的默认shell。
Bash的主要特性:
  • 命令历史记录和命令补全
  • 变量、数组和算术运算
  • 条件判断和循环控制
  • 函数定义和调用
  • 输入/输出重定向和管道
  • 作业控制和信号处理
  • 正则表达式匹配
  • 进程替换和协程

常见的Shell类型

Bash (Bourne Again Shell)
sh (Bourne Shell)
zsh (Z Shell)
ksh (Korn Shell)
csh (C Shell)
tcsh (TC Shell)
dash (Debian Almquist Shell)
fish (Friendly Interactive Shell)

语法格式

# 执行bash命令 bash [选项] [脚本文件] [参数...] # 执行bash脚本 bash script.sh ./script.sh # 需要有执行权限

常用选项

选项 描述 示例
-c 从字符串执行命令 bash -c "echo Hello"
-x 显示执行的命令(调试模式) bash -x script.sh
-n 检查语法但不执行 bash -n script.sh
-v 显示读取的命令(详细模式) bash -v script.sh
-e 遇到错误立即退出 bash -e script.sh
-u 使用未定义的变量时报错 bash -u script.sh
-o pipefail 管道中任一命令失败则失败 set -o pipefail
--version 显示bash版本 bash --version

第一个Bash脚本

创建第一个Bash脚本:

#!/bin/bash # 这是注释 echo "Hello, World!" echo "当前目录: $(pwd)" echo "用户: $USER" echo "今天是: $(date)"
# 1. 创建脚本文件 nano hello.sh # 2. 添加执行权限 chmod +x hello.sh # 3. 运行脚本 ./hello.sh # 或 bash hello.sh

变量

1. 变量定义和使用:

# 定义变量(等号两边不能有空格) name="Alice" age=25 path="/home/user" # 使用变量 echo $name echo ${name} # 推荐使用{}明确变量边界 echo "My name is $name" # 重新赋值 name="Bob" echo "New name: $name"

2. 特殊变量:

#!/bin/bash echo "脚本名: $0" echo "参数个数: $#" echo "所有参数: $@" echo "第一个参数: $1" echo "第二个参数: $2" echo "进程ID: $$" echo "上一个命令的退出状态: $?" # 示例调用: ./script.sh arg1 arg2

3. 环境变量:

# 查看所有环境变量 printenv env # 常见环境变量 echo "PATH: $PATH" echo "HOME: $HOME" echo "USER: $USER" echo "SHELL: $SHELL" echo "PWD: $PWD" # 设置环境变量 export MY_VAR="value" export PATH="$PATH:/custom/path"

4. 只读变量:

# 定义只读变量 readonly PI=3.14159 readonly SCRIPT_NAME="myapp" # 尝试修改会报错 PI=3.14 # bash: PI: readonly variable

字符串操作

1. 字符串基本操作:

str="Hello World" # 获取长度 echo ${#str} # 11 # 提取子字符串 echo ${str:0:5} # Hello echo ${str:6} # World # 大小写转换 echo ${str^^} # HELLO WORLD (大写) echo ${str,,} # hello world (小写)

2. 字符串替换和删除:

str="hello world hello" # 替换第一个匹配 echo ${str/hello/HI} # HI world hello # 替换所有匹配 echo ${str//hello/HI} # HI world HI # 从开头删除匹配 path="/usr/local/bin" echo ${path#/usr} # /local/bin # 从结尾删除匹配 file="archive.tar.gz" echo ${file%.gz} # archive.tar echo ${file%%.tar.gz} # archive

数组

1. 索引数组:

# 定义数组 fruits=("apple" "banana" "orange") # 或者 fruits[0]="apple" fruits[1]="banana" fruits[2]="orange" # 访问元素 echo ${fruits[0]} # apple echo ${fruits[1]} # banana # 所有元素 echo ${fruits[@]} # apple banana orange echo ${fruits[*]} # 数组长度 echo ${#fruits[@]} # 3 echo ${#fruits[0]} # 5 (第一个元素的长度)

2. 关联数组(Bash 4+):

# 声明关联数组 declare -A person person[name]="Alice" person[age]=25 person[city]="New York" # 访问 echo ${person[name]} # Alice echo ${person[age]} # 25 # 所有键 echo ${!person[@]} # name age city # 所有值 echo ${person[@]} # Alice 25 New York

3. 数组操作:

colors=("red" "green" "blue") # 添加元素 colors+=("yellow") echo ${colors[@]} # red green blue yellow # 删除元素 unset colors[1] echo ${colors[@]} # red blue yellow # 切片 numbers=(1 2 3 4 5 6 7 8 9 10) echo ${numbers[@]:2:5} # 3 4 5 6 7

算术运算

1. 基本算术运算:

# 使用 $(()) a=10 b=3 echo $((a + b)) # 13 echo $((a - b)) # 7 echo $((a * b)) # 30 echo $((a / b)) # 3 (整数除法) echo $((a % b)) # 1 echo $((a ** b)) # 1000 (幂运算) echo $((a++)) # 10 (后自增) echo $((++a)) # 12 (前自增) # 使用 $[] echo $[a + b] # 15 echo $[a * b] # 36

2. 浮点运算(需要bc命令):

# 使用bc进行浮点运算 echo "scale=2; 10 / 3" | bc # 3.33 echo "3.14 + 2.72" | bc # 5.86 echo "sqrt(16)" | bc # 4 echo "2 ^ 8" | bc # 256 # 变量运算 a=10.5 b=3.2 echo "$a + $b" | bc # 13.7

3. 位运算:

a=5 # 二进制 0101 b=3 # 二进制 0011 echo $((a & b)) # 1 (按位与: 0001) echo $((a | b)) # 7 (按位或: 0111) echo $((a ^ b)) # 6 (按位异或: 0110) echo $((~a)) # -6 (按位取反) echo $((a << 1)) # 10 (左移: 1010) echo $((a >> 1)) # 2 (右移: 0010)

条件判断

1. if语句:

#!/bin/bash # 基本if语句 if [ condition ]; then commands fi # if-else if [ condition ]; then commands1 else commands2 fi # if-elif-else if [ condition1 ]; then commands1 elif [ condition2 ]; then commands2 else commands3 fi

2. 测试条件:

# 字符串比较 [ "$a" = "$b" ] # 等于 [ "$a" != "$b" ] # 不等于 [ -z "$a" ] # 为空 [ -n "$a" ] # 不为空 [ "$a" < "$b" ] # 小于(按字典序) [ "$a" > "$b" ] # 大于 # 数值比较 [ $a -eq $b ] # 等于 [ $a -ne $b ] # 不等于 [ $a -lt $b ] # 小于 [ $a -le $b ] # 小于等于 [ $a -gt $b ] # 大于 [ $a -ge $b ] # 大于等于 # 文件测试 [ -e file ] # 存在 [ -f file ] # 是普通文件 [ -d file ] # 是目录 [ -r file ] # 可读 [ -w file ] # 可写 [ -x file ] # 可执行 [ -s file ] # 非空 [ -L file ] # 是符号链接

3. 逻辑操作:

# 与 [ condition1 ] && [ condition2 ] [ condition1 -a condition2 ] # 或 [ condition1 ] || [ condition2 ] [ condition1 -o condition2 ] # 非 [ ! condition ] # 组合示例 if [ -f "$file" ] && [ -r "$file" ]; then echo "文件存在且可读" fi if [ $age -ge 18 ] || [ $parent_consent = "yes" ]; then echo "允许访问" fi

4. case语句:

#!/bin/bash read -p "输入颜色: " color case $color in red) echo "你选择了红色" ;; green|blue) echo "你选择了绿色或蓝色" ;; yellow) echo "你选择了黄色" ;; *) echo "未知颜色" ;; esac

循环

1. for循环:

# 遍历列表 for fruit in apple banana orange; do echo "水果: $fruit" done # 遍历数组 colors=("red" "green" "blue") for color in "${colors[@]}"; do echo "颜色: $color" done # 数字范围 for i in {1..5}; do echo "数字: $i" done # 使用seq for i in $(seq 1 2 10); do echo "i: $i" # 1 3 5 7 9 done # C风格for循环 for ((i=0; i<5; i++)); do echo "计数: $i" done

2. while循环:

# 基本while循环 count=1 while [ $count -le 5 ]; do echo "计数: $count" count=$((count + 1)) done # 无限循环 while true; do echo "按Ctrl+C退出" sleep 1 done # 读取文件 while IFS= read -r line; do echo "行: $line" done < file.txt # 读取命令输出 while read -r user terminal; do echo "用户 $user 在 $terminal 登录" done < <(who)

3. until循环:

# until循环(与while条件相反) count=1 until [ $count -gt 5 ]; do echo "计数: $count" count=$((count + 1)) done # 等待条件 until ping -c1 google.com &>/dev/null; do echo "等待网络连接..." sleep 5 done echo "网络已连接"

4. 循环控制:

# break - 跳出循环 for i in {1..10}; do if [ $i -eq 5 ]; then break fi echo "i: $i" done # continue - 跳过本次循环 for i in {1..5}; do if [ $i -eq 3 ]; then continue fi echo "i: $i" done # break N - 跳出N层循环 for i in {1..3}; do for j in {1..3}; do if [ $j -eq 2 ]; then break 2 fi echo "i=$i, j=$j" done done

函数

1. 函数定义和调用:

# 函数定义 say_hello() { echo "Hello, $1!" return 0 } # 调用函数 say_hello "Alice" # 获取返回值 say_hello "Bob" echo "返回值: $?" # 0 # 带返回值的函数 add() { local sum=$(( $1 + $2 )) echo $sum } result=$(add 10 20) echo "结果: $result" # 30

2. 局部变量:

#!/bin/bash global_var="我是全局变量" myfunc() { local local_var="我是局部变量" echo "函数内: $local_var" echo "函数内: $global_var" } myfunc echo "函数外: $local_var" # 空 echo "函数外: $global_var" # 我是全局变量

3. 函数参数:

#!/bin/bash # 函数参数 show_args() { echo "参数个数: $#" echo "第一个参数: $1" echo "第二个参数: $2" echo "所有参数: $@" # 遍历所有参数 for arg in "$@"; do echo "参数: $arg" done } show_args "Alice" "Bob" "Charlie"

输入输出

1. 读取输入:

# 读取单行输入 read -p "请输入姓名: " name echo "你好, $name!" # 读取密码(不显示) read -sp "请输入密码: " password echo echo "密码已接收" # 读取数组 echo "输入多个值(空格分隔):" read -a array echo "数组: ${array[@]}" # 读取到超时 if read -t 5 -p "5秒内输入: " input; then echo "你输入了: $input" else echo "超时!" fi

2. 重定向:

# 输出重定向 echo "内容" > file.txt # 覆盖 echo "更多内容" >> file.txt # 追加 # 输入重定向 wc -l < file.txt # 错误重定向 command 2> error.log command 2>> error.log command 2>/dev/null # 丢弃错误 # 全部重定向 command &> output.log command &>> output.log command > output.log 2>&1 # Here Document cat << EOF 这是一个Here文档 可以包含多行文本 EOF # Here String grep "pattern" <<< "这是一个字符串"

3. 管道:

# 基本管道 ls -l | grep "\.txt$" | wc -l # 进程替换 diff <(ls dir1) <(ls dir2) # tee命令 ls -l | tee files.txt | wc -l ls -l | tee -a files.txt # 追加

脚本调试

1. 调试选项:

#!/bin/bash # 在脚本中设置调试选项 set -e # 遇到错误退出 set -u # 使用未定义变量时报错 set -x # 显示执行的命令 set -o pipefail # 管道失败时退出 # 或者运行脚本时指定 bash -xe script.sh # 临时启用调试 set -x # 调试代码 set +x

2. 调试技巧:

#!/bin/bash # 1. 输出变量值 echo "DEBUG: 变量值: $var" # 2. 使用trap调试 trap 'echo "在行: $LINENO, 变量: $var"' DEBUG # 3. 函数调用跟踪 trap 'echo "调用函数: ${FUNCNAME[0]}"' DEBUG # 4. 记录执行时间 start_time=$(date +%s) # 执行代码 end_time=$(date +%s) echo "执行时间: $((end_time - start_time))秒"

实用脚本示例

1. 备份脚本:

#!/bin/bash # 备份脚本 set -e # 配置 BACKUP_DIR="/backup" SOURCE_DIR="/var/www" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/backup_$DATE.tar.gz" # 检查目录 [ -d "$BACKUP_DIR" ] || mkdir -p "$BACKUP_DIR" [ -d "$SOURCE_DIR" ] || { echo "源目录不存在"; exit 1; } # 创建备份 echo "开始备份..." tar -czf "$BACKUP_FILE" "$SOURCE_DIR" # 检查备份 if [ $? -eq 0 ] && [ -f "$BACKUP_FILE" ]; then echo "备份成功: $BACKUP_FILE" echo "文件大小: $(du -h "$BACKUP_FILE" | cut -f1)" else echo "备份失败" exit 1 fi # 删除7天前的备份 find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete echo "清理7天前的备份完成"

2. 系统监控脚本:

#!/bin/bash # 系统监控脚本 set -e LOG_FILE="/var/log/system_monitor.log" THRESHOLD_CPU=80 THRESHOLD_MEM=80 THRESHOLD_DISK=85 log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" } # 检查CPU使用率 check_cpu() { local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1) if (( $(echo "$cpu_usage > $THRESHOLD_CPU" | bc -l) )); then log_message "警告: CPU使用率过高: ${cpu_usage}%" return 1 fi log_message "CPU使用率正常: ${cpu_usage}%" return 0 } # 检查内存使用率 check_memory() { local mem_total=$(free | grep Mem | awk '{print $2}') local mem_used=$(free | grep Mem | awk '{print $3}') local mem_usage=$((mem_used * 100 / mem_total)) if [ $mem_usage -gt $THRESHOLD_MEM ]; then log_message "警告: 内存使用率过高: ${mem_usage}%" return 1 fi log_message "内存使用率正常: ${mem_usage}%" return 0 } # 检查磁盘使用率 check_disk() { df -h | grep -E "^/dev/" | while read line; do local usage=$(echo $line | awk '{print $5}' | sed 's/%//') local mount=$(echo $line | awk '{print $6}') if [ $usage -gt $THRESHOLD_DISK ]; then log_message "警告: 磁盘 $mount 使用率过高: ${usage}%" else log_message "磁盘 $mount 使用率正常: ${usage}%" fi done } # 主函数 main() { log_message "开始系统监控检查" check_cpu check_memory check_disk log_message "系统监控检查完成" } main "$@"

3. 服务管理脚本:

#!/bin/bash # 服务管理脚本 set -e SERVICE_NAME="nginx" CONFIG_FILE="/etc/nginx/nginx.conf" PID_FILE="/var/run/nginx.pid" # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # 日志函数 log() { echo -e "$(date '+%Y-%m-%d %H:%M:%S') - $1" } # 检查服务状态 check_status() { if systemctl is-active --quiet "$SERVICE_NAME"; then log "${GREEN}服务 $SERVICE_NAME 正在运行${NC}" return 0 else log "${RED}服务 $SERVICE_NAME 已停止${NC}" return 1 fi } # 启动服务 start_service() { log "正在启动 $SERVICE_NAME..." if systemctl start "$SERVICE_NAME"; then log "${GREEN}服务启动成功${NC}" return 0 else log "${RED}服务启动失败${NC}" return 1 fi } # 停止服务 stop_service() { log "正在停止 $SERVICE_NAME..." if systemctl stop "$SERVICE_NAME"; then log "${GREEN}服务停止成功${NC}" return 0 else log "${RED}服务停止失败${NC}" return 1 fi } # 重启服务 restart_service() { log "正在重启 $SERVICE_NAME..." if systemctl restart "$SERVICE_NAME"; then log "${GREEN}服务重启成功${NC}" return 0 else log "${RED}服务重启失败${NC}" return 1 fi } # 重新加载配置 reload_service() { log "正在重新加载配置..." if systemctl reload "$SERVICE_NAME"; then log "${GREEN}配置重新加载成功${NC}" return 0 else log "${RED}配置重新加载失败${NC}" return 1 fi } # 显示使用帮助 show_help() { cat << EOF 用法: $0 {start|stop|restart|reload|status|check-config} 选项: start 启动服务 stop 停止服务 restart 重启服务 reload 重新加载配置 status 检查服务状态 check-config 检查配置文件语法 EOF } # 检查配置文件语法 check_config() { if nginx -t -c "$CONFIG_FILE"; then log "${GREEN}配置文件语法正确${NC}" return 0 else log "${RED}配置文件语法错误${NC}" return 1 fi } # 主函数 main() { case "$1" in start) start_service ;; stop) stop_service ;; restart) restart_service ;; reload) reload_service ;; status) check_status ;; check-config) check_config ;; *) show_help exit 1 ;; esac } main "$@"

Bash脚本最佳实践

1. 脚本开头:

#!/bin/bash # 脚本说明 # 作者: 你的名字 # 版本: 1.0 # 描述: 这个脚本的功能描述 set -euo pipefail # 安全选项 IFS=$'\n\t' # 设置内部字段分隔符 # 常量定义 readonly SCRIPT_NAME=$(basename "$0") readonly SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) readonly VERSION="1.0.0" readonly LOG_FILE="/var/log/${SCRIPT_NAME%.*}.log" # 颜色定义 readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly BLUE='\033[0;34m' readonly NC='\033[0m' # No Color

2. 错误处理:

#!/bin/bash # 错误处理函数 error_exit() { echo "$1" >&2 exit 1 } # 检查命令是否存在 check_command() { if ! command -v "$1" &> /dev/null; then error_exit "命令 $1 未找到,请先安装" fi } # 检查文件是否存在 check_file() { if [ ! -f "$1" ]; then error_exit "文件 $1 不存在" fi } # 检查目录是否存在 check_directory() { if [ ! -d "$1" ]; then error_exit "目录 $1 不存在" fi } # 使用示例 check_command curl check_file "/etc/passwd" check_directory "/var/log"

3. 日志函数:

#!/bin/bash # 日志函数 log() { local level="$1" local message="$2" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') case "$level" in INFO) echo -e "${GREEN}[INFO]${NC} $message" ;; WARN) echo -e "${YELLOW}[WARN]${NC} $message" ;; ERROR) echo -e "${RED}[ERROR]${NC} $message" ;; DEBUG) [ "${DEBUG:-false}" = "true" ] && echo -e "${BLUE}[DEBUG]${NC} $message" ;; esac # 写入日志文件 echo "[$timestamp] [$level] $message" >> "$LOG_FILE" } # 使用示例 log "INFO" "脚本开始执行" log "WARN" "磁盘空间不足" log "ERROR" "文件不存在"
重要安全提示:
1. 永远不要以root身份运行不受信任的脚本
2. 验证所有用户输入
3. 使用引号引用变量,避免单词分割和通配符扩展
4. 使用[[ ]]进行条件测试,比[ ]更安全
5. 在处理文件名时,使用--选项分隔选项和参数
6. 不要使用eval执行用户输入
7. 为脚本设置适当的权限(通常为755)

相关资源

资源 描述 链接
Bash参考手册 GNU Bash官方文档 gnu.org
Bash Pitfalls 常见Bash编程错误 mywiki.wooledge.org
ShellCheck Shell脚本静态分析工具 shellcheck.net
Advanced Bash-Scripting Guide 高级Bash脚本编程指南 tldp.org
Bash Hackers Wiki Bash编程维基 bash-hackers.org