Shell编程

变量

定义变量

  • 不用加 $
  • 等号两边不能有空格
name="liMing" #变量定义不用加$
  • 变量=$(命令)
date_str=$(date +%Y-%m-%d)

echo "${date_str}"

使用变量

  • 最好加上花括号用来区分变量边界
echo "${name}aaa"
echo "$nameaaa"

变量类型

  • 常见的有:字符串,整数,数组,环境变量,特殊变量
  • 通常被视为字符串
  • declare 可以声明整数变量
str="hello shell"
str='hello shell'
declare -i my_integer=42
arr=(1 2 3 4 5)

#环境变量 env可以查看所有环境变量
echo $PATH

echo $0 #输出脚本名称
echo $# #输出传递给脚本的所有参数数量
echo $? #输出上一条命令的执行结果状态:1失败,0成功

字符串

name="liMing"

echo "hello ${name}"

echo "hello "${name}""

echo ${#name} #获取字符串长度
echo ${name:0:4} #获取字符串从0开始长度为4的子字符串

数组

arr=("java" "python")
echo ${#arr[@]} #获取数据长度
echo ${arr[0]} #获取数组第一个元素
echo ${#arr[0]} #获取数组第一个元素长度

注释

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

<<'COMMENT'  
这是注释的部分。  
可以有多行内容。  
COMMENT
# eof可以换成其他的字符比如:a,b。。

参数传递

echo "name: $0"

echo "len: $#"

echo "first: $1"

echo "all $*"

echo "all $@"
╭─hzl@HZL /mnt/c/Users/20881/vscode/A001_temp
╰─$ ./hello.sh r
name: ./hello.sh
len: 1
first: r

数组

  • 简单数组
arr=("java" "python")
echo ${#arr[@]} #获取数据长度
echo ${arr[0]} #获取数组第一个元素
echo ${#arr[0]} #获取数组第一个元素长度
  • 关联数组
declare -A site

site["google"]="www.google.com"

site["baidu"]="www.baidu.com"

echo "site: ${site["google"]}"

echo "${site[*]}"  #www.google.com www.baidu.com 不含有key,只有value

运算符

算数运算符

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30。
- 减法 expr $a - $b 结果为 -10。
* 乘法 expr $a \* $b 结果为  200。
/ 除法 expr $b / $a 结果为 2。
% 取余 expr $b % $a 结果为 0。
= 赋值 a=$b 把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true。 [ $a == $b ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ $a != $b ] 返回 true。
  • [] 中开头和末尾要有空格, !=== 左右要有空格
a=10

b=20

val=`expr ${a} + ${b}`

echo "a+b: ${val}"

if [ ${a} == ${b} ]

then

    echo "a==b"

fi

if [ ${a} != ${b} ]

then

    echo "a!=b"

fi

关系运算符

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。
  • [] 中开头和末尾要有空格, -eq-ne 左右要有空格
a=10

b=20

if [ ${a} -eq ${b} ]

then

    echo "${a} -eq ${b} : ${a}等于${b}"

fi

  

if [ ${a} -ne ${b} ]

then

    echo "${a} -ne ${b} : ${a}不等于${b}"

fi

布尔运算符

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。
a=10

b=20

if [ ${a} -lt 100 -a ${b} -gt 10 ]

then

    echo "${a}<100 并且 ${b}>10"

fi

逻辑运算符

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ $a -lt 100 | $b -gt 100 ]] 返回 true
a=10

b=20

if [ ${a} -lt ${b} ]

then

    echo "${a}<${b}"

else

    echo "${a}>=${b}"

fi

字符串运算符

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否不相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [ -n “$a” ] 返回 true。
$ 检测字符串是否不为空,不为空返回 true。 [ $a ] 返回 true。
a=

if [ -z ${a} ]

then

    echo "字符串为空"

else

    echo "字符串不为空"

fi

文件测试运算符

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。
if [ -d "/etc" ]

then

    echo "/etc 是目录"

fi

自增运算符

Shell 本身没有像 C、C++ 或 Java 那样的 ++ 和 – 操作符,但可以通过其他方式实现相同的功能

let num++
num=$((num + 1))
((num++))

echo

echo hello

echo "hello"

echo "${SHELL}"

  

echo -n "hello  "  #默认echo后换行,这里不换行

echo "world"

  

echo -e "hello\tworld" #开启转义字符解释,\t 表示制表符

  

echo "$(date)"  # $() 圆括号表示执行结果
╭─hzl@HZL /mnt/c/Users/20881/vscode/A001_temp
╰─$ ./hello.sh r t
hello
hello
/bin/zsh
hello  world
hello   world
Tue May 19 03:51:11 PM CST 2026
  • 进度条
echo -n "progress:["

for i in {1..20}; do

    echo -n "#"

    sleep 0.1

done

echo -e  "]\nDone!"

printf

  1. 解析格式字符串,遇到普通字符直接输出
  2. 遇到格式说明符(以%开头)时:
    • 读取下一个参数
    • 按照说明符指定的格式处理该参数
    • 将结果插入到输出中
  3. 处理完所有格式说明符后,输出最终结果
name="Alice"

printf "hello %s\n" ${name}

printf "decimal %d\nhex %x\nbin %b\n" 255 255 255

printf "Year: %04d\n" 23

printf "Pi: %.2f\n" 3.14159

printf "%-10s %5d %8.2f\n" "Apple" 5 2.5 "Orange" 3 1.75

test命令

test 命令是 Shell 内置的条件判断工具,用于评估表达式并返回布尔值(真/假),它通常与 if 语句结合使用,是 Shell 脚本中实现逻辑控制的基础。

  • [] 单个方括号

[] 中可以使用文件测试运算符 [ -e file.txt ],字符串运算符 [ -n ${var} ],数值比较 [ ${a} -eq ${b} ] ,逻辑运算符 [ ! -f "$file" ][ ${a} -eq 1 -a ${b} -eq 2 ]

file="/etc/passwd"  
  
if [ -e "$file" ]; then  
    echo "$file 存在"  
    if [ -r "$file" ]; then  
        echo "并且可读"  
    fi  
else  
    echo "$file 不存在"  
fi
  • [[]] 支持正则,模式匹配
  • (()) 专为数值比较设计
if [[ "$file" == *.log ]]; then
    echo "这是日志文件"
fi

if (( $count > 10 )); then
    echo "数量超过10"
fi
if systemctl is-active --quiet "$service"; then
    # 直接判断状态码,无需括号,无需 [, 无需 [[
    echo "运行中"
fi

backup_file="/backups/data_$(date +%Y%m%d).tar.gz"

if [ ! -f "$backup_file" ]; then
    echo "错误:备份文件 $backup_file 不存在"
    exit 1
elif [ ! -s "$backup_file" ]; then
    echo "警告:备份文件为空"
else
    echo "备份验证成功"
fi

流程控制

if

if [ -e "/etc" ]
then
    command1 
    command2
    ...
    commandN
else
    command
fi
if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

for

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done
for i in 1 2 3 4

do

    echo "第${i}个"

done

  

for str in hello shell

do

    echo ${str}

done

for ((i=2; i*i<=num; i++))
do
	
done

while

while condition
do
    command
done
i=1

while((${i}<=5))

do

    echo "第${i}个"

    ((i++))

done
  • 无限循环
while :
do
    command
done

case


case $aNum in  
    1)  echo '你选择了 1'  
    ;;  
    2)  echo '你选择了 2'  
    ;;  
    3)  echo '你选择了 3'  
    ;;  
    4)  echo '你选择了 4'  
    ;;  
    *)  echo '你没有输入 1 到 4 之间的数字'  
    ;;  
esac

跳出循环

  • break/continue
while :

do

    echo "input number in 1-5"

    read num

    case ${num} in

        1|2|3|4|5) echo "output ${num}"

        ;;

        *) echo "no valid game over!"

            #break

            continue

        ;;

    esac

done

函数

  • 返回值后面的值应该是 0-255 否则溢出
#[] 中是可选的,即可以没有function关键字,也可以没括号,没返回值
[ function ] funname [()]

{

    action;

    [return int;] 

}
function doFunc(){

    echo "test function"

}

  

echo "start function"

doFunc

echo "end function"
function withReturn(){

    echo "input num1"

    read num1

    echo "input num2"

    read num2

    res=$((${num1} + ${num2}))

    echo "res is ${res}"

    return ${res}

}

withReturn

输入/输出重定向

大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回​​到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。

命令 说明
command > file 将输出重定向到 file。
command < file 将输入重定向到 file。
command » file 将输出以追加的方式重定向到 file。
n > file 将文件描述符为 n 的文件重定向到 file。
n » file 将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m 将输出文件 m 和 n 合并。
n <& m 将输入文件 m 和 n 合并。
« tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
  • 输出重定向
╭─hzl@HZL /mnt/c/Users/20881/vscode/A001_temp
╰─$ who > ./file.txt  #当前目录下的file.txt 中写 hzl      pts/1        2026-05-19 12:29

╭─hzl@HZL /mnt/c/Users/20881/vscode/A001_temp
╰─$ echo "第二次写" > ./file.txt  #此时文件就会被覆盖掉,不想覆盖就用 >>
  • 输入重定向
echo "Alice 25" > user.txt

read name age < user.txt

echo "姓名: ${name}, 年龄: ${age}"
  • cat将输出重定向到 log.txt ,而 cat的输入又是从 'EOF'......EOF 重定向来的
╭─hzl@HZL /mnt/c/Users/20881/vscode/A001_temp
╰─$ cat > log.txt  <<'EOF'                                                  
heredoc> test1
heredoc> test2
heredoc> EOF

╭─hzl@HZL /mnt/c/Users/20881/vscode/A001_temp
╰─$ cat log.txt
test1
test2

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。 如果希望 stderr 重定向到 file,可以这样写:

command 2>file #覆盖
command 2>>file #追加

如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:

command > file 2>&1 #覆盖
command >> file 2>&1 #追加

command &>file #等价于覆盖将stdout 和stderr写入file

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

command > /dev/null

文件引用

shell2中可以引用shell1

#shell1中定义
url="www.baidu.com"

#shell2中引用
. ./shell1.sh # 或者 source ./shell1.sh
echo "${url}"