飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 基础入门

    • Linux 命令概述与环境准备
    • 文件与目录操作(上):ls、cd、pwd
    • 文件与目录操作(下):cat、touch、mkdir、rm、cp、mv
    • 获取帮助与基础工具:man、help、clear、exit、sudo
  • 文件权限

    • 文件权限管理:chmod、chown
  • 系统与监控

    • 进程管理与系统监控:ps、top、kill、df、du、free
    • 用户与环境:whoami、id、env、echo、history
  • 网络命令

    • 网络诊断(上):ping、curl、wget
    • 网络诊断(下):netstat、ss、ip
    • 网络抓包与高级工具:traceroute、tcpdump、nc
    • DNS 与防火墙:nslookup、dig、iptables
  • 文本处理

    • 文本搜索:grep
    • 文本处理(上):awk
    • 文本处理(下):sed
    • 文本排序与统计:sort、uniq、wc
    • 文件查看与查找:head、tail、cut、find
    • 归档压缩与管道组合:tar、gzip、xargs
  • 开发工具

    • 开发工具链(上):gcc、make
    • 开发工具链(下):gdb、time
    • 版本控制:git
    • 远程操作:ssh、scp
    • 编辑器:vim、nano
  • Shell 编程

    • Shell 编程基础(上):变量、管道、重定向
    • Shell 编程基础(下):条件、循环、函数
  • 综合实战

    • 综合实战:日志分析

Shell 编程基础(上):变量、管道、重定向

Shell 脚本基础

Shell 脚本是把多个命令写入文件,赋予执行权限后批量运行的程序。它是 Linux 自动化的核心工具。

#!/bin/bash
# 上面这行叫 Shebang,指定用 /bin/bash 解释执行

echo "Hello, Shell Script!"

变量

定义和使用

#!/bin/bash

# 定义变量(等号两边不能有空格!)
name="John"
age=25

# 使用变量(前面加 $)
echo "Name: $name"
echo "Age: $age"

# 使用变量(推荐加花括号,避免歧义)
echo "Hello, ${name}!"

# 只读变量(readonly)
readonly PI=3.14159
# PI=3.14  # 错误!不能修改

# 删除变量(不能删除只读变量)
unset name

特殊变量

变量含义示例
$0脚本名./script.sh
$1-$9第 1-9 个参数$1 是第一个参数
$#参数个数echo $#
$*所有参数(作为一个字符串)"$*" = "$1 $2 $3"
$@所有参数(作为多个字符串)"$@" = "$1" "$2" "$3"
$$当前进程 PIDecho $$
$?上一个命令的退出状态echo $?
$_上一个命令的最后一个参数echo $_
#!/bin/bash
# script.sh

echo "脚本名: $0"
echo "第 1 个参数: $1"
echo "第 2 个参数: $2"
echo "参数个数: $#"
echo "所有参数: $@"
echo "当前进程 PID: $$"

# 执行:./script.sh arg1 arg2
# 输出:
# 脚本名: ./script.sh
# 第 1 个参数: arg1
# 第 2 个参数: arg2
# 参数个数: 2
# 所有参数: arg1 arg2
# 当前进程 PID: 12345

环境变量 vs 局部变量

#!/bin/bash

# 局部变量(仅在当前 Shell/脚本有效)
local_var="I am local"

# 环境变量(子进程也能访问)
export GLOBAL_VAR="I am global"

# 查看所有环境变量
env

# 查看特定环境变量
echo $PATH
echo $HOME
echo $USER

管道(Pipe)

管道 | 把前一个命令的标准输出作为后一个命令的标准输入,是 Linux 哲学的核心。

# 基本管道
cat file.txt | grep "error"

# 多个管道串联
ps aux | grep nginx | grep -v grep | awk '{print $2}'

# 统计单词数
cat file.txt | wc -w

# 排序去重
cat file.txt | sort | uniq -c | sort -rn

管道实战

#!/bin/bash

# 场景 1:查找占用 CPU 最高的 5 个进程
ps aux | sort -k3 -rn | head -n 6 | tail -n 5

# 场景 2:统计当前目录下每种文件类型的数量
ls -l | awk '{print $1}' | sort | uniq -c | sort -rn

# 场景 3:查找最大的 10 个文件
find . -type f -exec ls -lh {} + | awk '{print $5, $9}' | sort -rh | head -n 10

# 场景 4:统计日志中每个 IP 的访问次数
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -n 10

重定向

重定向改变命令的标准输入/输出/错误的目标。

标准流

流文件描述符默认目标符号
标准输入(stdin)0键盘<
标准输出(stdout)1终端> 或 1>
标准错误(stderr)2终端2>

输出重定向

#!/bin/bash

# 覆盖写入(>)
echo "Hello" > file.txt

# 追加写入(>>)
echo "World" >> file.txt

# 重定向标准错误(2>)
ls /notexist 2> error.log

# 同时重定向 stdout 和 stderr
ls /exist /notexist > output.log 2> error.log

# 合并 stderr 到 stdout(2>&1)
ls /exist /notexist > all.log 2>&1
# 或简写
ls /exist /notexist &> all.log

# 丢弃输出(重定向到 /dev/null)
command > /dev/null 2>&1

输入重定向

#!/bin/bash

# 从文件读取输入(<)
wc -l < file.txt

# 等价于
cat file.txt | wc -l

# Here Document(多行输入)
cat << EOF
This is line 1
This is line 2
EOF

# Here String(单行输入)
wc -w <<< "Hello World Linux"

重定向实战

#!/bin/bash

# 场景 1:运行程序,保存输出和错误
./program > output.log 2> error.log

# 场景 2:运行程序,合并输出和错误
./program > all.log 2>&1

# 场景 3:运行程序,丢弃所有输出
./program > /dev/null 2>&1

# 场景 4:同时输出到屏幕和文件(tee)
./program | tee output.log

# 场景 5:追加到日志
./program | tee -a log.txt

命令替换

命令替换把命令的输出作为另一个命令的参数。

#!/bin/bash

# 反引号方式(旧,不推荐)
echo "Today is `date +%Y-%m-%d`"

# $() 方式(推荐,可嵌套)
echo "Today is $(date +%Y-%m-%d)"

# 嵌套命令替换
echo "Files: $(ls $(pwd))"

# 把命令输出赋值给变量
current_date=$(date +%Y-%m-%d)
echo "Date: $current_date"

# 计算文件数量
file_count=$(ls | wc -l)
echo "Files in current directory: $file_count"

本篇小结

  • Shell 脚本第一行是 Shebang(#!/bin/bash),指定解释器
  • 变量定义:name="value"(等号两边不能有空格),使用:$name 或 ${name}
  • 特殊变量:$0 脚本名,$1-$9 参数,$# 参数个数,$@ 所有参数,$$ PID,$? 退出状态
  • export 定义环境变量,子进程可访问
  • 管道 |:前一个命令的 stdout 作为后一个命令的 stdin,可串联多个命令
  • 重定向:> 覆盖输出,>> 追加输出,2> 错误输出,2>&1 合并错误到输出,< 输入重定向
  • /dev/null:黑洞设备,丢弃输出;tee:同时输出到屏幕和文件
  • 命令替换:$(command) 或反引号,把命令输出作为参数或赋值给变量

动手实践

  1. 创建第一个脚本:

    cat > hello.sh
    #!/bin/bash
    echo "Hello, $USER!"
    echo "Today is $(date)"
    echo "Current directory: $(pwd)"
    # Ctrl+D
    
    chmod +x hello.sh
    ./hello.sh
    
  2. 带参数的脚本:

    cat > greet.sh
    #!/bin/bash
    echo "Hello, $1!"
    echo "You are $2 years old."
    echo "Total arguments: $#"
    echo "All arguments: $@"
    # Ctrl+D
    
    chmod +x greet.sh
    ./greet.sh Alice 25
    
  3. 管道练习:

    # 统计 /bin 下有多少命令
    ls /bin | wc -l
    
    # 查找当前目录下最大的 5 个文件
    ls -lh | sort -k5 -rh | head -n 6 | tail -n 5
    
    # 统计 /etc/passwd 中每种 Shell 的用户数
    awk -F: '{print $7}' /etc/passwd | sort | uniq -c | sort -rn
    
  4. 重定向练习:

    echo "Test" > test.txt
    echo "Append" >> test.txt
    cat test.txt
    
    ls /notexist 2> error.log
    cat error.log
    
    ls /tmp /notexist > out.log 2>&1
    cat out.log
    
    ls /tmp /notexist > /dev/null 2>&1
    echo "Silent execution done"
    
  5. 命令替换练习:

    echo "Current date: $(date)"
    files=$(ls)
    echo "Files: $files"
    count=$(ls | wc -l)
    echo "File count: $count"
    
  6. 思考:为什么 name = "value"(等号两边有空格)会报错?Shell 是如何解析这个语句的?2>&1 和 1>&2 有什么区别?

下一页
Shell 编程基础(下):条件、循环、函数