1、Shell1.1、打印方法# 简单打印 echo Hello World echo a\nb # 打印变量 nameTom echo Hello, ${name} # 打印转义字符 echo -e a\nb # 打印linux命令输出 date ls -l # 多行打印 cat EOF aaa bbb ccc ddd eee fff 111 222 333 444 555 666 变量值${name} EOF # 重定向到文件 echo hello out.txt # 覆盖 echo world out.txt # 追加# 将 多行内容 定向到文件 abcd.log 中 nameTom # 多行打印 cat EOF abcd.log aaa bbb ccc ddd eee fff 111 222 333 444 555 666 变量值${name} EOF1.2、变量####################### 变量定义 ####################### # 等号两边绝对不能有空格 nameTom age18 path/home/work # 如果变量内容包含空格必须用引号括起来 msgHello World # 打印变量 强烈建议使用 ${变量名} 的格式 echo ${name} echo ${age} echo ${path} echo ${msg} ####################### 修改变量 ####################### # 重新复制即可 nameJerry echo ${name} ####################### 删除变量 ####################### unset name echo ${name} # 打印为空 ####################### 只读变量 ####################### readonly PI3.14 ####################### 全局变量环境变量 ####################### export MY_VARhello word####################### 坑1 等号两边不能有空格 ####################### name Tom # Shell 会以为你在执行一个叫 name 的命令参数是 和 Tom ####################### 坑2 单引号 vs 双引号 ####################### # 双引号 弱引用会解析里面的变量和命令。 nameTom echo Hello $name # 输出Hello Tom # 单引号 强引用里面写什么就输出什么绝对不解析。 nameTom echo Hello $name # 输出Hello $name # 反引号 或 $() 用于执行命令并获取结果。 now$(date) # 推荐写法 echo 现在时间是 $now ####################### 坑3 变量默认是字符串不能直接算数 ####################### a1 b2 echo $a$b # 错误运用输出 12 (当成字符串拼接了) # 正确的数学运算方式 echo $((a b)) # 输出3 # 或者 echo $(expr $a $b) # 输出3 (注意 expr 的加号两边必须有空格) ####################### 坑4 变量引用遇到特殊字符 ####################### # 如果变量的值包含特殊字符如 *、空格在引用时加上双引号否则会被 Shell 解析成通配符或被截断。 filemy file.txt cat $file # 会报错认为你要打开 my 和 file.txt 两个文件 cat $file # 正确 ####################### 坑5 Here Document (EOF) 中的变量解析 ####################### # 使用 EOF 时里面的 $变量 会被解析。 # 使用 EOF给 EOF 加上单引号时里面的 $变量 不会被解析原样输出。 nameTom cat EOF aaa bbb ccc ddd eee fff 111 222 333 444 555 666 变量值${name} EOF ####################### 坑6 未定义变量的处理 ####################### # 如果你引用了一个没定义过的变量默认会输出空不会报错这可能导致灾难性后果。 # 推荐在脚本开头加上 set -u这样一旦用到未定义的变量脚本会立刻报错退出防止误删数据。 # 或者使用默认值语法${name:-默认值}如果 name 没定义则返回默认值。 set -u cat EOF aaa bbb ccc ddd eee fff 111 222 333 444 555 666 变量值${name} EOF1.3、循环####################### for循环 ####################### # 形式一 遍历列表 for 变量 in 列表 do 命令 done # 形式二 C 语言风格常用于精确控制次数 for ((初始值; 条件; 步长)) do 命令 done ####################### for循环举例 ####################### for file in *.log do echo 正在备份文件: $file cp $file ${file}.bak done aaa(1111 2222 3333 4444) for st1 in ${aaa[]} do echo ${st1} done for ((i0;i10;i)) do echo ${i} done # ${aaa[]} 展开数组所有元素加双引号可以应对元素带空格的场景是最佳实践 # 如果写 ${aaa[*]} 会把所有元素合并成一整个字符串####################### while循环 ####################### # 只要条件成立真就一直循环执行。适合不知道要循环多少次只知道达成某个条件就停止的场景。 while [ 条件判断 ] do 命令 done ####################### while循环举例 ####################### # 逐行读取out.txt文件 while IFS read -r line do echo 读取到内容: $line done out.txt i10 while [ ${i} -lt 11 ] # 判断 i 小于 11 do echo aaaaaa i$((i 1)) done####################### until循环 ####################### # 和 while 刚好反过来。只要条件不成立假就一直循环一旦条件成立真立刻停止。适合直到...才停止的逻辑。 until [ 条件判断 ] do 命令 done ####################### until循环举例 ####################### # 实例 等待 MySQL 服务完全启动 until systemctl is-active --quiet mysqld do echo MySQL 还没启动等 2 秒再试... sleep 2 done echo MySQL 已启动####################### select循环 ####################### select 变量 in 选项1 选项2 do 命令 done ####################### select循环举例 ####################### PS3请选择要执行的操作: select action in 启动服务 停止服务 重启服务 退出 do case $action in 启动服务) echo 正在启动...; break ;; 停止服务) echo 正在停止...; break ;; 退出) exit 0 ;; *) echo 无效选择请重新输入 ;; esac done # 坑点: 没有退出机制; 如果没有写 break 或 exitCtrlC 退出后如果不小心按了回车还会继续循环。通常需要配合 case 语句使用1.4、函数####################### 定义 ####################### # 方式一 推荐 函数名() { 命令 } # 方式二 使用 function 关键字更适合习惯其他语言的人 function 函数名 { 命令 } ####################### 调用 ####################### # 实例演示 say_hello() { echo Hello, 欢迎学习 Shell } # 调用函数 say_hello####################### 函数传参 ####################### is_even() { # 检查参数个数是否为 1 个 if [ $# -ne 1 ]; then echo 错误需要提供一个参数 return 1 # 返回错误状态码 fi if (( $1 % 2 0 )); then return 0 # 成功/代表真 else return 1 # 失败/代表假 fi } is_even 4 if [ $? -eq 0 ]; then echo 4 是偶数 fi1.5、想在一个shell脚本中同时启动多个字进程# Shell 中的进程主要分为以下几种情况 # 前台进程 (Foreground)默认情况你敲下命令终端被这个进程霸占直到它执行完你才能敲下一个命令。 # 后台进程 (Background)在命令末尾加 进程会在后台偷偷跑终端立刻还给你你可以继续敲别的命令。 # 子 Shell (Subshell)使用 (命令) 或 | 管道符或者执行另一个脚本 ./test.sh 时会 fork 出一个子进程子 Shell。子 Shell 会继承父 Shell 的变量但子 Shell 里的修改不会影响父 Shell。这就是前面提到的“管道陷阱”的根源。 常用进程控制命令 放到后台执行。 jobs 查看当前终端后台的任务。 fg %1 把后台第 1 个任务调回前台。 bg %1 把暂停的任务放到后台继续跑。 wait 等待所有后台子进程结束。 $! 获取最后一个后台进程的 PID进程号。 kill -9 PID 强杀进程。#!/bin/bash # # process_demo.sh # 并行跑两个循环打印各自 PID两个循环没跑完主进程就不退出 # # 循环1每 10 秒 1从 1 加到 100 loop1() { local n1 # 局部变量 n初始值 1local 保证只在函数内有效 while [ $n -le 100 ]; do # 当 n 100 时循环-le 表示小于等于 echo [循环1 pid$$] n $n # 打印当前值$$ 是脚本进程号 n$((n 1)) # n 加 1$(( )) 是算术运算 sleep 10 # 暂停 10 秒再进入下一轮 done } # 循环2每 20 秒 10从 1000 加到 2000 loop2() { local m1000 # 局部变量 m初始值 1000 while [ $m -le 2000 ]; do # 当 m 2000 时循环 echo [循环2 pid$$] m $m # 打印当前值 m$((m 10)) # m 加 10 sleep 20 # 暂停 20 秒再进入下一轮 done } # 后台并行启动两个循环用 $! 分别捕获 PID loop1 # 把 loop1 丢到后台执行不阻塞往下走 PID1$! # $! 最近一个后台进程的 PID立刻存进 PID1 loop2 # loop2 也丢到后台与 loop1 并行 PID2$! # 此刻 $! 变成 loop2 的 PID存进 PID2 echo 循环1 PID $PID1 # 打印两个后台进程各自的 PID echo 循环2 PID $PID2 # 主进程等待两个后台循环期间不退出两者都结束后主进程才退出 wait # 不带参数阻塞直到所有后台子进程结束
阅读完成 · 觉得有帮助?