乐闻世界logo
搜索文章和话题

Shell

Shell 是一个命令行解释器,它提供了一个用户界面,用于访问操作系统的服务。在 shell 中,用户可以输入命令、执行程序和管理文件系统。Shell 也可以运行存储在文本文件中的命令序列,这些文本文件通常被称为 shell 脚本或批处理文件。
Shell
查看更多相关内容
Shell 脚本中如何进行进程管理?如何启动、监控和终止进程?Shell 脚本中的进程管理包括启动、监控、终止进程以及进程间通信等操作。 ## 进程启动 ### 后台运行 ```bash # 在后台运行命令 command & # 后台运行并忽略挂起信号 nohup command & # 使用 disown 将进程从当前 shell 分离 command & disown %1 ``` ### 子进程 ```bash # 使用括号创建子进程 (command1; command2) & # 子进程中的变量不影响父进程 ( local_var="child" echo "Child: $local_var" ) echo "Parent: $local_var" # 输出: Parent: (空) ``` ## 进程监控 ### 查看进程 ```bash # 查看所有进程 ps aux # 查看特定用户的进程 ps -u username # 查看特定进程 ps -p PID # 实时监控进程 top htop # 查看进程树 pstree ``` ### 查找进程 ```bash # 使用 grep 查找进程 ps aux | grep "nginx" # 使用 pgrep 查找进程 ID pgrep nginx # 使用 pidof 查找进程 ID pidof nginx # 查找进程详细信息 ps -p $(pgrep nginx) -o pid,ppid,cmd ``` ### 监控进程状态 ```bash # 检查进程是否运行 if ps -p PID > /dev/null; then echo "Process is running" else echo "Process is not running" fi # 使用 pgrep 检查 if pgrep -x "nginx" > /dev/null; then echo "nginx is running" fi ``` ## 进程终止 ### 终止进程 ```bash # 发送 SIGTERM 信号(正常终止) kill PID # 发送 SIGKILL 信号(强制终止) kill -9 PID # 按名称终止进程 pkill process_name # 终止所有匹配的进程 killall process_name ``` ### 信号类型 ```bash # 常用信号 kill -l # 列出所有信号 # SIGHUP (1) - 重新加载配置 kill -1 PID # SIGINT (2) - 中断(Ctrl+C) kill -2 PID # SIGTERM (15) - 正常终止 kill -15 PID # SIGKILL (9) - 强制终止 kill -9 PID ``` ### 等待进程 ```bash # 启动后台进程 command & PID=$! # 等待进程完成 wait $PID # 等待所有后台进程 wait # 检查进程退出状态 wait $PID exit_status=$? echo "Exit status: $exit_status" ``` ## 进程间通信 ### 信号通信 ```bash # 捕获信号的脚本 #!/bin/bash trap 'echo "Received SIGINT"; exit' INT trap 'echo "Received SIGTERM"; exit' TERM while true; do echo "Running..." sleep 1 done ``` ### 管道通信 ```bash # 匿名管道 command1 | command2 # 命名管道(FIFO) mkfifo /tmp/my_pipe cat > /tmp/my_pipe & cat < /tmp/my_pipe ``` ### 文件通信 ```bash # 使用文件进行通信 lock_file="/tmp/script.lock" # 创建锁文件 if [ -f "$lock_file" ]; then echo "Script is already running" exit 1 fi touch "$lock_file" # 执行任务 # ... # 删除锁文件 rm -f "$lock_file" ``` ## 实际应用示例 ### 进程守护脚本 ```bash #!/bin/bash # 守护进程脚本 daemon_process() { local command="$1" local lock_file="/tmp/${command}.lock" # 检查锁文件 if [ -f "$lock_file" ]; then local pid=$(cat "$lock_file") if ps -p "$pid" > /dev/null 2>&1; then echo "$command is already running (PID: $pid)" return 1 else rm -f "$lock_file" fi fi # 启动进程 $command & local pid=$! echo $pid > "$lock_file" echo "Started $command (PID: $pid)" return 0 } # 使用守护进程 daemon_process "nginx" ``` ### 进程监控脚本 ```bash #!/bin/bash # 监控进程脚本 monitor_process() { local process_name="$1" local max_memory="$2" # MB local max_cpu="$3" # % while true; do # 获取进程信息 local pid=$(pgrep -x "$process_name") if [ -z "$pid" ]; then echo "$process_name is not running" sleep 5 continue fi # 获取内存使用 local memory=$(ps -p "$pid" -o rss= | awk '{print $1/1024}') # 获取 CPU 使用 local cpu=$(ps -p "$pid" -o %cpu=) echo "$process_name (PID: $pid) - Memory: ${memory}MB, CPU: ${cpu}%" # 检查是否超过阈值 if (( $(echo "$memory > $max_memory" | bc -l) )); then echo "Warning: Memory usage exceeds ${max_memory}MB" fi if (( $(echo "$cpu > $max_cpu" | bc -l) )); then echo "Warning: CPU usage exceeds ${max_cpu}%" fi sleep 5 done } # 使用监控脚本 monitor_process "nginx" 512 80 ``` ### 批量进程管理 ```bash #!/bin/bash # 批量终止进程 kill_processes() { local pattern="$1" local signal="${2:-15}" local pids=$(pgrep -f "$pattern") if [ -z "$pids" ]; then echo "No processes found matching: $pattern" return 1 fi echo "Killing processes: $pids" kill -$signal $pids sleep 2 # 检查进程是否仍在运行 for pid in $pids; do if ps -p "$pid" > /dev/null 2>&1; then echo "Process $pid still running, forcing kill" kill -9 "$pid" fi done echo "All processes killed" } # 使用批量终止 kill_processes "python.*script.py" ``` ### 进程重启脚本 ```bash #!/bin/bash # 进程重启脚本 restart_process() { local process_name="$1" local command="$2" echo "Restarting $process_name..." # 终止进程 local pid=$(pgrep -x "$process_name") if [ -n "$pid" ]; then echo "Stopping $process_name (PID: $pid)" kill "$pid" sleep 2 # 检查是否仍在运行 if ps -p "$pid" > /dev/null 2>&1; then echo "Force killing $process_name" kill -9 "$pid" fi fi # 启动进程 echo "Starting $process_name..." $command & local new_pid=$! sleep 2 # 检查是否启动成功 if ps -p "$new_pid" > /dev/null 2>&1; then echo "$process_name restarted successfully (PID: $new_pid)" return 0 else echo "Failed to start $process_name" return 1 fi } # 使用重启脚本 restart_process "nginx" "nginx -g 'daemon off;'" ``` ## 进程管理最佳实践 1. **使用 nohup 和 disown**: 确保进程在 shell 退出后继续运行 2. **使用锁文件**: 防止重复启动进程 3. **优雅地终止进程**: 优先使用 SIGTERM 而不是 SIGKILL 4. **监控进程状态**: 定期检查进程是否正常运行 5. **记录进程信息**: 便于问题排查和调试 6. **使用信号处理**: 实现优雅的进程关闭 7. **设置资源限制**: 防止进程占用过多资源 8. **使用进程组**: 便于管理相关进程
服务端 · 3月6日 23:38
Shell 脚本中如何进行错误处理和调试?有哪些常用的技巧?Shell 脚本中的错误处理和调试技巧对于编写健壮的脚本非常重要。 ## 错误处理 ### 退出状态码 ```bash # 检查命令执行状态 command if [ $? -eq 0 ]; then echo "Command succeeded" else echo "Command failed with exit code $?" fi # 使用 && 和 || 进行条件执行 command1 && command2 # command2 仅在 command1 成功时执行 command1 || command2 # command2 仅在 command1 失败时执行 ``` ### set 命令选项 ```bash # set -e: 任何命令失败时立即退出 set -e command1 command2 # 如果 command1 失败,不会执行 # set -u: 使用未定义变量时报错 set -u echo $undefined_var # 报错并退出 # set -o pipefail: 管道中任何命令失败时返回失败状态 set -o pipefail command1 | command2 # 如果 command1 失败,整个管道失败 # 组合使用 set -euo pipefail ``` ### 错误处理函数 ```bash # 错误处理函数 error_exit() { echo "Error: $1" >&2 exit 1 } # 检查文件是否存在 check_file() { if [ ! -f "$1" ]; then error_exit "File $1 not found" fi } # 使用函数 check_file "config.txt" ``` ### trap 命令 ```bash # 捕获退出信号 cleanup() { echo "Cleaning up..." rm -f /tmp/tempfile exit } # 捕获 EXIT 信号 trap cleanup EXIT # 捕获 INT 信号(Ctrl+C) trap cleanup INT # 捕获多个信号 trap cleanup EXIT INT TERM ``` ## 调试技巧 ### 调试模式 ```bash # 启用调试模式 set -x # 打印每个命令 set -v # 打印输入行 # 禁用调试模式 set +x set +v # 组合使用 set -xv command1 command2 set +xv ``` ### 调试输出 ```bash # 使用 echo 输出调试信息 echo "Debug: variable = $variable" # 使用 printf 格式化输出 printf "Debug: %s = %s\n" "variable" "$variable" # 使用 >&2 输出到标准错误 echo "Debug info" >&2 ``` ### 调试函数 ```bash # 调试函数 debug() { if [ "$DEBUG" = "true" ]; then echo "[DEBUG] $*" >&2 fi } # 使用调试函数 DEBUG=true debug "Processing file: $filename" ``` ### 变量追踪 ```bash # 显示变量值 echo "var1 = $var1" echo "var2 = $var2" # 使用 declare 显示变量信息 declare -p var1 declare -p var2 # 显示所有变量 declare -p # 显示所有函数 declare -f ``` ## 实际应用示例 ### 健壮的脚本模板 ```bash #!/bin/bash # 设置错误处理 set -euo pipefail # 定义错误处理函数 error_exit() { echo "Error: $1" >&2 exit 1 } # 定义清理函数 cleanup() { echo "Cleaning up..." [ -n "$tempfile" ] && rm -f "$tempfile" } # 设置陷阱 trap cleanup EXIT INT TERM # 定义调试函数 debug() { if [ "${DEBUG:-false}" = "true" ]; then echo "[DEBUG] $*" >&2 fi } # 主函数 main() { debug "Starting script" # 检查参数 if [ $# -lt 1 ]; then error_exit "Usage: $0 <filename>" fi local filename="$1" debug "Processing file: $filename" # 检查文件 if [ ! -f "$filename" ]; then error_exit "File not found: $filename" fi # 创建临时文件 tempfile=$(mktemp) || error_exit "Failed to create temp file" debug "Created temp file: $tempfile" # 处理文件 cp "$filename" "$tempfile" # 处理逻辑... debug "Script completed successfully" } # 执行主函数 main "$@" ``` ### 带错误检查的文件操作 ```bash #!/bin/bash # 安全的文件复制 safe_copy() { local src="$1" local dst="$2" # 检查源文件 if [ ! -f "$src" ]; then echo "Error: Source file not found: $src" >&2 return 1 fi # 检查目标目录 local dst_dir=$(dirname "$dst") if [ ! -d "$dst_dir" ]; then echo "Error: Destination directory not found: $dst_dir" >&2 return 1 fi # 检查写权限 if [ ! -w "$dst_dir" ]; then echo "Error: No write permission for: $dst_dir" >&2 return 1 fi # 执行复制 if ! cp "$src" "$dst"; then echo "Error: Failed to copy $src to $dst" >&2 return 1 fi echo "Successfully copied $src to $dst" return 0 } # 使用函数 safe_copy "source.txt" "destination.txt" || exit 1 ``` ### 带重试的操作 ```bash #!/bin/bash # 带重试的函数 retry_command() { local max_attempts="$1" shift local command=("$@") local attempt=1 while [ $attempt -le $max_attempts ]; do echo "Attempt $attempt of $max_attempts" if "${command[@]}"; then echo "Command succeeded" return 0 fi attempt=$((attempt + 1)) sleep 2 done echo "Command failed after $max_attempts attempts" >&2 return 1 } # 使用函数 retry_command 3 curl -s http://example.com || exit 1 ``` ### 日志记录 ```bash #!/bin/bash # 日志函数 log() { local level="$1" shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" | tee -a script.log } log_info() { log "INFO" "$@" } log_error() { log "ERROR" "$@" } log_debug() { if [ "${DEBUG:-false}" = "true" ]; then log "DEBUG" "$@" fi } # 使用日志函数 log_info "Script started" log_debug "Debug information" log_error "An error occurred" ``` ## 调试最佳实践 1. **使用 set -euo pipefail**: 提高脚本的健壮性 2. **使用 trap 进行清理**: 确保资源被正确释放 3. **使用调试函数**: 便于控制调试输出 4. **记录日志**: 便于问题追踪和调试 5. **检查命令状态**: 使用 $? 或 if 语句 6. **使用有意义的错误消息**: 帮助快速定位问题 7. **测试边界情况**: 确保脚本在各种情况下都能正常工作 8. **使用变量默认值**: `${VAR:-default}` 防止未定义变量错误
服务端 · 3月6日 23:38
Shell 脚本中常用的文本处理工具有哪些?如何使用 grep、sed、awk 和 cut?Shell 脚本中常用的文本处理工具包括 grep、sed、awk 和 cut 等。 ## grep - 文本搜索工具 ### 基本用法 ```bash # 在文件中搜索文本 grep "pattern" file.txt # 搜索多个文件 grep "pattern" file1.txt file2.txt # 递归搜索目录 grep -r "pattern" /path/to/directory # 忽略大小写 grep -i "pattern" file.txt # 显示行号 grep -n "pattern" file.txt # 反向匹配(不包含) grep -v "pattern" file.txt # 只显示匹配的文件名 grep -l "pattern" *.txt # 统计匹配行数 grep -c "pattern" file.txt ``` ### 正则表达式 ```bash # 匹配行首 grep "^start" file.txt # 匹配行尾 grep "end$" file.txt # 匹配数字 grep "[0-9]" file.txt # 匹配特定次数 grep "a\{3\}" file.txt # 匹配 3 个 a # 使用扩展正则表达式 grep -E "pattern1|pattern2" file.txt ``` ### 实际应用 ```bash # 查找进程 ps aux | grep "nginx" # 查找日志中的错误 grep "ERROR" /var/log/syslog # 查找包含特定内容的文件 grep -r "TODO" ./src # 统计代码行数 grep -c "^" *.py ``` ## sed - 流编辑器 ### 基本用法 ```bash # 替换文本 sed 's/old/new/' file.txt # 全局替换 sed 's/old/new/g' file.txt # 删除行 sed '3d' file.txt # 删除第 3 行 sed '/pattern/d' file.txt # 删除匹配的行 # 打印特定行 sed -n '5p' file.txt # 打印第 5 行 sed -n '1,5p' file.txt # 打印 1-5 行 # 插入和追加 sed '2i\new line' file.txt # 在第 2 行前插入 sed '2a\new line' file.txt # 在第 2 行后追加 ``` ### 高级用法 ```bash # 使用正则表达式 sed 's/[0-9]\+//g' file.txt # 多个替换 sed -e 's/old1/new1/g' -e 's/old2/new2/g' file.txt # 就地编辑(修改原文件) sed -i 's/old/new/g' file.txt # 备份后编辑 sed -i.bak 's/old/new/g' file.txt # 使用变量 var="pattern" sed "s/$var/replacement/g" file.txt ``` ### 实际应用 ```bash # 替换配置文件中的值 sed -i 's/port=8080/port=9090/' config.ini # 删除注释行 sed '/^#/d' file.txt # 删除空行 sed '/^$/d' file.txt # 格式化输出 sed 's/\s\+/ /g' file.txt ``` ## awk - 文本处理工具 ### 基本用法 ```bash # 打印特定列 awk '{print $1}' file.txt # 打印多列 awk '{print $1, $3}' file.txt # 指定分隔符 awk -F: '{print $1}' /etc/passwd # 打印行号 awk '{print NR, $0}' file.txt # 条件打印 awk '$3 > 100 {print $0}' file.txt ``` ### 内置变量 ```bash NR # 当前记录号(行号) NF # 当前记录的字段数 $0 # 完整的记录 $1, $2 # 第 1、2 个字段 FS # 字段分隔符(默认空格) OFS # 输出字段分隔符 RS # 记录分隔符(默认换行) ORS # 输出记录分隔符 ``` ### 模式和动作 ```bash # 模式匹配 awk '/pattern/ {print $0}' file.txt # BEGIN 和 END 块 awk 'BEGIN {print "Start"} {print $0} END {print "End"}' file.txt # 计算总和 awk '{sum += $1} END {print sum}' file.txt # 计算平均值 awk '{sum += $1; count++} END {print sum/count}' file.txt ``` ### 实际应用 ```bash # 统计文件大小 ls -l | awk '{sum += $5} END {print sum}' # 查找最大值 awk '{if ($1 > max) max = $1} END {print max}' file.txt # 格式化输出 awk '{printf "%-10s %10s\n", $1, $2}' file.txt # 处理 CSV 文件 awk -F, '{print $1, $3}' data.csv ``` ## cut - 文本切割工具 ### 基本用法 ```bash # 按字符切割 cut -c 1-5 file.txt # 提取 1-5 字符 cut -c 1,5,10 file.txt # 提取第 1、5、10 字符 # 按字节切割 cut -b 1-10 file.txt # 按字段切割 cut -d: -f1 /etc/passwd # 提取第 1 个字段 cut -d: -f1,3 /etc/passwd # 提取第 1、3 个字段 ``` ### 实际应用 ```bash # 提取用户名 cut -d: -f1 /etc/passwd # 提取 IP 地址 ifconfig | grep "inet " | cut -d: -f2 | cut -d' ' -f1 # 提取文件扩展名 echo "file.txt" | cut -d. -f2 ``` ## 组合使用示例 ### 日志分析 ```bash # 统计错误数量 grep "ERROR" /var/log/app.log | wc -l # 查找特定时间段的日志 sed -n '/2024-01-01 10:00/,/2024-01-01 11:00/p' /var/log/app.log # 提取 IP 地址 grep "ERROR" /var/log/app.log | awk '{print $5}' | cut -d: -f2 # 统计各错误类型的数量 grep "ERROR" /var/log/app.log | awk '{print $6}' | sort | uniq -c ``` ### 文本处理 ```bash # 删除空行和注释 sed '/^$/d; /^#/d' file.txt # 替换多个空格为单个空格 sed 's/\s\+/ /g' file.txt # 提取特定列并去重 awk '{print $1}' file.txt | sort -u # 计算平均值 awk '{sum += $1} END {print sum/NR}' file.txt ``` ### 系统管理 ```bash # 查找占用 CPU 最高的进程 ps aux | sort -rk 3 | head -n 5 # 查找占用内存最多的进程 ps aux | sort -rk 4 | head -n 5 # 统计各用户进程数 ps aux | awk '{print $1}' | sort | uniq -c # 查找特定端口的进程 lsof -i :8080 | awk '{print $2}' | tail -n +2 ``` ## 最佳实践 1. **使用管道组合工具**: `grep | awk | sort | uniq` 2. **优先使用 grep 进行搜索**: 简单搜索时最快 3. **使用 sed 进行替换**: 文本替换的首选工具 4. **使用 awk 处理列数据**: 处理结构化文本的最佳选择 5. **使用 cut 提取固定位置**: 简单的文本切割任务 6. **注意正则表达式语法**: grep 和 sed 的正则表达式略有不同 7. **测试命令**: 在处理重要文件前先测试命令
服务端 · 3月6日 21:40
Shell 脚本中如何使用 if 语句和 case 语句进行条件判断?Shell 脚本中常用的条件判断语句包括 if 语句、case 语句和测试命令。 ## if 语句 ### 基本语法 ```bash if [ condition ]; then commands elif [ condition ]; then commands else commands fi ``` ### 文件测试操作符 ```bash [ -f file ] # 文件存在且为普通文件 [ -d dir ] # 目录存在 [ -e file ] # 文件或目录存在 [ -r file ] # 文件可读 [ -w file ] # 文件可写 [ -x file ] # 文件可执行 [ -s file ] # 文件大小大于零 [ -L file ] # 文件是符号链接 ``` ### 字符串测试操作符 ```bash [ "$str1" = "$str2" ] # 字符串相等 [ "$str1" != "$str2" ] # 字符串不等 [ -z "$str" ] # 字符串为空 [ -n "$str" ] # 字符串非空 ``` ### 数值测试操作符 ```bash [ $num1 -eq $num2 ] # 等于 [ $num1 -ne $num2 ] # 不等于 [ $num1 -gt $num2 ] # 大于 [ $num1 -lt $num2 ] # 小于 [ $num1 -ge $num2 ] # 大于等于 [ $num1 -le $num2 ] # 小于等于 ``` ### 逻辑操作符 ```bash [ condition1 ] && [ condition2 ] # 逻辑与 [ condition1 ] || [ condition2 ] # 逻辑或 ! [ condition ] # 逻辑非 [ condition1 -a condition2 ] # 逻辑与(在 [] 内) [ condition1 -o condition2 ] # 逻辑或(在 [] 内) ``` ### if 语句示例 ```bash #!/bin/bash # 检查文件是否存在 if [ -f "/etc/passwd" ]; then echo "File exists" else echo "File does not exist" fi # 检查数值 age=25 if [ $age -ge 18 ]; then echo "Adult" elif [ $age -ge 13 ]; then echo "Teenager" else echo "Child" fi # 复合条件 if [ -f "$1" ] && [ -r "$1" ]; then echo "File exists and is readable" fi # 使用 [[ ]] 进行更复杂的测试 if [[ $name == "John" || $name == "Jane" ]]; then echo "Welcome $name" fi ``` ## case 语句 ### 基本语法 ```bash case $variable in pattern1) commands ;; pattern2) commands ;; *) commands ;; esac ``` ### case 语句示例 ```bash #!/bin/bash # 简单的菜单选择 echo "Select an option:" echo "1. Start" echo "2. Stop" echo "3. Restart" read -p "Enter choice: " choice case $choice in 1) echo "Starting service..." ;; 2) echo "Stopping service..." ;; 3) echo "Restarting service..." ;; *) echo "Invalid choice" ;; esac # 使用模式匹配 file="document.txt" case $file in *.txt) echo "Text file" ;; *.jpg|*.png) echo "Image file" ;; *) echo "Unknown file type" ;; esac ``` ## 测试命令 ### test 命令 ```bash test condition # 等同于 [ condition ] ``` ### 双括号 [[ ]] ```bash # [[ ]] 是 Bash 的扩展,功能更强大 [[ $name == "John" && $age -gt 18 ]] ``` ### 双括号 (( )) ```bash # (( )) 用于算术运算 (( age++ )) if (( age >= 18 )); then echo "Adult" fi ``` ## 实际应用示例 ```bash #!/bin/bash # 检查命令行参数 if [ $# -eq 0 ]; then echo "Usage: $0 <filename>" exit 1 fi file=$1 # 检查文件类型 if [ -f "$file" ]; then echo "File exists" # 检查文件权限 if [ -r "$file" ]; then echo "File is readable" else echo "File is not readable" fi if [ -w "$file" ]; then echo "File is writable" else echo "File is not writable" fi elif [ -d "$file" ]; then echo "Directory exists" # 检查目录内容 file_count=$(ls -1 "$file" | wc -l) if [ $file_count -eq 0 ]; then echo "Directory is empty" else echo "Directory contains $file_count items" fi else echo "File or directory does not exist" exit 1 fi # 使用 case 处理文件扩展名 extension="${file##*.}" case $extension in txt|md) echo "Text document" ;; sh) echo "Shell script" ;; py) echo "Python script" ;; *) echo "Unknown file type" ;; esac ``` ## 最佳实践 1. **始终使用引号包裹变量**: `"$variable"` 防止空格和特殊字符问题 2. **优先使用 [[ ]] 而不是 []**: 功能更强大,更安全 3. **使用 case 处理多条件匹配**: 代码更清晰 4. **检查命令执行状态**: 使用 `$?` 或 `if command; then` 5. **避免使用 -a 和 -o**: 使用 `&&` 和 `||` 更清晰
服务端 · 3月6日 21:40
Shell 脚本中如何定义和使用数组?常用的数组操作有哪些?Shell 脚本中的数组操作包括数组的定义、访问、遍历和常用操作。 ## 数组定义 ### 普通数组 ```bash # 定义空数组 arr=() # 定义数组(空格分隔) arr=(apple banana cherry) # 逐个定义 arr[0]="apple" arr[1]="banana" arr[2]="cherry" # 使用命令输出定义数组 arr=($(ls *.txt)) ``` ### 关联数组(Bash 4.0+) ```bash # 声明关联数组 declare -A arr # 定义关联数组 arr[name]="John" arr[age]=25 arr[city]="Beijing" # 一次性定义 declare -A arr=([name]="John" [age]=25 [city]="Beijing") ``` ## 数组访问 ### 访问单个元素 ```bash arr=(apple banana cherry) # 访问特定索引的元素 echo ${arr[0]} # 输出: apple echo ${arr[1]} # 输出: banana echo ${arr[2]} # 输出: cherry # 访问最后一个元素 echo ${arr[-1]} # 输出: cherry # 访问不存在的索引 echo ${arr[10]} # 输出: (空) ``` ### 访问所有元素 ```bash # 访问所有元素 echo ${arr[@]} # 输出: apple banana cherry echo ${arr[*]} # 输出: apple banana cherry # 访问所有元素的索引 echo ${!arr[@]} # 输出: 0 1 2 # 访问数组长度 echo ${#arr[@]} # 输出: 3 echo ${#arr[*]} # 输出: 3 ``` ### 访问部分元素 ```bash # 访问从索引 1 开始的所有元素 echo ${arr[@]:1} # 输出: banana cherry # 访问从索引 1 开始的 2 个元素 echo ${arr[@]:1:2} # 输出: banana cherry # 访问从倒数第 2 个元素开始 echo ${arr[@]: -2} # 输出: banana cherry ``` ## 数组遍历 ### 遍历所有元素 ```bash arr=(apple banana cherry) # 方法 1: 使用 for 循环 for item in "${arr[@]}"; do echo "Item: $item" done # 方法 2: 使用索引遍历 for i in "${!arr[@]}"; do echo "Index $i: ${arr[$i]}" done # 方法 3: 使用 C 风格循环 for ((i=0; i<${#arr[@]}; i++)); do echo "Index $i: ${arr[$i]}" done ``` ### 遍历关联数组 ```bash declare -A arr=([name]="John" [age]=25 [city]="Beijing") # 遍历键 for key in "${!arr[@]}"; do echo "Key: $key" done # 遍历键值对 for key in "${!arr[@]}"; do echo "$key: ${arr[$key]}" done ``` ## 数组操作 ### 添加元素 ```bash arr=(apple banana) # 添加到末尾 arr+=(cherry) arr+=("date" "fig") # 添加到特定位置 arr[2]="cherry" # 替换或添加 # 使用索引添加 arr[${#arr[@]}]="grape" # 添加到末尾 ``` ### 删除元素 ```bash arr=(apple banana cherry date fig) # 删除特定索引的元素 unset arr[2] # 删除索引 2 的元素 # 删除整个数组 unset arr # 删除关联数组的元素 declare -A arr=([name]="John" [age]=25) unset arr[name] ``` ### 修改元素 ```bash arr=(apple banana cherry) # 修改特定索引的元素 arr[0]="orange" arr[1]="pear" # 批量修改 for i in "${!arr[@]}"; do arr[$i]="${arr[$i]}_modified" done ``` ### 数组切片 ```bash arr=(one two three four five) # 切片操作 echo ${arr[@]:1:3} # 输出: two three four # 切片并赋值 new_arr=(${arr[@]:1:3}) ``` ## 数组排序 ### 简单排序 ```bash arr=(banana apple cherry date) # 字典序排序 sorted=($(echo "${arr[@]}" | tr ' ' '\n' | sort)) echo "${sorted[@]}" # 数字排序 nums=(5 2 8 1 9) sorted_nums=($(echo "${nums[@]}" | tr ' ' '\n' | sort -n)) echo "${sorted_nums[@]}" # 逆序排序 sorted_rev=($(echo "${arr[@]}" | tr ' ' '\n' | sort -r)) echo "${sorted_rev[@]}" ``` ### 去重 ```bash arr=(apple banana apple cherry banana) # 去重 unique=($(echo "${arr[@]}" | tr ' ' '\n' | sort -u)) echo "${unique[@]}" ``` ## 数组查找 ### 查找元素 ```bash arr=(apple banana cherry) # 检查元素是否存在 if [[ " ${arr[@]} " =~ " banana " ]]; then echo "Found banana" fi # 使用函数查找 contains_element() { local e match="$1" shift for e; do [[ "$e" == "$match" ]] && return 0; done return 1 } if contains_element "banana" "${arr[@]}"; then echo "Found banana" fi ``` ### 查找索引 ```bash arr=(apple banana cherry) # 查找元素索引 get_index() { local element=$1 shift local arr=("$@") for i in "${!arr[@]}"; do if [[ "${arr[$i]}" == "$element" ]]; then echo $i return 0 fi done return 1 } index=$(get_index "banana" "${arr[@]}") echo "Index of banana: $index" ``` ## 实际应用示例 ### 文件处理 ```bash # 获取所有 .txt 文件 files=($(ls *.txt)) # 遍历并处理 for file in "${files[@]}"; do echo "Processing: $file" # 处理文件 done # 检查文件是否存在 if [ ${#files[@]} -eq 0 ]; then echo "No .txt files found" fi ``` ### 参数处理 ```bash # 将参数存储到数组 args=("$@") # 遍历参数 for arg in "${args[@]}"; do echo "Argument: $arg" done # 检查特定参数 if [[ " ${args[@]} " =~ " --verbose " ]]; then verbose=true fi ``` ### 数据统计 ```bash # 读取数据到数组 readarray -t lines < data.txt # 统计行数 echo "Total lines: ${#lines[@]}" # 统计包含特定内容的行 count=0 for line in "${lines[@]}"; do if [[ "$line" =~ "pattern" ]]; then ((count++)) fi done echo "Matching lines: $count" ``` ### 配置管理 ```bash # 解析配置文件到关联数组 declare -A config while IFS='=' read -r key value; do config[$key]="$value" done < config.txt # 访问配置 echo "Database: ${config[db_host]}" echo "Port: ${config[db_port]}" ``` ## 数组最佳实践 1. **始终使用引号**: `"${arr[@]}"` 防止空格和特殊字符问题 2. **使用 ${#arr[@]} 获取长度**: 而不是 ${#arr} 3. **使用 ${!arr[@]} 获取索引**: 遍历时更安全 4. **关联数组需要声明**: `declare -A` 声明关联数组 5. **避免使用未初始化的索引**: 检查索引是否存在 6. **使用函数封装复杂操作**: 提高代码可读性 7. **注意数组索引从 0 开始**: 与其他编程语言一致
服务端 · 3月6日 21:40
Shell 脚本中如何定义和调用函数?如何传递参数和返回值?Shell 脚本中函数的定义、调用和参数传递机制如下: ## 函数定义 ### 基本语法 ```bash function_name() { commands } # 或者使用 function 关键字 function function_name { commands } ``` ### 函数定义示例 ```bash # 简单函数 greet() { echo "Hello, World!" } # 带参数的函数 greet_user() { echo "Hello, $1!" } # 带多个参数的函数 add() { local sum=$(($1 + $2)) echo "Sum: $sum" } # 带默认值的函数 backup_file() { local file=${1:-"default.txt"} local backup_dir=${2:-"./backup"} echo "Backing up $file to $backup_dir" } ``` ## 函数调用 ### 基本调用 ```bash # 定义函数 greet() { echo "Hello, World!" } # 调用函数 greet # 输出: Hello, World! ``` ### 传递参数 ```bash # 定义带参数的函数 greet_user() { echo "Hello, $1!" } # 调用并传递参数 greet_user "John" # 输出: Hello, John! # 传递多个参数 add() { echo "$1 + $2 = $(($1 + $2))" } add 5 3 # 输出: 5 + 3 = 8 ``` ## 函数参数 ### 位置参数 ```bash show_args() { echo "First argument: $1" echo "Second argument: $2" echo "Third argument: $3" echo "All arguments: $@" echo "Number of arguments: $#" } show_args "apple" "banana" "cherry" # 输出: # First argument: apple # Second argument: banana # Third argument: cherry # All arguments: apple banana cherry # Number of arguments: 3 ``` ### 处理可变参数 ```bash # 处理任意数量的参数 sum_all() { local total=0 for num in "$@"; do total=$((total + num)) done echo "Total: $total" } sum_all 1 2 3 4 5 # 输出: Total: 15 ``` ## 局部变量 ### local 关键字 ```bash # 使用 local 声明局部变量 counter=0 increment() { local counter=10 echo "Inside function: $counter" counter=$((counter + 1)) echo "After increment: $counter" } increment echo "Outside function: $counter" # 输出: # Inside function: 10 # After increment: 11 # Outside function: 0 ``` ### 局部变量的重要性 ```bash # 不使用 local 会导致全局变量被修改 global_var="original" bad_function() { global_var="modified" } good_function() { local global_var="local only" echo "Local: $global_var" } bad_function echo "After bad_function: $global_var" # 输出: After bad_function: modified good_function echo "After good_function: $global_var" # 输出: After good_function: modified ``` ## 返回值 ### 使用 return 返回状态码 ```bash check_file() { if [ -f "$1" ]; then return 0 # 成功 else return 1 # 失败 fi } check_file "/etc/passwd" if [ $? -eq 0 ]; then echo "File exists" else echo "File does not exist" fi ``` ### 使用 echo 返回值 ```bash # 返回字符串或计算结果 get_username() { echo "John Doe" } username=$(get_username) echo "Username: $username" # 输出: Username: John Doe # 返回计算结果 calculate() { echo $(($1 * $2)) } result=$(calculate 5 4) echo "Result: $result" # 输出: Result: 20 ``` ### 返回数组 ```bash get_array() { local arr=("apple" "banana" "cherry") echo "${arr[@]}" } fruits=($(get_array)) for fruit in "${fruits[@]}"; do echo "Fruit: $fruit" done ``` ## 函数库和导入 ### 创建函数库 ```bash # 文件: mylib.sh #!/bin/bash log_info() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_error() { echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2 } check_command() { if command -v "$1" >/dev/null 2>&1; then return 0 else return 1 fi } ``` ### 导入函数库 ```bash #!/bin/bash # 导入函数库 source ./mylib.sh # 或者 . ./mylib.sh # 使用库中的函数 log_info "Script started" if check_command "git"; then log_info "Git is installed" else log_error "Git is not installed" fi ``` ## 实际应用示例 ```bash #!/bin/bash # 函数 1: 检查文件是否存在 check_file() { local file=$1 if [ -f "$file" ]; then return 0 else return 1 fi } # 函数 2: 创建备份 create_backup() { local source=$1 local backup_dir=${2:-"./backup"} if ! check_file "$source"; then echo "Error: File $source does not exist" return 1 fi mkdir -p "$backup_dir" local timestamp=$(date +%Y%m%d_%H%M%S) local backup_file="$backup_dir/$(basename $source).$timestamp" cp "$source" "$backup_file" echo "Backup created: $backup_file" return 0 } # 函数 3: 批量处理文件 process_files() { local pattern=$1 local count=0 for file in $pattern; do if [ -f "$file" ]; then echo "Processing: $file" create_backup "$file" count=$((count + 1)) fi done echo "Processed $count files" return 0 } # 函数 4: 显示使用帮助 show_help() { echo "Usage: $0 [options]" echo "Options:" echo " -f <file> Backup single file" echo " -p <pattern> Backup files matching pattern" echo " -h Show this help" } # 主程序 main() { case $1 in -f) if [ -n "$2" ]; then create_backup "$2" else echo "Error: No file specified" show_help fi ;; -p) if [ -n "$2" ]; then process_files "$2" else echo "Error: No pattern specified" show_help fi ;; -h) show_help ;; *) echo "Error: Invalid option" show_help ;; esac } # 执行主程序 main "$@" ``` ## 函数最佳实践 1. **使用 local 声明局部变量**: 避免污染全局命名空间 2. **函数名使用小写**: 遵循 Shell 命名约定 3. **添加注释**: 说明函数用途和参数 4. **检查参数**: 验证输入参数的有效性 5. **返回适当的退出码**: 0 表示成功,非 0 表示失败 6. **使用函数库**: 将常用函数组织到单独的文件中 7. **避免副作用**: 函数应该专注于单一职责
服务端 · 3月6日 21:33
Shell 脚本中常用的字符串操作有哪些?如何进行字符串拼接、截取和替换?Shell 脚本中常用的字符串操作包括字符串拼接、截取、替换、比较和长度计算等。 ## 字符串定义 ### 基本定义 ```bash # 定义字符串 str1="Hello World" str2='Hello World' str3=Hello # 多行字符串 str4="Line 1 Line 2 Line 3" ``` ## 字符串拼接 ### 简单拼接 ```bash # 直接拼接 str1="Hello" str2="World" str3=$str1" "$str2 echo $str3 # 输出: Hello World # 使用引号 str4="${str1} ${str2}" echo $str4 # 输出: Hello World # 多个字符串拼接 str5="Hello" " " "World" echo $str5 # 输出: Hello World ``` ### 动态拼接 ```bash # 拼接变量和文本 name="John" greeting="Hello, $name!" echo $greeting # 输出: Hello, John! # 拼接命令输出 date_str="Current date: $(date)" echo $date_str # 拼接数组元素 arr=("apple" "banana" "cherry") str="${arr[0]}, ${arr[1]}, ${arr[2]}" echo $str # 输出: apple, banana, cherry ``` ## 字符串长度 ### 计算长度 ```bash # 计算字符串长度 str="Hello World" echo ${#str} # 输出: 11 # 使用 expr expr length "$str" # 输出: 11 # 使用 awk echo "$str" | awk '{print length}' ``` ## 字符串截取 ### 基本截取 ```bash str="Hello World" # 从指定位置开始截取 echo ${str:0} # 输出: Hello World echo ${str:6} # 输出: World # 从指定位置截取指定长度 echo ${str:0:5} # 输出: Hello echo ${str:6:5} # 输出: World # 从末尾截取 echo ${str: -5} # 输出: World echo ${str: -5:3} # 输出: Wor ``` ### 删除子串 ```bash str="Hello World" # 删除最短匹配的前缀 echo ${str#He} # 输出: llo World echo ${str#*o} # 输出: World # 删除最长匹配的前缀 echo ${str##*o} # 输出: rld # 删除最短匹配的后缀 echo ${str%ld} # 输出: Hello Wor echo ${str%o*} # 输出: Hello W # 删除最长匹配的后缀 echo ${str%%o*} # 输出: Hell ``` ### 提取文件名和路径 ```bash filepath="/path/to/file.txt" # 提取文件名 filename=${filepath##*/} echo $filename # 输出: file.txt # 提取目录 dirname=${filepath%/*} echo $dirname # 输出: /path/to # 提取扩展名 extension=${filepath##*.} echo $extension # 输出: txt # 去除扩展名 basename=${filename%.*} echo $basename # 输出: file ``` ## 字符串替换 ### 基本替换 ```bash str="Hello World" # 替换第一个匹配 echo ${str/World/Bash} # 输出: Hello Bash # 替换所有匹配 echo ${str//o/O} # 输出: HellO WOrld # 删除匹配 echo ${str/o/} # 输出: Hell World echo ${str//o/} # 输出: Hell Wrld ``` ### 前缀和后缀替换 ```bash str="Hello World" # 替换前缀 echo ${str/#Hello/Hi} # 输出: Hi World # 替换后缀 echo ${str/%World/Bash} # 输出: Hello Bash ``` ### 大小写转换 ```bash str="Hello World" # 转换为大写 echo ${str^^} # 输出: HELLO WORLD # 转换为小写 echo ${str,,} # 输出: hello world # 首字母大写 echo ${str^} # 输出: Hello world ``` ## 字符串比较 ### 基本比较 ```bash str1="Hello" str2="World" # 相等比较 if [ "$str1" = "$str2" ]; then echo "Strings are equal" fi # 不等比较 if [ "$str1" != "$str2" ]; then echo "Strings are not equal" fi # 使用 [[ ]] if [[ "$str1" == "$str2" ]]; then echo "Strings are equal" fi ``` ### 模式匹配 ```bash str="Hello World" # 检查是否以指定字符串开头 if [[ "$str" == Hello* ]]; then echo "Starts with Hello" fi # 检查是否以指定字符串结尾 if [[ "$str" == *World ]]; then echo "Ends with World" fi # 检查是否包含指定字符串 if [[ "$str" == *lo* ]]; then echo "Contains 'lo'" fi ``` ### 正则表达式匹配 ```bash str="Hello123" # 使用 =~ 进行正则匹配 if [[ "$str" =~ ^[A-Za-z]+$ ]]; then echo "Only letters" fi if [[ "$str" =~ ^[A-Za-z0-9]+$ ]]; then echo "Letters and numbers" fi # 提取匹配的子串 if [[ "$str" =~ ([A-Za-z]+)([0-9]+) ]]; then echo "Letters: ${BASH_REMATCH[1]}" echo "Numbers: ${BASH_REMATCH[2]}" fi ``` ## 字符串分割 ### 使用 IFS 分割 ```bash str="apple,banana,cherry" # 设置 IFS 并分割 IFS=',' read -ra arr <<< "$str" echo "${arr[0]}" # 输出: apple echo "${arr[1]}" # 输出: banana echo "${arr[2]}" # 输出: cherry # 遍历分割后的数组 for item in "${arr[@]}"; do echo "Item: $item" done ``` ### 使用 cut 分割 ```bash str="apple:banana:cherry" # 按分隔符分割 echo "$str" | cut -d: -f1 # 输出: apple echo "$str" | cut -d: -f2 # 输出: banana echo "$str" | cut -d: -f3 # 输出: cherry ``` ## 字符串去空 ### 去除空白字符 ```bash str=" Hello World " # 去除前导空格 str="${str#"${str%%[![:space:]]*}"}" # 去除尾部空格 str="${str%"${str##*[![:space:]]}"}" echo "$str" # 输出: Hello World ``` ### 使用 sed 去空 ```bash str=" Hello World " # 去除前导空格 echo "$str" | sed 's/^[[:space:]]*//' # 去除尾部空格 echo "$str" | sed 's/[[:space:]]*$//' # 去除所有空格 echo "$str" | sed 's/[[:space:]]//g' ``` ## 实际应用示例 ### 验证输入 ```bash # 验证邮箱格式 validate_email() { local email="$1" if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then echo "Valid email" return 0 else echo "Invalid email" return 1 fi } validate_email "user@example.com" ``` ### 格式化输出 ```bash # 格式化字符串 format_string() { local name="$1" local age="$2" local city="$3" printf "Name: %-20s Age: %3d City: %s\n" "$name" "$age" "$city" } format_string "John Doe" 25 "New York" ``` ### 批量重命名 ```bash # 批量修改文件扩展名 for file in *.txt; do new_name="${file%.txt}.bak" mv "$file" "$new_name" done ``` ### 日志解析 ```bash # 解析日志行 log_line="[2024-01-01 10:00:00] [INFO] User logged in" # 提取时间 time="${log_line#\[}" time="${time%%\]*}" echo "Time: $time" # 提取日志级别 level="${log_line#*\[}" level="${level%%\]*}" echo "Level: $level" # 提取消息 message="${log_line#*\] }" echo "Message: $message" ``` ## 字符串操作最佳实践 1. **始终使用引号**: 防止空格和特殊字符问题 2. **使用 ${} 而不是 $()**: 字符串操作更高效 3. **检查字符串长度**: 避免空字符串错误 4. **使用正则表达式验证输入**: 提高数据质量 5. **使用 printf 格式化输出**: 比 echo 更灵活 6. **注意大小写敏感**: 使用 ^ 和 , 进行转换 7. **使用数组存储分割结果**: 便于后续处理
服务端 · 3月6日 21:33
Shell 脚本中常用的循环结构有哪些?如何使用 for、while 和 until 循环?Shell 脚本中常用的循环结构包括 for 循环、while 循环和 until 循环。 ## for 循环 ### 基本语法 ```bash for variable in list do commands done ``` ### 遍历列表 ```bash # 遍历字符串列表 for fruit in apple banana orange do echo "Fruit: $fruit" done # 遍历数字序列 for i in 1 2 3 4 5 do echo "Number: $i" done # 使用 seq 命令生成序列 for i in $(seq 1 10) do echo "Number: $i" done # 使用 {start..end} 语法(Bash 3.0+) for i in {1..10} do echo "Number: $i" done # 指定步长 for i in {0..10..2} do echo "Number: $i" done ``` ### C 风格 for 循环 ```bash # C 风格语法 for (( i=0; i<10; i++ )) do echo "Number: $i" done # 多变量 for (( i=0, j=10; i<10; i++, j-- )) do echo "i=$i, j=$j" done ``` ### 遍历文件和目录 ```bash # 遍历当前目录下的文件 for file in *.txt do echo "Processing: $file" done # 遍历命令输出 for user in $(cut -d: -f1 /etc/passwd) do echo "User: $user" done # 递归遍历目录 for file in $(find . -name "*.sh") do echo "Shell script: $file" done ``` ## while 循环 ### 基本语法 ```bash while condition do commands done ``` ### while 循环示例 ```bash # 计数器 count=0 while [ $count -lt 5 ] do echo "Count: $count" count=$((count + 1)) done # 读取文件行 while IFS= read -r line do echo "Line: $line" done < input.txt # 读取用户输入 while true do read -p "Enter 'quit' to exit: " input if [ "$input" = "quit" ]; then break fi echo "You entered: $input" done # 等待进程结束 while ps -p $PID > /dev/null do echo "Process is running..." sleep 1 done echo "Process finished" ``` ## until 循环 ### 基本语法 ```bash until condition do commands done ``` ### until 循环示例 ```bash # 等待条件成立 count=0 until [ $count -ge 5 ] do echo "Count: $count" count=$((count + 1)) done # 等待服务启动 until curl -s http://localhost:8080 > /dev/null do echo "Waiting for service..." sleep 2 done echo "Service is ready" ``` ## 循环控制语句 ### break 语句 ```bash # 跳出循环 for i in {1..10} do if [ $i -eq 5 ]; then break fi echo "Number: $i" done # 输出: 1 2 3 4 ``` ### continue 语句 ```bash # 跳过当前迭代 for i in {1..10} do if [ $((i % 2)) -eq 0 ]; then continue fi echo "Odd number: $i" done # 输出: 1 3 5 7 9 ``` ### break 和 continue 指定层级 ```bash # 跳出多层循环 for i in {1..3} do for j in {1..3} do if [ $i -eq 2 ] && [ $j -eq 2 ]; then break 2 fi echo "i=$i, j=$j" done done ``` ## 实际应用示例 ```bash #!/bin/bash # 示例 1: 批量处理文件 echo "Processing text files..." for file in *.txt do if [ -f "$file" ]; then echo "Processing: $file" # 在这里添加处理逻辑 cp "$file" "backup_$file" fi done # 示例 2: 监控进程 echo "Monitoring process..." while true do if ps aux | grep -q "[m]yprocess"; then echo "Process is running" else echo "Process stopped" break fi sleep 5 done # 示例 3: 等待多个服务 services=("service1" "service2" "service3") for service in "${services[@]}" do echo "Waiting for $service..." until systemctl is-active --quiet "$service" do sleep 2 done echo "$service is ready" done # 示例 4: 读取配置文件 echo "Reading configuration..." while IFS='=' read -r key value do # 跳过注释和空行 [[ $key =~ ^#.*$ ]] && continue [[ -z $key ]] && continue echo "Config: $key = $value" done < config.txt # 示例 5: 并行处理 max_parallel=3 count=0 for task in task1 task2 task3 task4 task5 do # 启动后台任务 echo "Starting $task..." ( sleep 2 echo "$task completed" ) & count=$((count + 1)) # 控制并发数 if [ $((count % max_parallel)) -eq 0 ]; then wait fi done wait echo "All tasks completed" ``` ## 循环最佳实践 1. **使用引号包裹变量**: 防止空格和特殊字符问题 2. **设置 IFS 处理特殊分隔符**: `IFS= read -r line` 3. **使用 break 和 continue 控制循环**: 提高代码可读性 4. **避免无限循环**: 确保 while 和 until 循环有退出条件 5. **使用 wait 等待后台进程**: 确保所有任务完成 6. **优先使用 C 风格 for 循环**: 处理数字序列时更清晰 7. **使用数组遍历**: `for item in "${array[@]}"`
服务端 · 3月6日 21:33
Shell 脚本中的重定向和管道机制是什么?如何使用?Shell 脚本中的重定向和管道机制是进程间通信和数据流控制的重要方式。 ## 标准输入输出 ### 三种标准流 ```bash stdin (0) # 标准输入 - 默认从键盘读取 stdout (1) # 标准输出 - 默认输出到屏幕 stderr (2) # 标准错误 - 默认输出到屏幕 ``` ## 输出重定向 ### 重定向标准输出 ```bash # 覆盖重定向(>) echo "Hello" > file.txt # 创建或覆盖文件 ls -l > filelist.txt # 追加重定向(>>) echo "World" >> file.txt # 追加到文件末尾 date >> log.txt ``` ### 重定向标准错误 ```bash # 重定向错误输出 ls /nonexistent 2> error.log # 追加错误输出 ls /nonexistent 2>> error.log # 同时重定向输出和错误 command > output.txt 2>&1 command &> output.txt # Bash 4.0+ 简写 # 分别重定向 command > output.txt 2> error.txt ``` ### 丢弃输出 ```bash # 丢弃标准输出 command > /dev/null # 丢弃错误输出 command 2> /dev/null # 丢弃所有输出 command > /dev/null 2>&1 command &> /dev/null ``` ## 输入重定向 ### 从文件读取 ```bash # 从文件读取输入 wc -l < file.txt # 多行输入 cat << EOF > script.sh #!/bin/bash echo "Hello, World!" EOF ``` ### Here Document ```bash # 多行输入 cat << EOF This is line 1 This is line 2 This is line 3 EOF # 使用变量 name="John" cat << EOF Hello, $name! EOF # 禁用变量替换 cat << 'EOF' This is $name - not expanded EOF ``` ### Here String ```bash # 单行输入 wc -w <<< "Hello World" # 处理变量 text="Hello World" grep "World" <<< "$text" ``` ## 管道 ### 基本管道 ```bash # 将一个命令的输出作为另一个命令的输入 ps aux | grep nginx cat file.txt | grep "pattern" ls -l | sort -k5 -n # 多个管道连接 cat file.txt | grep "pattern" | wc -l ``` ### 管道与重定向结合 ```bash # 管道输出到文件 command | tee output.txt # 管道错误输出 command 2>&1 | grep "error" # 从管道读取输入 while read line; do echo "Line: $line" done < <(ls -l) ``` ## 进程替换 ### 基本语法 ```bash # 输出进程替换(<()) diff <(ls dir1) <(ls dir2) # 输入进程替换(>()) tar -cf >(gzip > archive.tar.gz) directory/ # 多个进程替换 paste <(cut -f1 file1) <(cut -f2 file2) ``` ### 实际应用 ```bash # 比较两个目录 diff <(ls dir1) <(ls dir2) # 合并多个文件 paste <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd) # 实时监控 tail -f /var/log/syslog | grep --line-buffered "ERROR" | tee errors.log ``` ## 高级重定向 ### 文件描述符操作 ```bash # 打开自定义文件描述符 exec 3> output.txt echo "Line 1" >&3 echo "Line 2" >&3 exec 3>&- # 从文件描述符读取 exec 4< input.txt read line <&4 echo "$line" exec 4<&- # 交换文件描述符 command 3>&1 1>&2 2>&3 ``` ### 同时读写 ```bash # 同时读写同一文件 exec 3<> file.txt read -u 3 line echo "New content" >&3 exec 3>&- ``` ## 实际应用示例 ### 日志处理 ```bash # 分离正常输出和错误输出 ./script.sh 1> success.log 2> error.log # 合并日志 ./script.sh > all.log 2>&1 # 实时监控日志 tail -f /var/log/app.log | grep --line-buffered "ERROR" | tee errors.log ``` ### 数据处理 ```bash # 处理 CSV 文件 cat data.csv | cut -d, -f1,3 | sort -u > output.txt # 统计信息 cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn > stats.txt # 批量处理 find . -name "*.txt" -exec cat {} \; | grep "pattern" > results.txt ``` ### 系统管理 ```bash # 备份重要命令输出 df -h > disk_usage_$(date +%Y%m%d).txt ps aux > process_list_$(date +%Y%m%d).txt # 监控系统 while true; do date >> monitor.log free -m >> monitor.log echo "---" >> monitor.log sleep 60 done ``` ### 脚本开发 ```bash # 捕获命令输出 result=$(command) echo "Result: $result" # 处理多行输出 while IFS= read -r line; do echo "Processing: $line" done < <(find . -name "*.sh") # 创建临时文件 tmpfile=$(mktemp) command > "$tmpfile" # 处理文件 rm -f "$tmpfile" ``` ## 最佳实践 1. **使用 /dev/null 丢弃不需要的输出**: 减少日志噪音 2. **分别处理 stdout 和 stderr**: 便于调试和日志分析 3. **使用管道组合命令**: 提高处理效率 4. **使用 tee 同时输出和保存**: 便于实时监控 5. **注意管道的缓冲**: 使用 `--line-buffered` 处理实时数据 6. **使用进程替换**: 避免创建临时文件 7. **及时关闭文件描述符**: 防止资源泄漏 8. **使用 mktemp 创建临时文件**: 避免文件名冲突
服务端 · 3月6日 21:33