Linux Shell脚本编程
Linux Shell脚本编程
什么是Shell脚本?
Shell脚本是一种包含一系列Shell命令的文本文件,它可以自动执行这些命令,实现自动化任务。Shell脚本是Linux系统中自动化管理和任务处理的重要工具。
常用的Shell
- Bash (Bourne Again Shell):最常用的Shell,是大多数Linux发行版的默认Shell
- sh (Bourne Shell):最早的Shell,是其他Shell的基础
- csh (C Shell):语法类似C语言的Shell
- tcsh:csh的增强版
- zsh:功能强大的Shell,兼容Bash
- ksh (Korn Shell):兼容Bash和csh的Shell
Shell脚本的基本结构
脚本的第一行
Shell脚本的第一行通常是shebang(#!),指定执行脚本的Shell。
#!/bin/bash # 使用Bash执行脚本#!/bin/sh # 使用sh执行脚本#!/usr/bin/env bash # 更灵活的方式,使用环境中的bash注释
在Shell脚本中,使用#开始的行是注释,不会被执行。
#!/bin/bash
# 这是一个注释# 下面的命令会创建一个目录mkdir -p test脚本的执行权限
需要为Shell脚本添加执行权限才能执行。
# 添加执行权限chmod +x script.sh
# 执行脚本./script.sh变量
变量的定义和使用
在Shell脚本中,变量的定义不需要类型声明,直接赋值即可。
#!/bin/bash
# 定义变量name="World"age=18
# 使用变量echo "Hello, $name!"echo "Your age is $age."
# 变量名的大括号包围(推荐)echo "Hello, ${name}!"echo "Your age is ${age}."
# 修改变量值age=20echo "Your new age is $age."环境变量
环境变量是系统预定义的变量,通常使用大写字母表示。
#!/bin/bash
# 显示环境变量echo "Home directory: $HOME"echo "Current user: $USER"echo "Path: $PATH"echo "Shell: $SHELL"echo "Current directory: $PWD"
# 设置环境变量export MY_VAR="Hello"echo "My variable: $MY_VAR"特殊变量
Shell中有一些特殊变量,用于获取命令行参数、退出状态等。
#!/bin/bash
# 显示脚本名称echo "Script name: $0"
# 显示命令行参数echo "Number of arguments: $#"echo "All arguments: $@"echo "All arguments (as single string): $*"echo "First argument: $1"echo "Second argument: $2"
# 显示最后一个命令的退出状态echo "Exit status: $?"
# 显示当前进程IDecho "Process ID: $$"
# 显示后台最后一个进程的IDecho "Last background process ID: $!"变量的读取
使用read命令从标准输入读取变量值。
#!/bin/bash
# 读取变量read -p "Enter your name: " nameecho "Hello, $name!"
# 读取多个变量read -p "Enter your name and age: " name ageecho "Hello, $name! Your age is $age."
# 隐藏输入(用于密码)read -s -p "Enter your password: " passwordecho "\nPassword entered."运算符
算术运算符
Shell中的算术运算需要使用$((...))或expr命令。
#!/bin/bash
# 使用$((...))进行算术运算x=5y=3echo "x + y = $((x + y))"echo "x - y = $((x - y))"echo "x * y = $((x * y))"echo "x / y = $((x / y))"echo "x % y = $((x % y))"echo "x ** y = $((x ** y))" # 幂运算
# 自增和自减x=5echo "x = $x"echo "x++ = $((x++))"echo "x = $x"echo "++x = $((++x))"echo "x = $x"
# 使用let命令let x=10echo "x = $x"let x=x+5echo "x = $x"比较运算符
比较运算符用于比较数值或字符串。
#!/bin/bash
# 数值比较x=5y=10
if [ $x -eq $y ]; then echo "x equals y"elif [ $x -ne $y ]; then echo "x not equals y"elif [ $x -lt $y ]; then echo "x less than y"elif [ $x -le $y ]; then echo "x less than or equal to y"elif [ $x -gt $y ]; then echo "x greater than y"elif [ $x -ge $y ]; then echo "x greater than or equal to y"fi
# 字符串比较str1="hello"str2="world"
if [ "$str1" = "$str2" ]; then echo "str1 equals str2"elif [ "$str1" != "$str2" ]; then echo "str1 not equals str2"elif [ -z "$str1" ]; then echo "str1 is empty"elif [ -n "$str1" ]; then echo "str1 is not empty"elif [ "$str1" "$str2" ]; then echo "str1 less than str2"elif [ "$str1" "$str2" ]; then echo "str1 greater than str2"fi逻辑运算符
逻辑运算符用于组合多个条件。
#!/bin/bash
# 逻辑与 (&&)x=5if [ $x -gt 0 ] && [ $x -lt 10 ]; then echo "x is between 0 and 10"fi
# 逻辑或 (||)x=15if [ $x -lt 0 ] || [ $x -gt 10 ]; then echo "x is less than 0 or greater than 10"fi
# 逻辑非 (!)x=5if ! [ $x -gt 10 ]; then echo "x is not greater than 10"fi
# 使用双括号的逻辑运算x=5if (( $x > 0 && $x < 10 )); then echo "x is between 0 and 10"fi文件测试运算符
文件测试运算符用于测试文件的属性。
#!/bin/bash
file="test.txt"
# 测试文件是否存在if [ -e "$file" ]; then echo "File exists"else echo "File does not exist"fi
# 测试是否为普通文件if [ -f "$file" ]; then echo "Is a regular file"fi
# 测试是否为目录if [ -d "$file" ]; then echo "Is a directory"fi
# 测试是否有读取权限if [ -r "$file" ]; then echo "Has read permission"fi
# 测试是否有写入权限if [ -w "$file" ]; then echo "Has write permission"fi
# 测试是否有执行权限if [ -x "$file" ]; then echo "Has execute permission"fi
# 测试文件是否为空if [ -s "$file" ]; then echo "File is not empty"else echo "File is empty"fi控制流
if语句
if语句用于条件判断。
#!/bin/bash
# 基本if语句x=5if [ $x -gt 0 ]; then echo "x is positive"fi
# if-else语句x=-5if [ $x -gt 0 ]; then echo "x is positive"else echo "x is not positive"fi
# if-elif-else语句x=0if [ $x -gt 0 ]; then echo "x is positive"elif [ $x -lt 0 ]; then echo "x is negative"else echo "x is zero"fi
# 使用双括号的if语句x=5if (( $x > 0 )); then echo "x is positive"fi
# 使用中括号的if语句(更灵活)x=5if [[ $x > 0 && $x < 10 ]]; then echo "x is between 0 and 10"ficase语句
case语句用于多分支条件判断,类似于其他语言的switch语句。
#!/bin/bash
# 基本case语句read -p "Enter a number (1-3): " num
case $num in 1) echo "You entered one." ;; 2) echo "You entered two." ;; 3) echo "You entered three." ;; *) echo "Invalid input." ;;esac
# 使用模式匹配read -p "Enter a string: " str
case $str in [a-z]*) echo "Starts with lowercase letter" ;; [A-Z]*) echo "Starts with uppercase letter" ;; [0-9]*) echo "Starts with number" ;; *) echo "Starts with other character" ;;esacfor循环
for循环用于遍历列表中的元素。
#!/bin/bash
# 遍历数字列表for i in 1 2 3 4 5; do echo "Number: $i"done
# 遍历字符串列表for fruit in apple banana cherry date; do echo "Fruit: $fruit"done
# 使用seq生成数字序列for i in $(seq 1 5); do echo "Number: $i"done
# 使用{start..end}生成序列(Bash 4+)for i in {1..5}; do echo "Number: $i"done
# 使用C风格的for循环for ((i=1; i<=5; i++)); do echo "Number: $i"done
# 遍历目录中的文件for file in *.txt; do echo "File: $file"done
# 遍历命令行参数for arg in "$@"; do echo "Argument: $arg"donewhile循环
while循环用于在条件为真时重复执行命令。
#!/bin/bash
# 基本while循环count=1while [ $count -le 5 ]; do echo "Count: $count" count=$((count + 1))done
# 使用C风格的while循环count=1while (( $count <= 5 )); do echo "Count: $count" count=$((count + 1))done
# 无限循环(使用Ctrl+C退出)# while true; do# echo "Hello"# sleep 1# done
# 从文件读取行while read line; do echo "Line: $line"done < file.txt
# 读取标准输入# echo "Enter lines (Ctrl+D to end):"# while read line; do# echo "You entered: $line"# doneuntil循环
until循环用于在条件为假时重复执行命令,与while循环相反。
#!/bin/bash
# 基本until循环count=1until [ $count -gt 5 ]; do echo "Count: $count" count=$((count + 1))done
# 使用C风格的until循环count=1until (( $count > 5 )); do echo "Count: $count" count=$((count + 1))donebreak和continue
- break:跳出当前循环
- continue:跳过当前循环的剩余部分,开始下一次循环
#!/bin/bash
# 使用breakfor i in 1 2 3 4 5; do if [ $i -eq 3 ]; then break fi echo "Number: $i"done
# 使用continuefor i in 1 2 3 4 5; do if [ $i -eq 3 ]; then continue fi echo "Number: $i"done函数
函数的定义和调用
#!/bin/bash
# 定义函数greet() { echo "Hello, $1!"}
# 调用函数greet "World"greet "Alice"
# 带返回值的函数add() { local sum=$(( $1 + $2 )) echo $sum}
# 使用函数返回值result=$(add 5 3)echo "Sum: $result"
# 函数中的局部变量count=10
increment() { local count=0 # 局部变量,不会影响外部变量 count=$((count + 1)) echo "Local count: $count"}
incrementecho "Global count: $count"
# 函数库# 可以将常用函数放在一个文件中,然后在其他脚本中source# source functions.sh函数的参数
函数可以接收参数,使用$1、$2等获取。
#!/bin/bash
# 函数的参数print_args() { echo "Number of arguments: $#" echo "First argument: $1" echo "Second argument: $2" echo "All arguments: $@"}
# 调用函数print_args "Hello" "World"
# 函数中的特殊变量print_special() { echo "Function name: $0" echo "Last command exit status: $?" echo "Process ID: $$"}
print_special输入和输出
标准输入、输出和错误
- 标准输入 (stdin):文件描述符为0,默认是键盘
- 标准输出 (stdout):文件描述符为1,默认是屏幕
- 标准错误 (stderr):文件描述符为2,默认是屏幕
重定向
#!/bin/bash
# 重定向标准输出到文件 echo "Hello, World!" > output.txt
# 重定向标准输出并追加到文件 echo "Hello again!" >> output.txt
# 重定向标准错误到文件error_command 2> error.txt
# 重定向标准输出和错误到文件command &> output.txt
# 重定向标准输出到标准错误echo "Error message" >&2
# 从文件读取输入while read line; do echo "Line: $line"done < input.txt
# 管道:将一个命令的输出作为另一个命令的输入echo "Hello, World!" | grep "World"
# tee命令:同时输出到标准输出和文件echo "Hello, World!" | tee output.txt数组
数组的定义和使用
#!/bin/bash
# 定义数组fruits=('apple' 'banana' 'cherry' 'date')
# 访问数组元素echo "First fruit: ${fruits[0]}"echo "Second fruit: ${fruits[1]}"echo "Third fruit: ${fruits[2]}"echo "Fourth fruit: ${fruits[3]}"
# 访问所有元素echo "All fruits: ${fruits[@]}"echo "All fruits: ${fruits[*]}"
# 数组长度echo "Number of fruits: ${#fruits[@]}"
# 修改数组元素fruits[2]='grape'echo "Third fruit: ${fruits[2]}"
# 添加数组元素fruits+=('elderberry')echo "Fifth fruit: ${fruits[4]}"echo "All fruits: ${fruits[@]}"
# 删除数组元素unset fruits[1]echo "All fruits after deletion: ${fruits[@]}"
# 遍历数组for fruit in "${fruits[@]}"; do echo "Fruit: $fruit"done
# 遍历数组(带索引)for i in "${!fruits[@]}"; do echo "Fruit $i: ${fruits[$i]}"done关联数组
关联数组(也称为哈希表)允许使用字符串作为索引,Bash 4.0+支持。
#!/bin/bash
# 声明关联数组declare -A colors
# 添加键值对colors["red"]="#ff0000"colors["green"]="#00ff00"colors["blue"]="#0000ff"
# 访问关联数组元素echo "Red: ${colors[red]}"echo "Green: ${colors[green]}"echo "Blue: ${colors[blue]}"
# 遍历关联数组的键for color in "${!colors[@]}"; do echo "Color: $color"done
# 遍历关联数组的键值对for color in "${!colors[@]}"; do echo "$color: ${colors[$color]}"done
# 关联数组的大小echo "Number of colors: ${#colors[@]}"
# 删除关联数组元素unset colors[red]echo "Colors after deletion: ${!colors[@]}"
# 清空关联数组unset colorsecho "Number of colors after unset: ${#colors[@]}"算术运算
基本算术运算
#!/bin/bash
# 使用$((...))x=5y=3
# 加法sum=$((x + y))echo "Sum: $sum"
# 减法diff=$((x - y))echo "Difference: $diff"
# 乘法product=$((x * y))echo "Product: $product"
# 除法quotient=$((x / y))echo "Quotient: $quotient"
# 取模remainder=$((x % y))echo "Remainder: $remainder"
# 幂运算power=$((x ** y))echo "Power: $power"
# 自增和自减x=5echo "Original x: $x"x=$((x + 1))echo "x after increment: $x"x=$((x - 1))echo "x after decrement: $x"
# 使用let命令x=5let x=x+1echo "x after let increment: $x"
# 使用expr命令(较旧的方式)x=5y=3sum=$(expr $x + $y)echo "Sum using expr: $sum"浮点运算
Shell本身不支持浮点运算,需要使用外部工具如bc。
#!/bin/bash
# 使用bc进行浮点运算x=5y=3
# 除法division=$(echo "scale=2; $x / $y" | bc)echo "Division: $division"
# 其他运算square_root=$(echo "scale=2; sqrt($x)" | bc)echo "Square root of $x: $square_root"
exponential=$(echo "scale=2; e($x)" | bc -l)echo "Exponential of $x: $exponential"
sine=$(echo "scale=2; s($x)" | bc -l)echo "Sine of $x: $sine"正则表达式
grep命令的正则表达式
#!/bin/bash
# 基本正则表达式# 匹配包含"test"的行echo "Testing 123" | grep "test"
# 匹配以"test"开头的行echo "testing 123" | grep "^test"
# 匹配以"123"结尾的行echo "testing 123" | grep "123$"
# 匹配包含数字的行echo "testing 123" | grep "[0-9]"
# 匹配包含字母的行echo "testing 123" | grep "[a-zA-Z]"
# 匹配包含"test"或"exam"的行echo "testing" | grep -E "test|exam"
# 匹配包含3个数字的行echo "testing 123" | grep "[0-9]{3}"
# 匹配单词边界echo "testing" | grep "\btest\b"sed命令的正则表达式
#!/bin/bash
# 替换文本echo "Hello, World!" | sed 's/World/Shell/'
# 替换所有匹配echo "Hello, World! World!" | sed 's/World/Shell/g'
# 使用正则表达式替换echo "Testing 123" | sed 's/[0-9]/X/g'
# 替换行首echo "Testing 123" | sed 's/^/Prefix: /'
# 替换行尾echo "Testing 123" | sed 's/$/ :Suffix/'awk命令的正则表达式
#!/bin/bash
# 匹配包含"test"的行echo "Testing 123" | awk '/test/ {print $0}'
# 匹配以"test"开头的行echo "testing 123" | awk '/^test/ {print $0}'
# 匹配以"123"结尾的行echo "testing 123" | awk '/123$/ {print $0}'
# 匹配包含数字的行echo "testing 123" | awk '/[0-9]/ {print $0}'脚本的调试
调试选项
Bash提供了几个调试选项,可以帮助找出脚本中的问题。
#!/bin/bash
# 在脚本开头添加调试选项
# -x:显示执行的命令和参数# #!/bin/bash -x
# -v:显示读取的脚本行# #!/bin/bash -v
# -n:检查语法错误,不执行脚本# #!/bin/bash -n
# 在脚本中启用/禁用调试set -x # 启用调试echo "This line will be debugged"set +x # 禁用调试echo "This line will not be debugged"
# 其他有用的调试选项set -e # 遇到错误时退出set -u # 遇到未定义变量时退出set -o pipefail # 管道中任何命令失败时退出常见错误
#!/bin/bash
# 常见错误示例
# 1. 变量名和等号之间不能有空格# name = "World" # 错误name="World" # 正确
# 2. 变量使用时需要$符号# echo "Hello, name!" # 错误echo "Hello, $name!" # 正确
# 3. 命令替换使用反引号或$()# current_dir=ls -la # 错误current_dir=$(ls -la) # 正确
# 4. 条件判断中的空格# if [$name == "World"]; then # 错误if [ "$name" == "World" ]; then # 正确 echo "Hello, World!"fi
# 5. 字符串比较使用=或==,数值比较使用-eq等# if [ $name == "World" ]; then # 正确(字符串比较)# if [ $age == 18 ]; then # 错误(数值比较)# if [ $age -eq 18 ]; then # 正确(数值比较)
# 6. 数组元素的访问# fruits=('apple' 'banana')# echo "First fruit: $fruits[0]" # 错误# echo "First fruit: ${fruits[0]}" # 正确
# 7. 函数调用不需要括号# greet() {# echo "Hello!"# }# greet() # 错误# greet # 正确实用脚本示例
备份脚本
#!/bin/bash
# 备份脚本
# 备份目录BACKUP_DIR="/backup"
# 源目录SOURCE_DIRS=("/home" "/etc" "/var/www")
# 日期格式DATE=$(date +"%Y-%m-%d_%H-%M-%S")
# 创建备份目录mkdir -p "$BACKUP_DIR"
# 备份每个目录for dir in "${SOURCE_DIRS[@]}"; do # 目录名 dir_name=$(basename "$dir")
# 备份文件名 backup_file="$BACKUP_DIR/${dir_name}_${DATE}.tar.gz"
# 执行备份 echo "Backing up $dir to $backup_file..." tar -czf "$backup_file" "$dir"
# 检查备份是否成功 if [ $? -eq 0 ]; then echo "Backup of $dir completed successfully!" else echo "Backup of $dir failed!" fidone
# 清理旧备份(保留最近7天的备份)echo "Cleaning up old backups..."find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -deleteecho "Old backups cleaned up!"
echo "Backup script completed!"监控脚本
#!/bin/bash
# 监控脚本
# 监控间隔(秒)INTERVAL=5
# 监控次数COUNT=10
# 日志文件LOG_FILE="system_monitor.log"
# 清空日志文件> "$LOG_FILE"
# 监控系统状态for ((i=1; i<=COUNT; i++)); do # 时间戳 timestamp=$(date +"%Y-%m-%d %H:%M:%S")
# 系统负载 load=$(uptime | awk '{print $10, $11, $12}')
# 内存使用 memory=$(free -h | grep "Mem:" | awk '{print $3 "/" $2}')
# 磁盘使用 disk=$(df -h / | grep "/" | awk '{print $3 "/" $2}')
# CPU使用(简化版) cpu=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
# 网络流量 network=$(ifconfig eth0 | grep "RX bytes" | awk '{print $2}' | sed 's/bytes://')
# 写入日志 echo "[$timestamp] Load: $load | Memory: $memory | Disk: $disk | CPU: ${cpu}% | Network: $network bytes" >> "$LOG_FILE"
# 显示到控制台 echo "[$timestamp] Load: $load | Memory: $memory | Disk: $disk | CPU: ${cpu}% | Network: $network bytes"
# 等待 sleep $INTERVALdone
echo "Monitoring completed! Log saved to $LOG_FILE"系统信息脚本
#!/bin/bash
# 系统信息脚本
echo "===================================="echo "System Information Script"echo "===================================="
# 主机名echo "Hostname: $(hostname)"
# 操作系统echo "OS: $(lsb_release -ds)"
# 内核版本echo "Kernel: $(uname -r)"
# 架构echo "Architecture: $(uname -m)"
# 启动时间echo "Uptime: $(uptime -p)"
# 处理器信息echo "CPU: $(lscpu | grep "Model name" | awk -F":" '{print $2}' | sed 's/^[ \t]*//')"echo "CPU Cores: $(nproc)"
# 内存信息echo "Memory: $(free -h | grep "Mem:" | awk '{print $3 "/" $2}')"
# 磁盘信息echo "Disk Usage:"df -h | grep -E "^/dev/"
# 网络信息echo "Network Interfaces:"ip addr | grep -E "^[0-9]+: " | awk -F":" '{print $2}' | sed 's/^[ \t]*//'
# 登录用户echo "Logged in Users:"who
# 运行进程数echo "Running Processes: $(ps aux | wc -l)"
# 系统负载echo "System Load: $(uptime | awk '{print $10, $11, $12}')"
echo "===================================="echo "Script completed!"echo "===================================="总结
本文介绍了Linux Shell脚本编程的基本概念和技术,包括变量、运算符、控制流、函数、输入输出、数组、算术运算、正则表达式和脚本调试等内容。通过学习这些知识,你可以编写各种自动化脚本,提高工作效率。
Shell脚本是Linux系统管理和自动化任务的强大工具,掌握Shell脚本编程是Linux系统管理员的必备技能。希望本文能够帮助你入门Shell脚本编程,并在实践中不断提高。
练习
-
编写一个脚本,输出当前目录下所有文件的名称和大小。
-
编写一个脚本,计算1到100的和。
-
编写一个脚本,检查指定文件是否存在,如果存在则显示其权限和大小。
-
编写一个脚本,接收用户输入的两个数,计算它们的和、差、积、商。
-
编写一个脚本,遍历当前目录下的所有.txt文件,将其中包含”error”的行输出到error.log文件。
-
编写一个脚本,创建一个目录结构:test/{dir1,dir2,dir3}。
-
编写一个脚本,显示系统的CPU使用率、内存使用率和磁盘使用率。
-
编写一个脚本,备份指定目录到/home/backup目录,备份文件名为目录名加上当前日期。
-
编写一个脚本,检查系统中是否安装了指定的软件包,如果没有安装则提示用户。
-
编写一个脚本,使用函数计算两个数的最大公约数。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!