Shell笔记

0 阅读5分钟

比较符

英文短语的缩写,含义清晰:

操作符英文全称中文含义
-eqequal等于
-nenot equal不等于
-ltless than小于
-leless or equal小于等于
-gtgreater than大于
-gegreater or equal大于等于

环境变量

环境变量描述
BASHPIDBash 进程的进程 ID。
EDITOR默认的文本编辑器。
HOME用户的主目录。
HOST当前主机的名称。
LANG字符集以及语言编码,比如 zh_CN.UTF-8。
PATH由冒号分开的目录列表,当输入可执行程序名后,会搜索这个目录列表。
PWD当前工作目录。
SHELLShell 的名字。
TERM终端类型名,即终端仿真器所用的协议。
UID当前用户的 ID 编号。
USER当前用户的用户名。

文件测试参数

参数说明
-e文件存在(最常用)
-f文件存在且是普通文件(不是目录或链接)
-d文件存在且是目录
-r文件存在且可读
-w文件存在且可写
-x文件存在且可执行

变量

在 Linux Shell(主要是 Bash)中,变量的使用非常灵活,但有一个核心规则必须记住:赋值时不能有空格,引用时要加 $ 符号。

定义变量

语法:变量名=值;关键点:等号 = 两边绝对不能有空格。

# ✅ 正确写法
name="Alice"
age=25
url=https://www.example.com
message='Hello World'

# ❌ 错误写法 (会报错:command not found)
# name = "Alice"   (空格会导致 shell 把 name 当作命令执行)
# age= 25

引号的使用:"允许变量替换和命令替换(推荐常用)。

greeting="Hello, $name"  # 这里的 $name 会被解析为 Alice

单引号:'不进行任何替换,内容原样输出。

greeting='Hello, $name'  # 输出就是字面量 "Hello, $name"

无引号:如果值中没有空格或特殊字符,可以不加引号;如果有空格,必须加引号。

city=Beijing      # ✅ 可行
city="New York"   # ✅ 必须加引号,否则 shell 会把 York 当作另一个参数

使用变量

语法:$变量名${变量名}

name="Bob"
# 方法 1:直接加 $
echo $name
# 输出: Bob
# 方法 2:加大括号 (推荐,更清晰,避免歧义)
echo ${name}
# 输出: Bob

为什么要用 ${};当变量名后面紧跟着其他字符时,必须用大括号区分。

file="report"
# ❌ 错误:shell 会尝试找名为 filetxt 的变量
echo $filetxt 
# ✅ 正确:明确告诉 shell 变量名是 file,后面紧跟 txt
echo ${file}txt
# 输出: report.txt

特殊变量

Shell 内置了一些特殊变量,用于脚本控制:

变量含义示例
$0当前脚本的名称./myscript.sh
$1, $2...第 1、第 2 个位置参数./script.sh arg1 arg2 ($1是arg1)
$#传递给脚本的参数个数echo $#
$?上一个命令的退出状态码 (0 表示成功,非 0 表示失败)ls /tmp; echo $?
$$当前 Shell 进程的 PID
$HOME当前用户的主目录
$PATH可执行程序的搜索路径

变量的运算

Shell 默认将变量视为字符串,进行数学运算需要特殊语法。

  1. 整数运算:使用 (( ))$[ ]expr (推荐 (( ))):
a=10
b=5
# 方法 1:双括号 (推荐)
sum=$((a + b))
echo $sum  # 输出: 15
# 方法 2:let 命令
let c=a*b
echo $c    # 输出: 50
  1. 字符串拼接:直接连写即可。
first="Hello"
second="World"
full="${first} ${second}"
echo $full  # 输出: Hello World
  1. 删除变量:使用 unset 命令
my_var="test"
echo $my_var  # 输出: test

unset my_var
echo $my_var  # 输出: (空)
  1. 环境变量 vs 局部变量 局部变量:只在当前 Shell 会话或脚本中有效。
name="Alice"
  • 环境变量:可以传递给子进程(如在脚本中调用的其他命令)。需要使用 export
export NAME="Alice"
# 或者
NAME="Alice"
export NAME

实战示例脚本:创建一个名为 test_var.sh 的文件:

#!/bin/bash

# 1. 定义变量
user="Root"
count=1
log_file="/var/log/syslog"

# 2. 使用变量
echo "当前用户: $user"
echo "登录次数: $((count + 5))"  # 运算

# 3. 检查上一个命令是否成功
ls /etc/hosts
if [ $? -eq 0 ]; then
    echo "文件存在 (退出码: $?)"
else
    echo "文件不存在"
fi

# 4. 使用位置参数 (运行脚本时传入)
# 运行方式: ./test_var.sh param1 param2
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "参数总数: $#"

# 5. 字符串拼接
full_path="${HOME}/projects/${user}_data"
echo "完整路径: $full_path"

常见坑点总结

  1. 空格陷阱:var = 1 是错的,必须是 var=1
  2. 引用陷阱:echo $var 如果 var 里有空格,最好写成 echo "$var",否则单词会被拆分。
  3. 只读变量:可以用 readonly var_name 锁定变量,防止被修改。
  4. 命令输出赋值:可以使用反引号 `cmd`$(cmd)
current_date=$(date)
file_list=$(ls -l)

if条件判断

if [ 条件测试 ] # 条件测试左右必须要有空格
then
	...
fi # 结束符

或者

if [ 条件测试 ]; then
	...
fi  
if [ 条件测试 ]
then
	...
else
	...
fi
if [ 条件表达式 ]; then
    # 条件成立时执行的命令
elif [ 另一个条件表达式 ]; then
    # 另一个条件成立时执行的命令
else
    # 以上条件都不成立时执行的命令
fi
  1. 判断数字大小与文件属性
#!/bin/bash
# 定义变量
num=20
file_name="v1.txt"
echo "### 开始执行 if 语句演示 ###"

# 1. 数值比较
# -eq 表示等于
if [ $num -eq 10 ]; then
    echo "数字等于 10"
# -gt 表示大于
elif [ $num -gt 15 ]; then
    echo "数字大于 15 (当前值为 $num)"
else
    echo "数字小于 15"
fi

# 2. 文件属性判断
echo -e "\n### 文件检查 ###"

# -e 表示文件是否存在
if [ -e "$file_name" ]; then
    echo "$file_name 存在。"
    # -f 表示是否为普通文件(不是目录)
    if [ -f "$file_name" ]; then
        echo "这是一个普通文件。"
    fi
else
    echo "$file_name 不存在,请先创建。"
fi

for循环

for 循环通常用于遍历一组数据(如数字序列、字符串列表或文件内容)。

for 变量 in 列表
do
    # 循环体命令
done
  1. 遍历数字与读取文件行
#!/bin/bash
echo "### 开始执行 for 循环演示 ###"
# 1. 遍历数字序列 (1 到 5)
# -e 参数的作用是启用反斜杠转义
echo -e "\n-- 打印数字 1 到 5 --"
for i in {1..5}
do
    echo "当前数字是: $i"
done

# 2. 遍历文件中的每一行 (基于你之前的 v1.txt)
echo -e "\n-- 读取 v1.txt 文件内容 --"
# 使用 < $filewhile 读取文件,这是更稳健的写法,但 for 也可以用 cat
for line in $(cat v1.txt)
do
    # 注意:这种方式会按空格分割单词,如果想按整行读取,请看 while 演示
    echo "读取到: $line"
done

while循环

while [ 条件表达式 ]
do
    # 循环体命令
done
  1. 读取文件与计数
#!/bin/bash
echo "### 开始执行 while 循环演示 ###"
# 1. 读取文件的每一行 (推荐用于读取文件)
echo -e "\n-- 逐行读取 v1.txt (保留空格) --"
count=1
# read line 会从标准输入读取一行赋值给 line
while read line
do
    echo "第 $count 行: $line"
    # ((count++)) 是 shell 中的自增写法
    ((count++))
done < v1.txt
# 2. 简单计数器
echo -e "\n-- 倒计时 (5 到 1) --"
i=5
while [ $i -gt 0 ]
do
    echo "$i..."
    sleep 1 # 暂停 1 秒
    i=$((i - 1))
done
echo "倒计时结束!"

case多分支选择

case 语句类似于其他语言的 switch,用于匹配变量的值并执行相应操作,非常适合处理用户输入。

case $变量 in
    模式1)
        # 命令1
        ;;
    模式2)
        # 命令2
        ;;
    *)
        # 默认命令 (相当于 else)
        ;;
esac
  1. 菜单选择
#!/bin/bash
# 假设这是用户输入的参数,$1 代表第一个命令行参数
# 你可以运行脚本时加参数,例如:./script.sh start
action="$1"
echo "### 开始执行 case 语句演示 ###"
case $action in
    "start")
        echo "正在启动服务..."
        # 这里可以放启动命令
        ;;
    "stop")
        echo "正在停止服务..."
        ;;
    "restart")
        echo "正在重启服务..."
        ;;
    "status")
        echo "正在查询服务状态..."
        ;;
    *)
        echo "错误:无效的命令"
        echo "用法: $0 {start|stop|restart|status}"
        # $0 代表脚本名本身
        ;;
esac

逻辑运算符

&& 逻辑与

只有当左边的命令执行成功(返回值为 0)时,才执行右边的命令。前真后执行。

# 如果 v1.txt 存在,则删除它
[ -f v1.txt ] && rm v1.txt

|| 逻辑或

只有当左边的命令执行失败(返回值非 0)时,才执行右边的命令。前假后执行。

# 如果 v1.txt 不存在(grep 失败),则创建一个新文件
grep "hello" v1.txt || touch v1.txt

! 逻辑非

对判断结果取反。

# 如果 v1.txt 不存在
if [ ! -f v1.txt ]; then
    echo "文件没找到"
fi

函数

函数的定义

方法 1:使用 function 关键字(推荐,更清晰)

# function my_function 这里的()可以不写,但是不建议
function my_function() {
    echo "这是函数内部"
    # 可以放多条命令
}

方法 2:使用括号语法(传统写法)

my_function() {
    echo "这也是函数"
}

函数的调用

定义后,直接使用函数名调用,不需要加括号:

my_function  # 调用函数

完整脚本

#!/bin/bash
function greet() {
    echo "你好,欢迎使用 Shell 脚本!"
}
greet  # 输出:你好,欢迎使用 Shell 脚本!

函数参数传递

局部变量与全局变量:默认变量是全局的,函数内外都可访问。 使用 local 关键字声明局部变量,仅在函数内部有效。 示例:局部变量

function test_localVar() {
    local var="局部变量"
    echo "函数内:$var"
}

var="全局变量"
test_localVar
echo "函数外:$var"  # 仍输出“全局变量”

函数可以接收参数,使用方式与脚本参数相同:

  • $1$2$3...:表示第 1、2、3 个参数
  • $#:参数个数
  • $@$*:所有参数
  • $0:脚本名(在函数中仍表示脚本名,不是函数名) 示例:带参数的函数
function greet_user {
    local name=$1
    local age=$2
    echo "你好,$name!你今年 $age 岁。"
}

greet_user "小明" 20
# 输出:你好,小明!你今年 20 岁。

函数返回值

Shell 函数不能像其他语言那样“返回字符串或数字”,它只能通过 return 返回退出状态码(0~255),通常:

  • return 0:表示成功
  • return 1:表示失败 如果要返回实际数据(如字符串、计算结果),应使用 echo 输出,然后用命令替换获取。
  1. 示例:返回状态码
function check_file() {
    if [ -f "$1" ]; then
        return 0  # 成功
    else
        return 1  # 失败
    fi
}

check_file "test.txt"
if [ $? -eq 0 ]; then
    echo "文件存在"
else
    echo "文件不存在"
fi
  1. 示例:返回实际值(推荐做法)
function add() {
    local sum=$(( $1 + $2 ))
    echo $sum  # 用 echo 输出结果
}

result=$(add 3 5)
echo "结果是:$result"  # 输出:结果是:8

函数的嵌套与作用域

函数可以嵌套定义(不推荐),也可以在函数中调用其他函数:

function outer() {
    echo "外层函数"
    inner
}

function inner() {
    echo "内层函数"
}

outer
# 输出:
# 外层函数
# 内层函数

实用技巧

  1. 函数参数校验
function divide() {
    if [ $# -ne 2 ]; then
        echo "用法:divide <被除数> <除数>"
        return 1
    fi
    if [ $2 -eq 0 ]; then
        echo "错误:除数不能为 0"
        return 1
    fi
    echo $(( $1 / $2 ))
}
divide 10 2  # 输出:5
divide 10 0  # 输出错误信息
  1. 函数库复用:可以把常用函数写入一个文件,如 lib.sh
# lib.sh
function log_info() {
    echo "[INFO] $1"
}

function log_error() {
    echo "[ERROR] $1" >&2
}

在主脚本中引入:

#!/bin/bash
source ./lib.sh  # 或 . ./lib.sh

log_info "程序开始"
log_error "发生错误"

函数调试技巧

使用 set -x 开启调试模式,可看到函数执行过程:

set -x
my_function
set +x