linux shell写法(基础结构、数据处理、流程控制、函数与高级特性几大部分)

一、基础语法

(1)脚本开头 

脚本开头指定解释器       #!后接  指定解释器路径

#!/bin/bash  # 指定使用 Bash 解释器
#!/bin/sh    # 指定使用系统默认 Shell

(2)注释

#                  井号是 单行注释

: '
多行注释1
多行注释2
'

<<EOF
多行注释1
多行注释2
EOF                                冒号加单引号   或者   <<EOF 再加EOF  是多行注释

(3)变量

变量定义与赋值:
        变量名只能包含字母、数字、下划线,不能以数字开头;赋值时=两边无空格。

基本赋值:给变量存东西

命令替换赋值:让变量存 “命令执行的结果”

算术赋值:让变量存 “数学计算的结果”

        定义PI变量,加了readonly就成 “只读变量”,存好 3.14159 后不能改也不能删。

        unset删除已定义的变量(普通变量age才能删,定义为只读变量的PI删不了)。

         pwd是 “显示当前路径” 的命令,$(命令)会先执行命令,把结果存到current_dir里。

        num=$((18+5))双括号是因为Shell 本身不会自动算算术,如果直接写 num=18+5,它不会帮你算 23,而是会把 18+5 这个字符串直接存进变量里,双层括号 ((...)) 的作用是告诉 Shell “这里要算算术”。

        $(美元符号)是个 “提取 / 引用” 的标记 ,核心作用是 “把内容取出来用“。
·跟变量名(如$name)→ 提取变量里的内容;
·跟括号(如$((1+1))、$(pwd))→ 提取括号里计算 / 命令的结果;
·跟特殊符号(如$0)
$0:当前脚本的文件名;
$1:脚本运行时传入的第 1 个参数;
$?:上一条命令的执行结果(0 代表成功,非 0 代表失败)

变量引用

花括号 {} 明确框定了「变量名的范围」,Shell 只会识别 {} 内的字符为变量名,不会把后面的内容粘连。

echo ${name:-默认值} 是【变量为空 / 未定义时,临时用默认值,不修改原变量】,下次再输出就不会是这个默认值,而是原来是怎样就是怎样
{变量名:-默认值} 里的符号是固定组合 :-,不能拆分,中间无空格

echo ${name:=新值} 【变量为空 / 未定义时,赋值 + 取值,永久修改原变量】,仅在他为空或未定义时,进行修改。

这两者都是一开始有定义且有值不变,区别是在无值或无定义时 :-是只生效一次 :=是永久改变

echo ${#name} 【获取变量的字符串长度】

echo ${name:1:2} 【字符串截取 / 切片,精准取值】

对变量的字符串进行「切片 / 截取」,从指定位置开始,取指定个数的字符

固定语法:
${变量名:起始索引:截取长度}
Shell 中字符串的 索引(下标)从 0 开始,第一个字符的索引是 0,第二个是 1,以此类推

如果「截取长度」不写,会从起始索引开始,截取到字符串末尾。

索引可以写负数,表示「从字符串末尾开始倒数」但注意负号前必须加空格

环境变量(系统预定义的全局变量)

环境变量大小写敏感,注意大写

echo $PATH

我们在终端输入任何命令(ls、cd、mkdir、python),系统不知道这个命令的文件在哪,于是会自动去 $PATH 变量里记录的所有目录中,挨个查找这个命令的可执行文件,找到就运行,找不到就报错 command not found

系统需要快速查找命令的关键目录才会被加入$PATH

路径之间用 冒号 : 分隔

这里没有包含下面的home目录是因为,默认情况下,普通用户的$PATH会包含/home/用户名/bin,但我这里的是root用户的$PATH通常不包含/root以外的home目录

echo $HOME

当前登录用户的 主目录 / 家目录,是每个用户的「专属文件夹」,存放个人文件 / 配置

cd ~ 等价于 cd $HOME,输入 cd ~ 一键回到自己的家目录

超级管理员 root 用户:$HOME = /root
普通用户(ubuntu):$HOME = /home/ubuntu

echo $PWD

显示你「此时此刻」在终端所处的文件夹路径,和命令 pwd 效果完全一样(pwd 命令本质就是读取这个环境变量的值)。

echo $USER 
当前登录的用户名,作用是系统识别「谁在操作终端」

root 用户登录时输出 root,普通用户登录时输出你的用户名(比如 test)

echo $SHELL

当前终端正在「使用的 Shell 解释器」
作用:Linux 的终端本质是「Shell 解释器」在工作,它负责解析你输入的所有命令,再交给系统内核执行。

echo $?

$? 不是环境变量,是 Shell 的「特殊内置状态变量」,但它的使用频率和上面的环境变量持平,所以被归为一类。

含义:上一条执行的命令的「退出状态码」
核心规则:0 = 命令执行成功 ,非0 = 命令执行失败/报错
        状态码为 0 → 告诉你:上一条命令,我执行完了,没问题
        状态码为 1/2/127 等数字 → 告诉你:上一条命令出错了,这个数字是错误代号

export PATH=$PATH:/new/path
含义:临时追加系统命令搜索路径,仅当前终端会话有效
核心关键字 export:变量「全局化」命令,作用是把变量升级为「环境变量」,让这个变量在当前终端 + 所有子进程中全局生效。
如果定义变量不加 export → 只是「局部普通变量」,仅当前终端能用;
如果定义变量加 export → 变成「临时的全局环境变量」,当前终端 + 所有子进程中全局的所有命令 / 脚本都能读取。
核心语法 PATH=$PATH:/new/path:给 PATH「追加新路径」,不是覆盖
不要写 export PATH=/new/path → 这是覆盖原有的 PATH。会导致系统找不到 ls、cd 等所有系统命令
必须写 export PATH=$PATH:/new/path → 这是追加新路径!$PATH 代表「原来的所有搜索路径」: /new/path 是「把新路径加到原来的后面」
语法逻辑:新 PATH = 原来的全部路径 + 冒号分隔 + 新增的路径
仅当前终端会话有效(临时生效),执行这条命令后,在当前这个终端窗口里,系统能识别 /new/path 目录下的所有可执行命令;
如果你新开一个终端窗口,或者关闭当前终端重新打开,刚才追加的 /new/path 路径就消失了,PATH 变回原来的样子;这种写法是「内存临时生效」,没有写入系统配置文件,重启终端就会被清空。
示例----在 /usr/local/mybin 目录下放了一个自己写的脚本 mycmd.sh,想在终端任意目录直接输入 mycmd.sh 就能执行,就需要把这个目录加入 PATH;如果没有加入PATH,他就需要切换到具体的目录中再去启动

临时修改重启终端就失效,那需要「永久生效」时,采用:

给当前用户永久修改

~/.bashrc 是当前用户的「开机自启配置文件」,每次终端启动时,系统会自动读取这个文件里的命令,相当于把配置「写进开机启动项」

给全部用户永久修改

(4)输入输出

echo输出

-n 参数,作用是「取消默认的末尾换行」。终端打印出 无换行输出,打印完成后光标停在这句话的末尾,不会跳到下一行。不加 -n:输出内容 + 自动换行(光标在下一行开头)

-e 是 enable 的缩写,开启对「反斜杠转义字符」的解析功能。如果不加 -e,echo 会把 \n、\t 这类符号当成普通文本直接打印,不会生效,效果:换行\n制表\t测试

配合echo -e使用的特殊字符,Linux/Shell:
\n :换行符 → 光标换到下一行开头
\t :制表符 → 相当于键盘按一下 Tab 键,空出 4 个字符的间距
\b :退格符 → 删掉前一个字符(相当于键盘 Backspace)
\\ :打印一个反斜杠 → 因为\是特殊符号,想输出它本身就要写两个
\" :打印一个双引号 → 双引号在 echo 里是定界符,想输出它就要加\转义

read输入

读取用户在终端手动敲的内容,按下 回车键时,read 就会停止读取,并把输入的内容存到变量里。

read name
echo "你输入的名字是:$name"

终端会出现一个「空白光标」,等待你输入,比如输入张三,那么变量name的值就是张三,后续可以用echo $name调用这个变量

read -p "请输入年龄:" age

-p 是 prompt(提示) 的缩写,作用是在等待输入前,先打印一段提示文字,无需配合 echo 命令。

带超时限制的输入:read -t 5 -p "5秒内输入:" input
-t 是 timeout(超时) 的缩写,后面跟「数字」代表 超时秒数,作用是:
只等待用户在指定秒数内输入,超过时间自动结束等待,不再阻塞脚本运行,脚本会继续往下执行。-p是prompt(提示)的缩写,作用是在等待用户输入时,先显示一段提示文字

-t 和 -p 是可以组合使用的,Shell 的绝大多数命令参数都支持这种-pt组合写法;
超时后,变量(这里是input)会被赋值为空值;
适用场景:脚本需要用户快速确认 / 输入,不想让脚本一直卡在输入步骤无限等待。

隐藏输入:read -s -p "提示文本" 变量名

 -s 是 silent(静默 / 隐藏)的缩写,核心作用:用户输入内容时,终端不会显示任何字符(输入的内容被隐藏,看不到输入的密码 / 字符)。

重定向与管道

「重定向」
默认情况下:
命令的输出(比如 echo 打印的内容)会显示在终端屏幕;
命令的输入(比如 read 读取的内容)会从键盘获取;
命令的错误提示(比如 ls /不存在的目录)也会显示在终端屏幕。
「重定向」就是改变这些默认流向:让输出存到文件、让输入从文件读、让错误提示存到文件等。而且不会显示到终端

重定向符号:

先处理重定向操作(比如创建 / 清空 / 追加文件);
再执行左边的命令,把命令的输出写入重定向的文件。


管道符号(|)
执行顺序是从左到右依次执行:
先执行左边命令,得到输出;
把左边命令的输出,作为右边命令的输入;
再执行右边命令。

以下是重定向符号

 1.>:覆盖输出到文件
作用:把命令的输出内容,直接覆盖写入到指定文件(文件不存在则新建,存在则清空原有内容)。
示例:echo "test" > file.txt
执行后,file.txt 里的内容会变成 test(如果之前有内容会被清空)。
2. >>:追加输出到文件
作用:把命令的输出内容,追加写入到指定文件末尾(文件不存在则新建,存在则保留原有内容,新内容加在后面)。
示例:echo "test" >> file.txt
执行后,file.txt 会新增一行 test(原有内容不会被删)。
3. <:从文件读取输入
作用:让命令的输入来源,从「键盘」改成「指定文件」。
示例:read < file.txt
执行后,read 不会等待键盘输入,而是直接读取 file.txt 里的内容,赋值给变量(如果 read 后跟变量名,就存到变量里)。
4. 2>:重定向错误输出
作用:把命令的错误提示信息(比如「文件不存在」),重定向到指定文件(不显示在终端)。
(注:Shell 中,1 代表「标准输出」,2 代表「错误输出」)
示例:ls /xxx 2> error.log
因为 /xxx 目录不存在,ls   会报错;执行后,错误信息会存到 error.log,终端不会显示报错。

当执行 2> error.log 时,不管 error.log 原本有没有内容,执行命令的瞬间,这个文件会被立刻清空(就是第一点的清除,然后有信息再填上)


5. 2>&1:错误输出合并到标准输出
作用:把「错误输出」和「标准输出」合并到一起,统一流向同一个地方(比如文件)。
示例:ls /xxx > all.log 2>&1
执行后:
不管 ls 是正常输出还是报错,内容都会存到 all.log;
终端不会显示任何内容(所有输出都被重定向到文件)

管道符 |:前命令输出作为后命令输入
作用:把「前一个命令的输出结果」,直接作为「后一个命令的输入内容」,实现多命令串联。
示例:ps -ef | grep java
执行逻辑:
ps -ef:列出系统中所有进程;
|:把进程列表传递给后面的 grep;
grep java:从进程列表中筛选出包含 java 的进程

-ef 是 ps 的参数组合:
-e:显示系统中所有进程(默认只显示当前用户的进程);
-f:以完整格式显示进程信息(包含进程 ID、父进程 ID、启动用户、启动时间、命令路径等)。

-e = everything
-f = full-format

grep 用于在文本内容中搜索匹配指定关键词的行;
ps -ef 会输出一大片文字 → 里面是系统所有进程的一行一行信息
此时 grep rcu 就会在上面这些一行一行的文字里,只挑出包含 rcu 这个关键词的那几行

二、数据类型与运算

1.数据类型

Shell 无严格数据类型,默认都是字符串,支持以下 “伪类型”:
·字符串(单 / 双引号):str='hello'、str="hello $name"(双引号解析变量)
·整数:仅支持整数运算,浮点数需借助bc工具
·数组:索引数组、关联数组

2.数组

索引数组是通过数字下标(索引)访问元素的数组,Shell 中默认索引从 0 开始。

关联数组是通过自定义键名(而非数字索引)访问元素的数组,需要先通过 declare -A 声明。

不带!是取值

带!是取键

3.运算

a++ 是后自增运算符,意思是 “先返回 a 的当前值,再把 a 的值加 1”。
执行逻辑:
先把 a 的当前值代入表达式计算。
计算完成后,再将 a 的值加 1。

++b 是前自增运算符,意思是 “先把 b 的值加 1,再返回新的值”。
执行逻辑:
先将 b 的值加 1。
再把新的值代入表达式计算。

let 是 Shell 内置的算术运算命令,专门用来处理整数运算。
语法特点
直接写算术表达式,不需要加 $ 或括号包裹。
如果表达式里有空格或特殊字符,需要用引号把整个表达式括起来。

expr 是一个外部命令(不是 Shell 内置),同样用于整数运算,也可以做字符串处理。
语法特点
运算符(+、*、- 等)两边必须有空格,否则会被当成普通字符串。
乘法 * 需要加反斜杠转义(\*),因为 * 在 Shell 里是通配符。

字符串的替换

只管第一个匹配的

${变量/旧字符串/新字符串} 是 Bash 的单次替换语法。
它会把 str 中第一个出现的 "world" 替换为 "Shell",所以输出 "hello Shell"。
如果要全局替换所有匹配项,需要写成 ${str//world/Shell}

             

括号语法:

赋值用 = → 等号左右无空格
判断用 == → 等号左右必须有空格((( )) 里可省略)

[[ ... ]] 是 Bash 的增强条件判断语法。
== 在这里用作通配符匹配,*hello* 表示 “包含 hello 这个子串”。
因为 str 的值是 "hello world",确实包含 "hello",所以条件成立,会输出 "包含hello"。

语法格式要求
左右两个方括号 [[ 和 ]] 两侧必须有空格 → ✅正确:[[ $str == *hello* ]];❌错误:[[ $str == *hello*]] 或 [[ $str==*hello* ]]
整个 [[ ... ]] 外侧不需要加额外括号,直接跟在 if 后面即可

== 不是「等于」,是「通配符匹配」

if ...;then...

Shell 是「分行解析」的语言,Shell 解析器读代码时,默认把「每一行」当作「一条完整的命令 / 语法单元」来解析。换行符 \n 在 Shell 里,天生就是「语句终止符」,告诉 Shell:这一行结束了,下一行是新的内容。
if 后面要跟「条件判断内容」(比如[[ $str == *hello* ]]),这个「条件」是一坨完整的东西;then 是一个独立的 Shell 关键字,它必须和前面的「条件」做切割、做分离,不能连在一起。
要么用「换行」把它们切开 → 条件写一行,then 写在下一行
要么用「分号」把它们切开 → 条件和then写在同一行,中间加分号

三、流程控制

1. 条件判断(if)

"$file" 这种「双引号 + $变量名」的写法是安全地引用变量,避免很多潜在的脚本错误。

[ -f "$file" ] 是标准的正确写法,而如果写成 [ -f '$file' ] 就会判断一个叫 $file 的文件是否存在,这通常不是我们想要的。

Shell 中只有双中括号 [[ 表达式 ]] 支持正则匹配,单中括号 [ ] 不支持正则。 
以下是正则表达式的语法

普通字母 / 数字比如a、5:本质也是元字符,匹配【1 个】a、【1 个】5

大写的R,S,W是小写的r,s,w的“非”,非space,非word,非digit

这里的格式校验就是说还有其他隐藏的“合法性、合规性”要求,比如电话号码,第 1 位:必须是 1;第 2 位:必须是 3-9 中的一个数字;后面跟 9 位数字;整个字符串必须恰好 11 位,不能多也不能少

上面这个例子1[3-9]\d{9} 校验 'a13812345678b' 会返回「匹配成功」,但若换成 ^1[3-9]\d{9}$会精准校验「整个字符串是否是手机号」,返回「匹配失败」

[] 的作用是:表示「二选一 / 多选一」,在括号里列出所有允许出现的字符,最终只会匹配「其中 1 个字符」
{} 的作用是:用来指定「它左边紧邻的那一个字符 / 表达式」,需要「连续重复出现的次数」

不用边界符:在字符串中找到任意位置,只要出现 1 开头,跟着 3-9中的随便一个,再跟着 9 个0到9的数字的片段,就算匹配成功。

用边界符:

整个字符串的第一个字符必须是 1
第二个字符是 3-9 中的一个
接下来是连续紧挨的 9 个数字
第 11 个字符之后不能再有任何字符(必须直接到字符串结尾)

^ → 字符串的开头,不能有任何字符在它前面
\d{9} → 匹配9 个紧紧挨着的字符,每个字符都是「0-9 的任意数字」,数字可以乱序、可以重复
$ → 字符串的结尾,不能有任何字符在它后面

  • 不加 ^$:只要字符串包含符合规则的子串,就会匹配成功
  • 加上 ^$:只有整个字符串完全符合规则,才会匹配成功

正则表达式的基础骨架就是[元字符]+[量词]

「用元字符定义匹配什么内容,用量词定义匹配多少次

校验手机号:

合法手机号:13812345678、19966668888、15011112222

不合法:12812345678(第二位是 2)、1381234567(只有 10 位)、138123456789(12 位)、 13812345678(前面有空格)、13812345678a(后面有字母)

校验邮箱

[ ]里面的条件是        允许「可选出现」,且没有先后

合法邮箱:test@qq.com、my_email123@163.com、user.name-666@baidu.cn

不合法:test@.com(域名主体为空)、test@qq.c(后缀只有 1 位)、test@qq.companyname(后缀 7 位超长度)、test@qq com(@后无点号)

密码强度校验(8-20 位,含大小写 + 数字)

合法密码:Aa123456(8 位,全满足)、Test666888(大小写 + 数字)、123Abc456789(12 位)

不合法:aa123456(无大写)、AA123456(无小写)、AaBbCcDd(无数字)、Aa123(只有 5 位)、Aa12345678901234567890(21 位超长)

提取字符串中所有数字 

源文本:订单号:20260119,金额:999元,数量:5,总价:1299.99元,编号:66

提取结果:['20260119', '999', '5', '1299', '99', '66']

手机号脱敏(中间 4 位变 ****)

把 11 位手机号 13812345678 → 脱敏为 138****5678,保护用户隐私,所有 11 位手机号都适用。

 脱敏效果:13966668888→139****8888、15011112222→150****2222、18899990000→188****0000

四种括号使用

2.分支选择(case)

case 和 if 的【核心区别】
区别 1:「判断本质」完全不同(核心)
case → 值的精准匹配:只适合判断 「一个变量 是否等于 某几个固定值」,比如:变量等于 1/2/3、变量等于 yes/no、变量等于 start/stop/restart
 if → 条件的逻辑判断:万能判断!可以判断 「大小、区间、逻辑与 / 或、是否为空、文件属性」等所有场景,比如:判断数字是否大于 5、判断是否为空、判断文件是否存在、判断是否在区间 [1,10] 内
区别 2:「代码简洁度」
 当判断的是「变量等于多个固定值」时 → case 代码极度简洁,行数少,可读性极高,比如你的 1/2/3 匹配案例,case比if少写很多重复的elif [ $num -eq x ]
 当判断的是「复杂条件」时 → if 更简洁,case 无法实现
区别 3:「匹配规则」不同
 case:从上到下匹配,匹配成功立即退出,效率高,不会做多余判断
 if:从上到下依次判断所有条件,直到匹配到第一个满足的if/elif,否则执行else
 区别 4:「支持的场景」不同
case 支持 通配符:比如匹配「所有数字」[0-9])、匹配「所有字符」*)、匹配「以 a 开头的字符串」a*),这个是if做不到的简洁写法
if 支持 逻辑运算:&&(与)、||(或)、!(非),比如 if [ $num -gt 1 -a $num -lt 5 ](判断 num 在 2-4 之间),case完全无法实现
 区别 5:「结束标记」不同
case 以 esac 结尾(case 倒过来写)
if 以 fi 结尾(if 倒过来写)

3.循环(for |while|until)

Shell 里只有 4 种循环:for 普通循环、for 遍历循环、while 循环、until 循环,还有配套的 break/continue 循环控制、死循环、循环嵌套

until 的含义:当条件不满足时,执行循环体,一旦条件满足,立即退出循环
until 和 while 是完全相反的逻辑:

 while 条件 → 条件成立 → 执行循环
until 条件 → 条件不成立 → 执行循环
语法、条件写法、循环体规则,和 while 完全一样,唯一区别就是「条件判断的逻辑相反」

这里的let等价于(())还有$(()) 起到 让里面的内容解析为算术运算,如果没有,就会被解析为纯字符,而$(())和(())的区别在于 

没加$,运算后,只改变变量本身的值,表达式本身没有输出,没法赋值给其他变量。

加了$   能有返回值,算后会把最终的数字结果直接返回,可以直接赋值给变量、直接 echo 输出、直接当参数用。

 read 是 bash 的内置命令,它的功能就是从输入中读取一行内容,并把这行内容存到你给的变量里(这里就是 line)。每次循环,read 会从 file.txt 里读一行,然后把这行文本赋值给变量 line。这个变量不需要提前定义,read 会自动帮你创建它。line 并不是一个需要提前声明的变量,而是 read 命令的一个 “接收容器”。

第一次循环:read 读取 file.txt 的第一行,把内容放到 line 里,然后 echo $line 输出这行。
第二次循环:read 读取第二行,覆盖 line 的值,再输出。
当 read 读到文件末尾时,它会返回一个非零的退出状态,while 循环就会结态码

read 命令执行后会有一个退出状态码(也叫返回值):
成功读到内容 → 退出状态码是 0(代表 “真”),while 继续循环;
读到文件末尾 / 读不到内容 → 退出状态码是 非0(通常是 1,代表 “假”),while 停止循环。
在 bash 里,$? 这个特殊变量可以获取上一条命令的退出状态码
退出状态码 0 = 成功 / 真(true) → 让 while/if 等判断继续执行;
退出状态码 非0(比如 1)= 失败 / 假(false) → 让 while/if 停止执行。
这个设计的初衷是:退出状态码可以有很多种(1、2、127 等),用来表示不同的失败原因;但成功只有一种,所以用 0 代表 “一切正常”。

这个案例,可能会认为break结束的是if 

4.函数

$1 代表函数或脚本被调用时传入的第一个参数
$2 代表第二个参数

(写法 1)带 function 关键字
function func1() {函数体}
(写法 2)不带 function 关键字
func2() {函数体}        就是 带不带function的区别,两个都一样

result=$?:$? 是 bash 的特殊变量,用来获取上一条命令(这里就是 func1)的返回值,也就是func1 里 return 0,所以 result 会被赋值为 0
func2:调用 func2,它会声明局部变量 var=10 并输出 10,没有参数也没有返回值。

返回值(return 0)(0 = 加工成功,非 0 = 加工失败)

5.高级语法

${10}作用:当要获取第 10 及以后的参数,必须用大括号 {} 包裹,否则 bash 会把 $10 当成 $1 加上字符 0

$*作用:获取所有传入的参数,但会把它们合并成一个字符串(默认用空格连接)。不适合需要区分单个参数的场景,通常用得较少

$# 作用:获取传入的参数总个数。

"$@"  会完整保留参数的原始格式。如果是传入参数 "  hello  you"会被$@识别出    前面2个空格,hello五个字符,中间2个空格,后面3个字符。

只写 $@(不带双引号),会先把所有参数展开成一个大字符串,然后再按空格、制表符等进行分词拆分。bash 会把参数里的空格当成分隔符,把 " hello you" 拆成 hello 和 you 两个独立参数,原始的空格就丢失了

echo 命令里,-e 的作用是启用反斜杠转义字符的解析,会识别并处理像 \n(换行)、\t(制表符)、\r(回车)这类转义序列,否则会把反斜杠 \ 当成普通字符直接输出,不会解析它的特殊含义

可以进行    拆分+组合   理解用法

bash 里# 有两个完全不同的作用 ——注释 和 参数计数

# 作为注释  场景:写在一行开头,或代码行的末尾(用空格隔开)

$# 作为参数计数(特殊变量)必须和 $ 组合成 $#,单独的 # 没用;是统计传给脚本 / 函数的参数总个数;

#!:只有出现在脚本第一行开头的 #! 才生效,是给操作系统内核看的指令,不是给 bash 看的
如果 #! 不在第一行开头(比如第二行、或行首有空格),就会被当成普通注释。

Shell 通配符,就是在命令行里用来模糊匹配文件名的符号

都是“匹配”,通配符和正则匹配的区别:

* 在 Shell 里是 “任意字符”(数量,长度),在正则里是 “重复匹配”(次数)

锚点是正则表达式里用来定位文本位置的特殊符号,^ 行首锚点,$ 行尾锚点

算术扩展   $[表达式] 或 $((表达式))        $((...)),它功能更强,支持更多运算符

空值处理    这里的 var 只是一个占位符,代表你自己定义的任意变量名

字符串替换     str 和刚才的 var 一样,都只是示例用的占位符,可以换成任何合法的变量名

sleep 10 是 “休眠 10 秒” 的命令,后加 & 就会把它放到后台执行,可以在当前终端输入其他命令

jobs:查看当前终端里正在后台运行或暂停的作业列表。输出会显示作业编号(如 [1])、状态(Running/Stopped)和对应的命令。

当你想知道 自己当前终端里 有哪些后台任务(比如 sleep 100 &)时,用 jobs。
当你想排查 整个系统 的进程状态(比如看看哪个进程占用了 CPU)时,用 ps(常用 ps aux)

fg %1:把指定的后台作业调回前台运行。%1 表示作业编号为 1 的后台任务,执行后你会在当前终端看到它的输出,并且终端会被它占用,直到任务结束。

bg %1:让已经暂停的后台作业继续在后台运行。
如果一个后台作业被 Ctrl+Z 暂停了,用 bg %1 可以让它恢复运行,且依然留在后台。

kill %1:终止指定编号的后台作业。%1 对应 jobs 里看到的作业编号,精准结束某个后台任务

kill -9 1234:强制终止指定 PID(进程 ID) 的进程。
-9 是强制终止信号,1234 是目标进程的 PID。这个命令可以结束任何进程,包括那些无法正常退出的程序。

Bash 脚本默认是 “黑盒” 运行的 —— 你只能看到最终结果,看不到中间过程。调试模式就是为了打破这个黑盒。

调试模式---当脚本不符合预期或者出现错误时,需要开启调试模式,要用它来 “破案” :

#!/bin/bash -x  在脚本的开头就开启全局调试模式。-x 会让脚本在执行每一行命令时,先输出该命令的内容(前面带 + 号),再执行并显示结果。适合在运行整个脚本时跟踪每一步的执行过程。

命令替换:result=$(ls -l | wc -l)
作用:执行括号里的命令,并把命令的输出结果赋值给变量 result。 

$(...) 是 Shell 推荐的命令替换写法。

ls -l  列出当前目录下所有文件和文件夹的详细信息(包括权限、所有者、大小、修改时间等) -l是long长格式
wc -l:统计输入内容的行数(wc 是 word count,-l 参数表示只统计行数  lines 行数)
|:管道符,把前一个命令的输出,作为后一个命令的输入。

ls -l | wc -l 的作用就是:统计当前目录下有多少个文件和子目录(因为 ls -l 输出的每一行对应一个文件 / 目录)          ls -l 将当前目录里列出的文件和子目录传递给管道符后面的对象进行统计行数

可以理解成 “捕获命令的输出,存到变量里备用”。

命令替换 看起来像 普通赋值?

看赋值右边有没有 $(命令) —— 有就是命令替换,没有就是普通赋值

测试命令:test -f file.txt && echo "文件存在"
作用:test 命令(或等价的 [ ])用来判断条件是否成立,比如文件是否存在、变量是否为空等。
-f 是一个测试选项,意思是 “判断是否为普通文件”。

逻辑短路:&& 和 ||

Shell 里的 && 和 || 不是用来判断 “两个命令的结果都满足 / 满足一个”,而是根据第一个命令的执行结果,决定是否执行第二个命令

逻辑与:只有 command1 执行成功(返回状态码 0),才会执行 command2
逻辑或:只有 command1 执行失败(返回非 0 状态码),才会执行 command2

计时:time sleep 2

time 命令会跟踪并显示后面命令的执行时间,包括实际时间、用户 CPU 时间和系统 CPU 时间。

例如:

user:CPU 在用户态执行的时间(这里是 0m0.010s)
sys:CPU 在内核态执行的时间

6.语法规范与最佳实践

        推荐在绝大多数场景下都加双引号,Shell 脚本里有个不成文的原则:“当你不确定要不要加双引号时,就加上”。

        

脚本开头加 set -euo pipefail(增强健壮性)

-u 是 nounset 由 no(表示 “不”)和 unset(表示 “未设置”)组合而来的,字面意思就是 “不允许未设置(的变量)”

在 Shell 的默认行为中,管道 cmd1 | cmd2 的返回值只由最后一个命令 cmd2 的退出状态决定,而不管前面的 cmd1 是否成功执行。

这种默认行为的风险在于:
上游命令(比如 cmd1)发生错误时,完全不知道,因为整个管道的返回值是由下游命令决定的。
这会导致脚本在错误发生时继续执行,从而引发更隐蔽的逻辑错误。

开启 set -o pipefail 后,管道的返回值会变成管道中第一个失败命令的退出状态,只要有一个命令失败,整个管道就返回失败。

四、总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值