第十章、认识与学习BASH 10.1 认识 BASH 这个 Shell 什么是 Shell Shell 是一个命令行解释器,它是用户与操作系统内核之间的接口。
主要功能:
命令执行 :解释用户输入的命令并执行
程序启动 :启动和管理程序
输入输出重定向 :控制数据流向
管道 :将一个命令的输出作为另一个命令的输入
变量和环境 :管理环境变量
脚本编程 :支持流程控制和函数
Shell 的种类 Linux 系统中有多种 Shell,常见的有:
Shell
说明
特点
Bash
Bourne-Again SHell
Linux 默认 Shell,功能强大,兼容 sh
sh
Bourne Shell
最经典的 Unix Shell,简洁快速
csh
C Shell
语法类似 C 语言
tcsh
Turbo C Shell
csh 的增强版
ksh
Korn Shell
商业 Unix 常用,功能强大
zsh
Z Shell
功能最丰富,可定制性强,推荐
fish
Friendly Interactive SHell
用户友好,有自动补全和语法高亮
查看和切换 Shell 查看当前使用的 Shell:
1 2 3 echo $SHELL echo $0 ps -p $$
查看系统安装的所有 Shell:
临时切换 Shell:
1 2 3 4 /bin/sh /bin/bash /bin/zsh exit
永久修改默认 Shell:
1 2 3 chsh chsh -s /bin/zsh chsh -s $(which zsh)
⚠️ 注意: 修改后需要重新登录才能生效。
Bash 的特性 Bash (Bourne-Again SHell) 是 Linux 系统默认的 Shell,具有以下特性:
1. 命令历史(History) 1 2 3 4 5 6 7 8 9 history history | tail -20 history -c !n !! !-n !string !?string ^old^new
2. 命令补全(Tab Completion)
按一次 Tab :自动补全命令或文件名
按两次 Tab :显示所有可能的补全选项
Alt+? :显示所有可能的补全
Alt+ *:插入所有可能的补全
3. 命令别名(Alias) 1 2 3 4 5 alias alias ll alias ll='ls -alF' alias rm ='rm -i' unalias ll
4. 输入输出重定向 1 2 3 4 5 6 7 8 9 10 11 12 13 > file >> file 2> file 2>> file &> file > file 2>&1 > /dev/null < file command << EOF # Here Document command <<< string # Here String
5. 管道(Pipe) 1 2 3 command1 | command2 command1 | command2 | command3 cat file | grep pattern | sort | uniq | wc -l
6. 变量和环境变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var=value export var=value export var echo $var echo ${var} echo ${var:-default} echo ${var:=default} $0 $1 , $2 , ... $# $@ $* $? $$ $! $_
7. 引号的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 echo "Hello $USER " echo "Today is $(date +%Y-%m-%d) " echo 'Hello $USER' echo 'Today is $(date)' echo "Today is `date +%Y-%m-%d`" \$ \\ \" \n \t
8. 通配符(Globbing) 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 ls *.txt ls file* ls *backup* ls file?.txt ls ???.txt ls file[0-9].txt ls file[abc].txt ls [A-Z]* ls file[!0-9].txt cp file{,.bak} mkdir -p project/{src,test ,docs}touch file{1..10}.txt echo {a,b,c}{1,2} shopt -s extglob ls !(*.txt) ls @(file1|file2).txt ls +(file).txt ls ?(file).txt ls *(file).txt shopt -s globstar ls **/*.txt
9. 进程控制 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 command command &Ctrl+Z jobs jobs -l bg %1 bg fg %1 fg wait PID wait kill PID kill -9 PID kill %1 killall process_name pkill pattern nohup command & disown %1
10. 命令组合与控制 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 command1; command2; command3 command1 && command2 mkdir test && cd test && touch file.txtcommand1 || command2 test -f file.txt || echo "File not found" command1 && command2 || command3 cat file | grep pattern | sort | uniq | wc -l(cd /tmp; ls ) (command1; command2) { command1; command2; } { cd /tmp; ls ; } command &(command1; command2) & { command1; command2; } &
Bash 配置文件 配置文件加载顺序 登录 Shell 的加载顺序:
1 2 3 4 5 6 7 8 9 10 11 12 13 登录 │ ├─ /etc/profile │ │ │ ├─ /etc/profile.d/*.sh │ │ │ └─ (~/.bash_profile | ~/.bash_login | ~/.profile) 按顺序第一个存在的 │ │ │ └─ ~/.bashrc │ │ │ └─ /etc/bash.bashrc (某些系统) │ └─ 登录完成
非登录交互式 Shell:
1 2 3 ~/.bashrc │ └─ /etc/bash.bashrc (某些系统)
非交互式 Shell(执行脚本):
主要配置文件说明 /etc/profile
系统全局配置文件,对所有用户生效
只在登录时加载一次
设置全局环境变量和启动程序
通常包含对 /etc/profile.d/ 的引用
~/.bash_profile, ~/.bash_login, ~/.profile
用户个人配置文件,按顺序加载第一个存在的
只在登录时加载
设置用户特定的环境变量
通常调用 ~/.bashrc
~/.bashrc
每次打开新的交互式 shell 时加载
设置命令别名、函数、提示符等
设置用户特定的环境变量
/etc/bash.bashrc 或 /etc/bashrc
系统级的 bashrc,对所有用户生效
在某些发行版中存在
/etc/profile.d/
目录,包含多个脚本文件
被 /etc/profile 调用
方便软件包添加自己的环境设置
配置文件示例 /etc/profile 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binexport EDITOR=vimexport PAGER=lessumask 022if [ -d /etc/profile.d ]; then for i in /etc/profile.d/*.sh; do if [ -r $i ]; then . $i fi done unset i fi if [ -x /usr/bin/figlet ]; then figlet "Welcome" fi
~/.bash_profile 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 if [ -f ~/.bashrc ]; then source ~/.bashrc fi export PATH=$PATH :$HOME /bin:$HOME /.local/binexport EDITOR=vimexport VISUAL=vimexport PAGER=lessexport LANG=en_US.UTF-8export LC_ALL=en_US.UTF-8if [ -z "$SSH_AUTH_SOCK " ]; then eval $(ssh-agent -s) ssh-add ~/.ssh/id_rsa fi
~/.bashrc 示例:
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 [ -z "$PS1 " ] && return export HISTSIZE=10000 export HISTFILESIZE=20000 export HISTCONTROL=ignoreboth:erasedups export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " shopt -s histappend shopt -s cmdhist alias ..='cd ..' alias ...='cd ../..' alias ....='cd ../../..' alias ll='ls -alF' alias la='ls -A' alias l='ls -CF' alias grep='grep --color=auto' alias fgrep='fgrep --color=auto' alias egrep='egrep --color=auto' alias rm ='rm -i' alias cp ='cp -i' alias mv ='mv -i' alias mkdir ='mkdir -pv' alias df ='df -h' alias du ='du -h' alias free='free -h' alias ps='ps auxf' alias top='htop' alias vim='nvim' alias upgrade='sudo apt update && sudo apt upgrade -y' extract () { if [ -f "$1 " ]; then case "$1 " in *.tar.bz2) tar xjf "$1 " ;; *.tar.gz) tar xzf "$1 " ;; *.tar.xz) tar xJf "$1 " ;; *.bz2) bunzip2 "$1 " ;; *.rar) unrar x "$1 " ;; *.gz) gunzip "$1 " ;; *.tar) tar xf "$1 " ;; *.tbz2) tar xjf "$1 " ;; *.tgz) tar xzf "$1 " ;; *.zip) unzip "$1 " ;; *.Z) uncompress "$1 " ;; *.7z) 7z x "$1 " ;; *) echo "Unknown format: '$1 '" ;; esac else echo "'\$1' is not a valid file" fi } mkcd () { mkdir -p "$1 " && cd "$1 " } up () { cd .. && ls } if [ -x /usr/bin/tput ] && tput setaf 1 &>/dev/null; then PS1='\[\e[01;32m\]\u@\h\[\e[00m\]:\[\e[01;34m\]\w\[\e[00m\]\$ ' else PS1='\u@\h:\w\$ ' fi shopt -s extglobshopt -s globstarshopt -s cdspellshopt -s checkwinsizeshopt -s globstar 2>/dev/nullif [ -f ~/.bash_local ]; then source ~/.bash_local fi echo "Welcome, $USER !" echo "Today is $(date '+%A, %B %d, %Y') " echo "System: $(uname -sr) " echo "Uptime: $(uptime -p 2>/dev/null || uptime) "
配置文件的加载顺序总结 1 2 3 4 5 6 7 8 9 10 11 12 13 登录 Shell: /etc/profile └── /etc/profile.d/*.sh ~/.bash_profile | ~/.bash_login | ~/.profile (第一个存在的) └── ~/.bashrc └── /etc/bash.bashrc (某些系统) 非登录交互式 Shell: ~/.bashrc └── /etc/bash.bashrc (某些系统) 非交互式 Shell (执行脚本): $BASH_ENV 指定的文件
最佳实践:
系统级环境变量 → /etc/profile
用户环境变量 → ~/.bash_profile 或 ~/.profile
交互式设置(别名、函数、提示符) → ~/.bashrc
在 ~/.bash_profile 中调用 ~/.bashrc 确保登录时也加载交互式设置
10.2 Shell 的变量功能 什么是变量 变量 是用于存储数据的命名内存空间。在 Shell 中,变量可以存储字符串、数字、命令输出等。
变量的分类
类型
说明
示例
局部变量
只在当前 Shell 中有效
var=value
环境变量
子进程也能访问
export var=value
位置参数
命令行参数
$1, $2, $@
特殊变量
Shell 内置的特殊变量
$?, $$, $#
只读变量
不能被修改
readonly var=value
变量的定义和使用 定义变量:
1 2 3 4 5 6 7 8 name="John Doe" age=25 pi=3.14159 name = "John" $name ="John"
使用变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 echo $name echo $age echo $pi echo "My name is ${name} " echo "PI = ${pi} " test_var="Hello" echo "$test_varworld " echo "${test_var} world"
环境变量 查看环境变量:
1 2 3 4 env printenv printenv PATH echo $PATH
设置环境变量:
1 2 3 4 5 6 7 export PATH=$PATH :/new/pathexport MY_VAR="my value" echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrcsource ~/.bashrc
常用环境变量:
变量名
说明
示例
PATH
可执行文件的搜索路径
/usr/bin:/bin:/usr/sbin
HOME
当前用户的主目录
/home/username
USER
当前用户名
john
SHELL
当前使用的 Shell
/bin/bash
LANG
系统语言设置
en_US.UTF-8
PWD
当前工作目录
/home/user/documents
OLDPWD
上一次所在的目录
/home/user
PS1
命令提示符
\u@\h:\w\$
PS2
续行提示符
>
EDITOR
默认编辑器
vim
PAGER
默认分页器
less
TERM
终端类型
xterm-256color
DISPLAY
X11 显示
:0
SSH_CLIENT
SSH 客户端信息
192.168.1.100 12345 22
SSH_TTY
SSH 分配的 TTY
/dev/pts/0
特殊变量
变量
说明
示例
$0
脚本名称或 Shell 名称
myscript.sh
$1 - $9
第1到第9个位置参数
arg1, arg2
${10} - ${n}
第10个及以后的位置参数
${10}, ${100}
$*
所有位置参数(作为单个字符串)
"$1 $2 $3"
$@
所有位置参数(作为多个字符串)
"$1" "$2" "$3"
$#
位置参数的数量
3
$?
上一个命令的退出状态码
0 表示成功
$$
当前 Shell 的进程ID
12345
$!
最近一个后台进程的PID
12346
$_
上一个命令的最后一个参数
或上一条命令本身
$-
当前 Shell 的标志位
himBH
位置参数示例:
1 2 3 4 5 6 7 8 9 10 #!/bin/bash echo "Script name: $0 " echo "First argument: $1 " echo "Second argument: $2 " echo "Tenth argument: ${10} " echo "All arguments (\$*): $*" echo "All arguments (\$@): $@ " echo "Number of arguments: $# "
运行:
1 ./test_args.sh arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10
$* 和 $@ 的区别:
1 2 3 4 5 6 7 8 9 10 11 12 #!/bin/bash echo 'Using $*:' for arg in "$*" ; do echo " [$arg ]" done echo 'Using $@:' for arg in "$@ " ; do echo " [$arg ]" done
运行:
1 2 3 4 5 6 7 8 ./difference.sh "hello world" foo bar
变量的操作 删除变量:
1 2 3 unset var unset -v var unset -f function_name
只读变量:
1 2 3 4 readonly var=value readonly var readonly -p
本地变量:
1 2 local var=value local var
默认值:
1 2 3 4 ${var:-default} ${var:=default} ${var:?message} ${var:+replacement}
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/bin/bash name=${1:-"World"} echo "Hello, $name !" config_file=${CONFIG_FILE:="/etc/default.conf"} echo "Config: $config_file " target_dir=${TARGET_DIR:?"Error: TARGET_DIR is not set"} debug_mode=${DEBUG:+"--debug"}
字符串操作:
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 ${#var} ${var:position} ${var:position:length} ${var#pattern} ${var%pattern} ${var##pattern} ${var%%pattern} ${var/pattern/replacement} ${var//pattern/replacement} ${var/#pattern/replacement} ${var/%pattern/replacement} ${var^^} ${var,,} ${var^} ${var,} ${var^^pattern} ${var,,pattern}
示例:
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 path="/home/user/documents/file.txt" echo "Length: ${#path} " echo "${path:5} " echo "${path:5:4} " echo "${path: -8} " echo "${path%/*} " echo "${path##*/} " echo "${path/file/document} " echo "${path//n/N} " str="Hello World" echo "${str^^} " echo "${str,,} " echo "${str^} "
数组 定义数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 arr=(value1 value2 value3) arr[0]=value1 arr[1]=value2 arr[2]=value3 arr=(value1 [3]=value4 value5) arr=($(ls *.txt)) arr=($(cat file.txt)) read -a arr <<< "a b c"
访问数组元素:
1 2 3 4 5 6 7 8 9 10 arr=(apple banana cherry date ) echo "${arr[0]} " echo "${arr[1]} " echo "${arr[-1]} " echo "${arr[-2]} " echo "${arr[@]} " echo "${arr[*]} " echo "${#arr[@]} "
数组操作:
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 arr=(a b c d e) echo "${arr[@]:1} " echo "${arr[@]:1:3} " echo "${arr[@]: -2} " arr+=(f g) arr=(x "${arr[@]} " ) arr=("${arr[@]:0:2} " new "${arr[@]:2} " ) unset arr[1] arr=("${arr[@]/b/} " ) arr=("${arr[@]/old/new} " ) for item in "${arr[@]} " ; do echo "$item " done for i in "${!arr[@]} " ; do echo "$i : ${arr[$i]} " done i=0 while [ $i -lt ${#arr[@]} ]; do echo "${arr[$i]} " ((i++)) done
关联数组(Bash 4.0+):
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 declare -A assocassoc[name]="John" assoc[age]=30 assoc[city]="New York" echo "${assoc[name]} " echo "${assoc[age]} " echo "${!assoc[@]} " echo "${assoc[@]} " echo "${#assoc[@]} " for key in "${!assoc[@]} " ; do echo "$key : ${assoc[$key]} " done if [ -v assoc[name] ]; then echo "Key exists" fi unset assoc[age]
10.3 数据流重定向 什么是数据流重定向 在 Linux 中,程序运行时会产生三种数据流:
数据流
文件描述符
默认指向
说明
标准输入 (stdin)
0
键盘
程序读取输入的地方
标准输出 (stdout)
1
屏幕
程序正常输出的地方
标准错误 (stderr)
2
屏幕
程序报错的地方
数据流重定向 就是改变这些数据的默认流向。
输出重定向 覆盖输出到文件:
1 2 3 4 command > file command 1> file command >> file command 1>> file
错误输出重定向:
1 2 command 2> file command 2>> file
同时重定向标准输出和标准错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 command > stdout.log 2> stderr.logcommand > file 2>&1command > file 2>&1command &> file command &>> file command > /dev/null command 2> /dev/null command > /dev/null 2>&1 command &> /dev/null
输入重定向 从文件输入:
1 2 3 command < file cat < file.txt sort < input.txt
Here Document(多行输入):
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 command << EOF line 1 line 2 line 3 EOF cat << END This is a multi-line text. It will be displayed as is. Variables like $HOME will be expanded. END cat << 'EOF' $HOME will NOT be expanded.It is displayed literally: $HOME EOF cat <<- EOF This line has a tab. This line also has a tab. The tabs will be removed. EOF
Here String(单行输入):
1 2 3 4 5 6 command <<< "string" bc <<< "1 + 2" grep pattern <<< "text to search" awk '{print $1}' <<< "one two three"
管道 管道 | 将一个命令的标准输出连接到另一个命令的标准输入。
基本用法:
1 2 command1 | command2 command1 | command2 | command3
常用组合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ls -la | sort ls | wc -lcat /var/log/syslog | grep errorcat /var/log/syslog | grep error | head -20ps aux | grep nginx cat file.txt | sort | uniq cat file.txt | sort | uniq -c | sort -nrps aux | grep "[n]ginx" | awk '{print $2}' | xargs kill -9
管道与重定向结合:
1 2 3 4 5 6 7 8 9 10 11 12 13 ls -la | sort > sorted.txtcommand 2>&1 | next_commandcommand | tee output.txt command | tee -a output.txt command | tee file1.txt file2.txt cat input.txt | tee /dev/tty | wc -l
高级重定向 进程替换:
1 2 3 4 5 6 7 8 9 10 11 command <(list)diff <(ls dir1) <(ls dir2) cat <(echo "Header" ) <(main_content.txt) <(echo "Footer" )comm -12 <(sort file1) <(sort file2)
文件描述符操作:
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 exec 3> output.txt exec 4< input.txt exec 5>> log.txt echo "Hello" >&3read line <&4echo "Log entry" >&5exec 3>&-exec 4<&-exec 3>&1 exec 1> output.txt exec 1>&3exec 3>&-exec 3>&1 4>&2 exec 1> >(stdbuf -oL tee stdout.log) exec 2> >(stdbuf -eL tee stderr.log) exec 1>&3 2>&4exec 3>&- 4>&-
本章重点总结
Shell 是用户与内核的接口,Bash 是 Linux 默认 Shell
变量 分为局部变量、环境变量、位置参数、特殊变量
环境变量 PATH 决定命令搜索路径
数据流 :stdin(0)、stdout(1)、stderr(2)
重定向 :> 覆盖、>> 追加、2> 错误重定向、2>&1 合并
管道 | 连接多个命令
Here Document << 用于多行输入
配置文件 :/etc/profile、~/.bash_profile、~/.bashrc
特殊变量 :$? 退出状态、$$ 进程ID、$# 参数个数
引号 :双引号允许变量扩展,单引号原样输出
10.4 命令执行的判断依据 顺序执行与逻辑控制 在 Shell 中,多个命令可以通过特定符号组合执行,实现流程控制。
分号 ; - 顺序执行 1 command1; command2; command3
特点:
无论前一个命令是否成功,都执行后一个命令
命令按顺序依次执行
退出状态是最后一个命令的退出状态
示例:
1 2 3 4 5 6 7 8 9 10 11 echo "Step 1" ; echo "Step 2" ; echo "Step 3" ./configure; make; sudo make install cd /data; tar -czf backup.tar.gz important_files; mv backup.tar.gz /backup/process_data; cleanup_temp_files
&& - 逻辑与1 command1 && command2 && command3
特点:
前一个命令成功 (退出状态为0)时,才执行后一个命令
如果某个命令失败,后续的命令不会执行
常用于条件判断
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 mkdir -p /tmp/test && cd /tmp/testping -c 1 server.com && scp file.txt server.com:/backup/ ./configure && make && sudo make install [ -f data.txt ] && process_data.sh command1 && { echo "Command 1 succeeded" command2 command3 } check_important "file.txt" && rm "file.txt" connect_db && run_migrations && restart_server
|| - 逻辑或1 command1 || command2 || command3
特点:
前一个命令失败 (退出状态非0)时,才执行后一个命令
如果某个命令成功,后续的命令不会执行
常用于提供备选方案或错误处理
示例:
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 [ -d /tmp/test ] || mkdir /tmp/test [ -f package.tar.gz ] || wget http://example.com/package.tar.gz download_from_mirror1 || download_from_mirror2 || download_from_mirror3 process_data || { echo "Error: Failed to process data" >&2 exit 1 } username=${1:-$(whoami)} || username="anonymous" which git || echo "Please install git" start_service || restart_service || notify_admin load_local_config || load_global_config || load_default_config backup_database || { logger -t backup "Database backup failed" send_alert_email exit 1 }
组合使用 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 command && success_action || failure_actionif command ; then success_action else failure_action fi code command && { success_action; true ; } || { failure_action; }command1 && command2 && command3 && final_action command1 || command2 || command3 || final_action (command1 && command2) || (command3 && command4) || fallback [ -f Makefile ] && make || [ -f CMakeLists.txt ] && cmake . load_config_from ~/.app/config \ || load_config_from /etc/app/config \ || load_default_config run_tests && build_package && deploy_to_staging \ || notify_deployment_failure [ -w "$file " ] && [ ! -d "$file " ] && rm "$file " \ || echo "Cannot remove $file " >&2 pgrep mysqld && mysqladmin ping || systemctl restart mysql
退出状态码 每个命令执行后都会返回一个退出状态码 (exit status),可以用 $? 获取:
状态码
含义
0
成功
1
通用错误
2
误用命令(Bash内置命令)
126
命令不可执行
127
命令未找到
128
无效退出参数
128+N
致命信号 N(如 130 = Ctrl+C)
130
脚本被Ctrl+C终止
255
退出状态码超出范围
查看退出状态:
1 2 3 4 5 6 7 8 ls /existing/direcho $? ls /nonexistentecho $? command_not_found echo $?
在脚本中使用退出状态:
1 2 3 4 5 6 7 8 9 10 #!/bin/bash if ping -c 1 google.com > /dev/null 2>&1; then echo "Network is up" exit 0 else echo "Network is down" exit 1 fi
自定义退出状态:
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 readonly SUCCESS=0readonly ERROR_GENERAL=1readonly ERROR_INVALID_ARGS=2readonly ERROR_FILE_NOT_FOUND=3readonly ERROR_PERMISSION=4if [ $# -eq 0 ]; then echo "Usage: $0 <file>" >&2 exit $ERROR_INVALID_ARGS fi if [ ! -f "$1 " ]; then echo "Error: File '$1 ' not found" >&2 exit $ERROR_FILE_NOT_FOUND fi if [ ! -r "$1 " ]; then echo "Error: Cannot read '$1 '" >&2 exit $ERROR_PERMISSION fi process_file "$1 " exit $SUCCESS
本章总结
Shell 是命令解释器 ,Bash 是 Linux 默认 Shell
变量 :局部变量、环境变量、位置参数、特殊变量
引号 :双引号允许变量扩展,单引号原样输出,反引号执行命令
重定向 :> 覆盖、>> 追加、2> 错误、2>&1 合并
管道 | 连接多个命令,实现数据流传递
命令分隔符 :; 顺序执行、&& 逻辑与、|| 逻辑或
退出状态码 :0 表示成功,非 0 表示失败,存储在 $? 中
数组 :支持索引数组和关联数组(Bash 4.0+)
配置文件 :/etc/profile、~/.bash_profile、~/.bashrc 等的加载顺序
字符串操作 :提取子串、删除匹配、替换、大小写转换等
10.5 管道命令 管道命令| 是 Linux Shell 中非常强大的功能,它允许将一个命令的输出直接作为另一个命令的输入,实现数据的流水线处理。
管道的工作原理 1 2 3 4 命令1 | 命令2 | 命令3 | ... | 命令N ↓ ↓ ↓ ↓ 输出1 输入2 输出2 最终输出 输入3
关键点:
管道连接的是标准输出 到标准输入
管道中的每个命令在子shell 中执行
管道可以包含任意数量的命令
常用管道命令 1. cut - 切割提取 从每行中提取指定部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cut -c 1-5 file.txt cut -c 1,3,5 file.txt cut -c 5- file.txt cut -f 1 file.txt cut -f 1,3 file.txt cut -f 2-4 file.txt cut -d ':' -f 1 /etc/passwd cut -d ',' -f 2 data.csv cut --complement -f 1 file.txt
管道示例:
1 2 3 4 5 6 7 8 9 10 11 grep "bin/bash" /etc/passwd | cut -d ':' -f 1 ifconfig eth0 | grep "inet " | cut -d ' ' -f 10 ps aux | grep nginx | grep -v grep | cut -c 10-15 cat data.csv | cut -d ',' -f 1,3,5 | sort
2. grep - 文本过滤 使用正则表达式搜索文本:
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 grep pattern file.txt grep pattern dir /*.txt cat file.txt | grep pattern -i -v -n -c -l -L -r -w -x -A 3 -B 3 -C 3 -E -F -o -q --color=auto grep "^start" file grep "end$" file grep "a.b" file grep "a*b" file grep "a\+b" file grep "a\?b" file grep "[abc]" file grep "[^abc]" file grep "\{3\}" file grep -E "a{2,4}" file
管道示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ps aux | grep nginx | grep -v grep ps aux | grep "[h]ttpd" | wc -l cat /var/log/syslog | grep -i error | tail -20ps aux | awk '{print $2, $11}' | grep chrome | grep -v grep | awk '{print $1}' du -ah /home | grep -E "^[0-9]+M|^[0-9]+G" | sort -hr | head -10ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' cat file.txt | tr ' ' '\n' | sort | uniq -c | sort -nr | head -20
3. sort - 文本排序 对文本行进行排序:
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 sort file.txt cat file.txt | sort -r -n -k 2 -k 2,3 -t ':' -f -u -c -C -m -s -b -h --version-sort echo -e "10\n2\n1\n20" | sort -ncat data.txt | sort -k 3 -n ps aux | sort -k 3 -nr | head -5
管道示例:
1 2 3 4 5 6 7 8 9 10 11 cat file.txt | sort -udu -sh /home/* | sort -hps aux | sort -k 4 -nr | head -10 ps aux | grep -E "(nginx|php)" | sort -k 3 -nr | awk '{print $2, $11}'
4. uniq - 去重 过滤或报告重复的行(需要先排序):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 sort file.txt | uniq -c -d -u -i -f N -s N -w N --all-repeated=METHOD sort file.txt | uniq -c | sort -nr sort file.txt | uniq -dsort file.txt | uniq -u
管道示例:
1 2 3 4 5 6 7 8 9 10 11 awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -20 cat file.txt | tr ' ' '\n' | sort | uniq -c | sort -nr | head -20find /path -type f -exec md5sum {} \; | sort | uniq -d -w32 ps aux | awk '{print $11}' | sort | uniq -c | sort -nr
5. wc - 统计 统计行数、单词数、字节数:
1 2 3 4 5 6 7 8 9 10 11 12 13 wc file.txt wc -l file.txt wc -w file.txt wc -c file.txt wc -m file.txt wc -L file.txt wc file1.txt file2.txtls | wc -l grep pattern file | wc -l
管道示例:
1 2 3 4 5 6 7 8 9 10 11 find . -name "*.py" | xargs wc -l ps aux | wc -l who | wc -lawk '{print $1}' access.log | sort | uniq | wc -l
6. tee - 双向输出 同时将输出显示在屏幕并保存到文件:
1 2 3 4 5 6 7 8 9 10 11 12 command | tee file.txt command | tee -a file.txt command | tee -i file.txt command | tee file1.txt file2.txt file3.txtecho "content" | sudo tee /etc/config.confcat file.txt | tee /dev/tty | wc -l
7. tr - 字符转换 转换或删除字符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 echo "hello" | tr 'a-z' 'A-Z' echo "HELLO" | tr 'A-Z' 'a-z' tr -d 'a' < file.txt tr -d '0-9' < file.txt tr -s ' ' < file.txt echo "hello world" | tr ' ' '_' tr -cd 'a-zA-Z' < file.txtcat file.txt | tr '\n' ' ' echo "hello" | tr 'A-Za-z' 'N-ZA-Mn-za-m' echo "uryyb" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
8. split - 文件分割 将大文件分割成多个小文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 split -l 1000 large.txt part_ split -b 10M large.bin part_ split -b 1G large.iso part_ split -l 100 large.txt output_split -l 100 -d large.txt part_split -l 100 -d -a 4 large.txt part_cat part_* > original_filetar czf - large_dir | split -b 100M - large_dir.tar.gz.part_ cat large_dir.tar.gz.part_* | tar xzf -
管道命令总结
命令
功能
常用选项
cut
按列提取
-d, -f, -c
grep
文本过滤
-i, -v, -n, -r
sort
文本排序
-n, -r, -k, -t
uniq
去重
-c, -d, -u
wc
统计
-l, -w, -c
tee
双向输出
-a, -i
tr
字符转换
-d, -s
split
文件分割
-l, -b
管道实战示例 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 cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10du -ah /home | grep -E "^[0-9]+(\.[0-9]+)?G" | sort -hr | head -10find . -name "*.py" -o -name "*.js" -o -name "*.java" | xargs wc -l | tail -1 ls *.bak | while read f; do mv "$f " "${f%.bak} " ; done ps aux | grep "[n]ginx" | awk '{print $2}' | xargs kill -9 awk '{print $4}' error.log | cut -d: -f1 | sort | uniq -c cat file1.txt file2.txt file3.txt | sort | uniq > merged.txtgrep -oE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' file.txt | sort -u comm <(sort file1.txt) <(sort file2.txt)diff file1.txt file2.txt tail -f access.log | grep --line-buffered "500" | tee errors.txt
10.6 重点回顾
Shell 是命令解释器,Bash 是 Linux 默认 Shell
变量 :定义 var=value,使用 $var 或 ${var}
环境变量 :export var=value,子进程可继承
特殊变量 :$? 退出状态、$$ 进程ID、$# 参数个数、$@ 所有参数
数据流 :stdin(0)、stdout(1)、stderr(2)
重定向 :> 覆盖、>> 追加、2> 错误重定向、2>&1 合并到stdout
管道 | 将一个命令的输出作为另一个命令的输入
命令组合 :; 顺序执行、&& 逻辑与、|| 逻辑或
引号 :双引号允许变量扩展,单引号原样输出
配置文件加载顺序 :/etc/profile → ~/.bash_profile → ~/.bashrc
常用管道命令 :cut、grep、sort、uniq、wc、tee、tr
数组 :索引数组 arr=(a b c),关联数组 declare -A assoc
字符串操作 :长度 ${#var}、提取 ${var:pos:len}、替换 ${var/old/new}
10.7 本章习题
什么是 Shell?Shell 和 Bash 有什么关系?
定义一个变量 myvar="Hello",然后输出它的值。请用三种不同的方式引用这个变量。
解释 $?、$$、$#、$@、$* 的含义和区别。
编写一个脚本,接受两个参数,输出它们的和。如果没有提供参数,使用默认值 0。
解释以下命令的区别:
echo $HOME
echo "$HOME"
echo '$HOME'
echo \$HOME
如何将一个命令的输出保存到文件?如何追加到文件?如何将错误信息也保存到文件?
解释 command1 && command2 || command3 的执行逻辑。这种写法有什么潜在问题?
使用管道组合命令,找出当前目录下占用空间最大的5个文件。
编写一个脚本,从 /etc/passwd 中提取用户名、UID 和 Shell,按 UID 排序后显示。
解释以下配置文件的作用和加载顺序:/etc/profile、~/.bash_profile、~/.bashrc、/etc/bash.bashrc。
10.8 参考资料与延伸阅读