解码Shell 脚本编程

news/2025/10/30 20:38:49/文章来源:https://www.cnblogs.com/YouEmbedded/p/19172545

Shell 脚本基础

编程语言的分类

编程语言主要分为编译型语言解释型语言,核心区别在于代码执行前是否需要 “编译” 步骤:

类型 核心特点 优势 劣势 典型例子
编译型语言 平台相关编译器将源码一次性翻译成机器码(可执行文件),运行时直接执行机器码 执行效率高、对硬件掌控力强 跨平台性差、代码复杂度高、易出现安全漏洞 C/C++、Java(半编译)
解释型语言 平台无关解释器逐行读取源码并直接执行,无需编译步骤 跨平台性好、易用性高、安全性强 执行效率低、消耗系统资源多 Shell、Python、JS

脚本解释器

Shell 脚本是解释型语言,需通过脚本解释器执行。Linux 系统支持多种解释器,常见类型如下:

  • bash:Bourne Again Shell,Linux 默认解释器,功能最丰富
  • sh:Bourne Shell,早期标准解释器,功能简单
  • dash:Debian Almquist Shell,轻量级解释器,比 bash 快
  • csh:C Shell,语法类似 C 语言,较少用

查看系统支持的解释器

通过读取/etc/shells文件(系统级配置文件)查看所有支持的解释器:

cat /etc/shells  # cat=concatenate,拼接并输出文件内容
# 输出示例:
# /bin/sh
# /bin/dash
# /bin/bash
# /bin/rbash

查看当前使用的解释器

系统默认只使用一种解释器,通过环境变量$SHELL查看:

echo $SHELL  # echo=print,输出变量值;$SHELL存储当前解释器路径
# 输出示例:/bin/bash(表示当前用bash解释器)

bash 解释器的工作机制

bash 本质是一个应用程序,运行后成为进程,核心是一个 “死循环”:

  • 标准输入(stdin,如键盘) 或指定脚本文件读取命令;
  • 解释命令(需符合 bash 语法);
  • 将结果输出到标准输出(stdout,如终端) 或指定文件;
  • 重复步骤 1-3,直到用户退出(如输入exit)。

脚本的固定格式

Shell 脚本文件的默认后缀是.sh,核心格式需满足两点:

指定解释器(Shebang 语法)

脚本第一行必须用#!(称为 Shebang)指定解释器路径,告诉系统 “用哪个解释器执行该脚本”。格式:#!/bin/bash(指定 bash 解释器)

  • 若省略#!,系统会使用默认解释器(通常是 bash);
  • 其他解释器路径示例:#!/bin/sh(指定 sh)、#!/usr/bin/python3(指定 Python3)。

创建脚本文件

touch(创建空文件)或vi/vim(创建并编辑)创建脚本:

touch demo.sh  # 创建空脚本文件
vim demo.sh    # 用vim编辑脚本(推荐,直接写内容)

示例:最小脚本

#!/bin/bash  # 指定bash解释器
echo "Hello Shell!"  # 输出内容到终端

脚本的注释

注释用于说明脚本功能,提高可读性,Shell 脚本中仅支持单行注释,用#开头(#后的内容会被解释器忽略)。

注释示例

#!/bin/bash
# 这是单行注释:脚本功能是输出当前时间
echo "当前时间:"  # 行内注释:输出时间提示
date  # date命令:显示系统当前时间

脚本的输入与输出

脚本支持 “获取用户输入” 和 “输出内容到终端 / 文件”,核心命令是read(输入)和echo(输出)。

输入:read 命令

read用于从标准输入(键盘) 读取一行数据,可直接存入变量(无需提前定义变量)。语法:read [选项] 变量名

  • 常用选项:p "提示信息"(读取前显示提示,无需单独用echo)、t 秒数(设置输入超时)。

输入示例

#!/bin/bash
# 示例1:基础输入(无提示)
read name  # 读取用户输入,存入变量name(用户按回车结束输入)
echo "你好,$name!"  # 输出变量值($符号获取变量)# 示例2:带提示的输入(-p选项)
read -p "请输入你的年龄:" age  # 显示提示,读取年龄存入age
echo "你的年龄是:$age"# 示例3:超时输入(-t选项)
read -t 5 -p "5秒内输入你的爱好:" hobby  # 5秒内未输入则超时
if [ -z "$hobby" ]; then  # 判断hobby是否为空(-z:空字符串为真)echo "输入超时!"
elseecho "你的爱好是:$hobby"
fi

输出:echo 命令

echo用于将内容输出到标准输出(终端),支持普通字符串和变量。语法:echo [选项] 内容/变量

  • 常用选项:n(不换行输出,默认换行)、e(支持转义字符,如\n换行、\t制表符)。

输出示例

#!/bin/bash
# 示例1:输出普通字符串
echo "Hello World!"  # 输出后自动换行# 示例2:输出变量(需用$符号)
name="Shell"
echo "Hello $name!"  # 输出:Hello Shell!# 示例3:不换行输出(-n选项)
echo -n "请等待:"
sleep 2  # sleep:暂停2秒
echo "完成!"  # 输出:请等待:完成!(两行内容在同一行)# 示例4:支持转义字符(-e选项)
echo -e "姓名\t年龄\t性别"  # \t:制表符(对齐)
echo -e "张三\t20\t男\n李四\t19\t女"  # \n:换行

Shell 脚本的变量

Shell 是弱类型语言,定义变量时无需指定数据类型(默认是字符串),核心规则需注意 “赋值符号无空格”。

变量定义规则

  • 变量名只能包含字母、数字、下划线,且不能以数字开头(如name1合法,1name非法);
  • 赋值符号=两边不能有空格(如data=10合法,data = 10非法);
  • 若变量值包含空格或特殊字符,需用双引号""包裹(如data="Hello World")。

变量定义示例

#!/bin/bash
# 普通变量(无空格)
age=20  # 数值型(本质是字符串,运算时需转换)
name="Tom"  # 字符串型(无空格可省略引号,如name=Tom)# 带空格的变量(必须用双引号)
info="I am a student"  # 正确:空格被识别为值的一部分
# info=I am a student  # 错误:解释器会把am、a当作命令# 输出变量($符号)
echo "姓名:$name"
echo "年龄:$age"
echo "信息:$info"

变量的引用技巧

  • 引用普通变量:直接用$变量名(如$name);

  • 引用长变量或复杂表达式:用${变量名}(避免歧义),示例:

    data="Hello"
    echo "${data}World"  # 输出:HelloWorld(若写$dataWorld,解释器会找dataWorld变量,导致错误)
    

Shell 脚本的通配符

通配符是 “匹配文件 / 路径的特殊字符”,用于快速筛选文件(如 “所有.c文件”),仅在支持通配符的命令中生效(如lsfind)。

image

示例:通配符使用

ls *.sh    # 列出当前目录所有.sh脚本文件
ls /home/[tT]*  # 列出/home目录下以t或T开头的文件
ls ???.txt # 列出当前目录下文件名3个字符、以.txt结尾的文件(如123.txt)

wc 命令

#基本格式
wc [选项] [文件...]

wc 常用选项(对应 “统计维度”)

wc 命令的核心功能是统计文本的 3 类信息,通过选项控制输出哪些维度(默认输出 “行数、单词数、字节数 + 文件名”):

选项 功能描述
-l 仅统计文件的行数
-w 仅统计文件的单词数(以空白符分隔)
-c 仅统计文件的字节数(与文件大小一致)
-m 仅统计文件的字符数(支持中文等多字节字符)
-L 仅统计文件中最长行的长度(字节数)

wc 命令的使用示例

  • 统计单个文件的所有维度(默认输出):

    wc test.txt
    # 输出格式:行数  单词数  字节数  文件名
    # 示例输出:15   48   320   test.txt
    
  • 仅统计多个文件的行数总和:

    wc -l a.txt b.txt
    # 输出:5 a.txt (a.txt 5行)
    #      8 b.txt (b.txt 8行)
    #     13 总用量 (两行总和)
    
  • 统计管道传递的内容(无文件参数):

    # 用 echo 输出内容,通过管道传给 wc 统计单词数
    echo "hello world from wc" | wc -w
    # 输出:4 (共4个单词)
    

Shell 脚本的管道

管道(|)是 Shell 的核心机制,用于 “将一个命令的输出作为另一个命令的输入”,实现 “多命令组合”。核心逻辑:cmd1 | cmd2 → cmd1的输出 → 作为cmd2的输入 → 执行cmd2

管道的两种常见模式

管道是 Shell 脚本中连接多个命令的核心机制,通过传递前一个命令的结果给后一个命令,可将多个简单命令组合成复杂功能,其常见执行模式分为以下两种:

  • 模式:cmd1 | cmd2
    • 核心作用:将cmd1的输出直接作为cmd2的输入,实现 “前命令输出→后命令输入” 的流转。
    • 示例find ~ -name hello.c | grep "world"
    • 执行逻辑:首先通过find ~ -name hello.c在用户家目录(~)下查找所有名为hello.c的文件,该命令会输出找到的hello.c文件完整路径;接着通过管道符号|,将这些文件路径传递给grep "world"grep "world"会以这些路径对应的文件内容为输入,筛选并输出包含 “world” 字符串的行。
  • 模式:cmd1 | xargs cmd2
    • 核心作用:借助xargs工具,将cmd1的输出转换为cmd2的命令行参数,实现 “前命令输出→后命令参数” 的转换。
    • 示例find ~ -name hello.c | xargs grep "world"
    • 执行逻辑:先通过find ~ -name hello.c查找用户家目录下所有hello.c文件,输出这些文件的路径(每行一个路径);随后xargs接收这些路径并进行处理,将其拼接成grep "world"的命令行参数;最终执行类似grep "world" 路径1/hello.c 路径2/hello.c的命令,在所有找到的hello.c文件中搜索并输出包含 “world” 字符串的行。

示例:管道实战

# 示例1:统计当前目录下.sh文件的数量(ls → grep → wc)
ls *.sh | wc -l  # ls *.sh:列出.sh文件;wc -l:统计行数(即文件数)# 示例2:查找系统中包含“root”的进程(ps → grep)
ps aux | grep "root"  # ps aux:查看所有进程;grep "root":筛选包含root的进程

Shell 脚本的重定向

重定向用于 “改变命令的输入 / 输出来源”,默认输入是键盘(stdin)、输出是终端(stdout),重定向可改为文件。

image

示例:重定向实战

# 覆盖写:将当前时间写入time.txt(覆盖原有内容)
date > time.txt# 追加写:将当前用户追加到user.txt
whoami >> user.txt  # whoami:显示当前用户名# 错误重定向:执行错误命令,错误信息写入log
rm /test.txt **2**> error.log  # 若/test.txt不存在,错误信息存入error.log

Shell 脚本的反引号

反引号( )的作用是 “先执行括号内的命令,再将命令结果作为字符串使用”,等价于$()(推荐用$(),可读性更高)。

核心用法

  • 格式 1:var=命令``
  • 格式 2:var=$(命令)(推荐,支持嵌套)

示例:反引号实战

#!/bin/bash
# 示例1:将ls命令结果存入变量(当前目录文件列表)
file_list=$(ls)  # 执行ls,结果存入file_list
echo "当前目录文件:$file_list"# 示例2:嵌套使用(推荐$())
# 统计当前目录下.c文件的数量:ls *.c → wc -l
c_count=$(ls *.c | wc -l)echo "当前目录.c文件数量:$c_count"# 示例3:反引号嵌套在双引号中
echo "当前日期:$(date +%Y-%m-%d)"  # 输出:当前日期:2024-05-20

Shell 脚本的数值运算

Shell 变量默认是字符串类型,若需进行数值运算(加、减、乘、除等),需用专门的语法,常用双括号$(( ))(兼容 bash 和 sh)。

数值运算语法

  • 格式:result=$(( 表达式 ))
  • 支持的运算符:+(加)、(减)、(乘)、/(除,整数除法)、%(取余)、++(自增)、-(自减)。

示例:数值运算

#!/bin/bash
# 础运算
a=10
b=5
sum=$((a + b))    # 加法:15
diff=$((a - b))   # 减法:5
prod=$((a * b))   # 乘法:50(注意:不能用*,会被当作通配符)
quot=$((a / b))   # 除法:2(整数除法,10/3=3)
mod=$((a % b))    # 取余:0echo "和:$sum,差:$diff,积:$prod,商:$quot,余:$mod"# 自增自减
c=3
((c++))  # c变成4(等价于c=$((c+1)))
echo "c自增后:$c"# 表达式直接输出
echo "10+20= $((10+20))"  # 输出:10+20= 30

Shell 脚本的函数

函数用于将 “重复执行的代码块” 封装成一个模块,便于调用和维护,Shell 函数无需定义参数列表和返回值类型。

函数定义语法

函数名() {# 函数体:要执行的命令[命令1][命令2]# 返回值(可选):用return,只能返回数值(0-255,0表示成功)[return 数值]
}

函数关键规则

  • 函数名不能与系统命令(如lsecho)或脚本关键字(如iffor)冲突;
  • 函数无需参数列表,但可接收参数(调用时传递,如func arg1 arg2);
  • 函数参数引用:与脚本外部传参一致($1第一个参数、$2第二个参数、$#参数个数、$*所有参数);
  • 函数返回值:用return返回数值(默认返回最后一条命令的执行结果,0 成功,非 0 失败),用$?获取返回值。

函数示例

#!/bin/bash
# 示例1:无参数、无返回值的函数
print_hello() {echo "------------------------"echo "Hello from function!"echo "------------------------"
}# 调用函数(直接写函数名)
print_hello# 示例2:带参数、有返回值的函数(计算两个数的和,返回结果)
add() {# 判断是否传递了2个参数($#是参数个数)if [ $# -ne 2 ]; thenecho "错误:需传递2个参数!"return 1  # 返回1,表示错误fi# 计算和($1是第一个参数,$2是第二个参数)sum=$(( $1 + $2 ))echo "两数之和:$sum"return 0  # 返回0,表示成功
}# 调用函数并传递参数(arg1=10,arg2=20)
add 10 20
# 获取函数返回值($?是上一个命令的返回值)
echo "函数返回值(0=成功):$?"# 示例3:函数参数的遍历($*表示所有参数)
print_args() {echo "传递的参数个数:$#"echo "所有参数:$*"# 遍历参数(用for循环)for arg in $*; doecho "参数:$arg"done
}# 调用函数,传递3个参数
print_args "a" "b" "c"

函数调用输出

-----------------------
Hello from function!
------------------------
两数之和:30
函数返回值(0=成功):0
传递的参数个数:3
所有参数:a b c
参数:a
参数:b
参数:c

Shell 脚本控制语句

控制语句用于改变代码执行流程,Shell 支持分支语句(if、case)和循环语句(while、for、until),语法与 C 语言不同,需注意格式。

分支语句

分支语句根据 “条件是否成立” 执行不同代码块,分为 “两种情况判断”(if-else)和 “多种情况判断”(case)。

两种情况判断:if-else

适用于 “条件成立执行 A,不成立执行 B” 的场景,核心是if [ 表达式 ][ ]两边必须有空格)。

语法格式

if [ 条件表达式 ]; then# 条件成立时执行的代码命令1命令2
else# 条件不成立时执行的代码命令3命令4
fi  # if的逆序,标记分支结束(必须写)

关键说明

  • test 命令(条件判断核心工具)
    test是 Shell 内置命令,功能是 “检查文件属性”“比较数值 / 字符串”,最终通过返回值告知判断结果(0表示条件成立,非0表示不成立)。它与单中括号[ ]完全等价([ 表达式 ]本质是test 表达式的简写),是if语句条件的底层实现。

    • 核心语法

      语法格式 1(test) 语法格式 2([ ],更常用) 说明
      test 表达式 [ 表达式 ] 两者功能完全一致,仅写法不同
      test -f ~/test.txt [ -f ~/test.txt ] 判断~/test.txt是否为普通文件
      test 10 -gt 5 [ 10 -gt 5 ] 判断 10 是否大于 5
  • 分号;then可与if同行,需用;分隔(如if [ ... ]; then),也可换行写then

  • 条件表达式支持:文件判断、数值比较、字符串比较。

常用条件表达式

判断类型 表达式(语法规范) 核心含义(含判断条件) 实际示例(带变量/场景)
文件判断 -f 路径 判断路径存在且为普通文件(非目录、设备文件等) file="/home/test.txt"; [ -f "$file" ](若test.txt存在且是普通文件,结果为真)
-d 路径 判断路径存在且为目录(非文件、链接等) dir="/home/docs"; [ -d "$dir" ](若docs存在且是目录,结果为真)
-e 路径 判断路径是否存在(不区分文件/目录,仅验证“存在性”) path="/home/old.log"; [ -e "$path" ](若old.log存在,无论类型,结果为真)
数值比较 $a -eq $b 数值$a 等于 $b(eq=equal,仅用于整数,不可比较浮点数) a=20; b=20; [ $a -eq $b ](20等于20,结果为真)
$a -gt $b 数值$a 大于 $b(gt=greater than) a=30; b=25; [ $a -gt $b ](30大于25,结果为真)
$a -lt $b 数值$a 小于 $b(lt=less than) a=15; b=20; [ $a -lt $b ](15小于20,结果为真)
$a -ge $b 数值$a 大于等于 $b(ge=greater or equal) a=25; b=25; [ $a -ge $b ](25等于25,结果为真)
$a -le $b 数值$a 小于等于 $b(le=less or equal) a=18; b=20; [ $a -le $b ](18小于20,结果为真)
字符串比较 "$a" == "$b""$a" = "$b" 字符串$a$b 完全一致(建议加双引号,避免空格/空值导致语法错误) a="hello"; b="hello"; [ "$a" == "$b" ](字符串完全匹配,结果为真)
"$a" != "$b" 字符串$a$b 不完全一致 a="apple"; b="banana"; [ "$a" != "$b" ](字符串不匹配,结果为真)
-z "$a" 字符串$a 为空字符串(长度为0,需加双引号,否则空变量会被解析为无参数) a=""; [ -z "$a" ]a是空字符串,结果为真);a="test"; [ -z "$a" ](非空,结果为假)
-n "$a" 字符串$a 为非空字符串(长度≥1,必须加双引号,否则[ -n $a ]空变量会误判为真) a="shell"; [ -n "$a" ]a非空,结果为真);a=""; [ -n "$a" ](空,结果为假)
逻辑运算 [ 表达式1 ] && [ 表达式2 ] 逻辑与:两个表达式都为真,整体才为真(短路特性:表达式1假则不执行表达式2) file="/home/test.sh"; [ -f "$file" ] && [ -x "$file" ]test.sh是普通文件且可执行,才为真)
[ 表达式1 ] || [ 表达式2 ] 逻辑或:任一表达式为真,整体即为真(短路特性:表达式1真则不执行表达式2); dir="/home/docs"; [ -d "$dir" ] || mkdir -p "$dir"docs不存在则创建,存在则跳过)
! [ 表达式 ] 逻辑非:对表达式结果取反(真变假、假变真) file="/home/none.txt"; ! [ -f "$file" ]none.txt不是普通文件,结果为真)

示例 1:判断文件是否为普通文件

#!/bin/bash
# 功能:判断家目录下的test.txt是否为普通文件
file_path="~/test.txt"if [ -f $file_path ]; thenecho "该文件是普通文件"
elseecho "该文件不是普通文件或不存在"
fi

示例 2:判断输入整数是否在 20-50 之间

#!/bin/bash
# 功能:输入整数,判断是否在20-50之间
read -p "请输入一个整数:" num# 先判断输入是否为有效整数
#(正则表达式:^(匹配行的开头)[0-9]+(匹配前面的元素1 次或多次)$(匹配行的结尾) 表示全为数字)
if ! [[ $num =~ ^[0-9]+$ ]]; thenecho "输入不是有效的整数!"
elif [ $num -gt 20 ] && [ $num -lt 50 ]; thenecho "你输入的整数是:$num"
elseecho "不是"
fi

多种情况判断:case

适用于 “多个固定值匹配” 的场景(如 “输入 1 执行 A,输入 2 执行 B”),比多分支 if-elif 更简洁。

语法格式

case 测试值 in匹配值1)# 测试值 == 匹配值1 时执行命令1命令2;;  # 结束当前分支(必须写,两个分号)匹配值2)# 测试值 == 匹配值2 时执行命令3命令4;;匹配值3)# 测试值 == 匹配值3 时执行命令5;;*)  # 默认分支:所有匹配值都不满足时执行(等价于C语言的default)命令6;;
esac  # case的逆序,标记分支结束(必须写)

关键说明

  • 测试值:通常是变量(如$num)或常量;
  • 匹配值:必须是字符串(数值也会被当作字符串处理);
  • ;;:表示 “当前分支执行完毕,跳出 case”,不可省略;
  • ):默认分支,建议写上(处理异常输入)。

示例:输入 1-3 的整数,匹配执行

#!/bin/bash
# 功能:输入1-3的整数,输出对应信息
read -p "请输入1-3之间的整数:" numcase $num in1)echo "你输入的是1,执行操作A";;2)echo "你输入的是2,执行操作B";;3)echo "你输入的是3,执行操作C";;*)echo "输入错误!请输入1-3之间的整数";;
esac

多分支判断:if-elif-else

适用于 “多个条件依次判断” 的场景(如 “分数 >=90 优秀,>=80 良好”),语法是在 if-else 中嵌套 elif。

语法格式

if [ 条件1 ]; then命令1  # 条件1成立执行
elif [ 条件2 ]; then命令2  # 条件1不成立、条件2成立执行
elif [ 条件3 ]; then命令3  # 条件1、2不成立、条件3成立执行
else命令4  # 所有条件都不成立执行
fi

示例:判断学生成绩等级

#!/bin/bash
# 功能:输入成绩(0-100),判断等级
read -p "请输入学生成绩(0-100):" score# 先判断成绩是否合法
if ! [[ $score =~ ^[0-9]+$ ]] || [ $score -lt 0 ] || [ $score -gt 100 ]; thenecho "成绩输入错误!请输入0-100的整数"
elif [ $score -ge 90 ]; thenecho "成绩等级:优秀"
elif [ $score -ge 80 ]; thenecho "成绩等级:良好"
elif [ $score -ge 60 ]; thenecho "成绩等级:及格"
elseecho "成绩等级:不及格"
fi

循环语句

循环语句用于 “重复执行代码块”,Shell 支持三种循环:while(条件成立循环)、for(枚举列表循环)、until(条件不成立循环)。

while 循环

适用于 “条件成立时重复执行” 的场景(如 “循环 10 次”“直到用户输入 exit”),核心是 “先判断条件,再执行循环体”。

语法格式

while [ 条件表达式 ]; do# 条件成立时执行的循环体命令1命令2# (可选)更新条件变量(避免死循环)变量更新
done  # 标记循环结束(必须写)

关键说明

  • 死循环:若条件表达式永远为真(如while truewhile [ 1 ]),循环会一直执行,需用break跳出;
  • 循环控制:break(跳出整个循环)、continue(结束当前循环,进入下一次)。

示例 1:循环输出 1-5(基础循环)

#!/bin/bash
# 功能:循环输出1-5
i=1  # 初始化循环变量while [ $i -le 5 ]; do  # 条件:i<=5echo "当前数值:$i"i=$((i + 1))  # 更新变量(i自增1)
doneecho "循环结束"

示例 2:死循环(直到输入 exit 退出)

#!/bin/bash
# 功能:死循环,输入exit退出
while true; do  # 条件永远为真(死循环)read -p "请输入命令(输入exit退出):" cmdif [ "$cmd" == "exit" ]; thenecho "退出循环"break  # 跳出循环fiecho "你输入的命令是:$cmd"
done

示例 3:循环控制(break 和 continue)

#!/bin/bash
# 功能:输出1-5,跳过3,遇到4退出
i=1while [ $i -le 5 ]; doif [ $i -eq 3 ]; theni=$((i + 1))continue  # 跳过当前循环(不输出3)fiif [ $i -eq 4 ]; thenecho "遇到4,退出循环"break  # 跳出整个循环fiecho "当前数值:$i"i=$((i + 1))done
# 输出结果:1、2、遇到4,退出循环

for 循环

适用于 “枚举列表中的元素,逐个执行” 的场景(如 “遍历目录下的所有文件”“遍历数组”),循环次数等于列表中元素的个数。

语法格式

for 变量名 in 枚举列表; do# 每次循环,变量取列表中的一个元素命令1命令2
done

关键说明

  • 枚举列表:元素之间用空格分隔,可是直接写的列表(如1 2 3)、变量(如$file_list)、命令结果(如$(ls));
  • 若省略in 枚举列表,默认枚举$@(脚本外部传参列表)。

示例 1:遍历固定列表(输出周一到周五)

#!/bin/bash
# 功能:遍历列表,输出工作日
weekdays="周一 周二 周三 周四 周五"for day in $weekdays; doecho "工作日:$day"
done

示例 2:遍历命令结果(输出当前目录文件)

#!/bin/bash
# 功能:遍历当前目录下的所有文件,输出文件名
echo "当前目录文件:"# $(ls):执行ls命令,结果作为枚举列表(文件列表)
for file in $(ls); doecho "- $file"
done

示例 3:遍历数字范围(输出 1-10)

#!/bin/bash
# 功能:遍历1-10的数字(用{start..end}表示范围)
for i in {1..10}; doecho "数字:$i"
done# 等价写法(C语言风格,bash支持)
# for ((i=1; i<=10; i++)); do
#     echo "数字:$i"
# done

until 循环

与 while 循环相反,适用于 “条件不成立时重复执行” 的场景(即 “直到条件成立才退出循环”),语法与 while 类似。

语法格式

until [ 条件表达式 ]; do# 条件不成立时执行的循环体命令1命令2变量更新
done

关键区别

  • while:条件成立 → 执行循环体;
  • until:条件成立 → 退出循环体(条件不成立才执行)。

示例:until 循环输出 1-5

#!/bin/bash
# 功能:until循环输出1-5(条件i>5时退出)
i=1until [ $i -gt 5 ]; do  # 条件:i>5(不成立时执行循环)echo "当前数值:$i"i=$((i + 1))
doneecho "循环结束"
# 输出结果与while示例1一致:1-5

Shell 脚本外部传参

脚本启动时可携带 “外部参数”(如./demo.sh arg1 arg2),脚本内部通过特殊变量获取这些参数,实现 “动态传入数据”。

外部传参的特殊变量

Shell 预定义了一组特殊变量,用于获取外部传参信息:

特殊变量 含义 示例(脚本:./demo.sh a b c,或扩展场景) 注意事项
$0 脚本的完整调用路径/文件名(调用方式不同,路径显示不同:相对路径调用含相对路径,绝对路径调用含绝对路径) - 调用:./demo.sh a b c$0./demo.sh
- 调用:/home/user/demo.sh a b c$0/home/user/demo.sh
- 提取纯文件名:basename $0(如demo.sh);
- 若需统一路径格式,可结合readlink -f $0获取绝对路径
$1 第一个外部参数 $1a - 无参数时$1为空;
- 若参数含空格,需用双引号包裹(如./demo.sh "a b" c,则$1a b
$2 第二个外部参数 $2b $1,参数索引从1开始,需注意参数顺序
$n n个外部参数(n≥10必须用${n}语法,否则会被解析为$1后接字符0 调用:./demo.sh 1 2 3 ... 10${10}10;若写$10则会被解析为$1的内容+0(如$11$1010 n≥10必须加花括号,这是语法强制要求
$# 外部参数的总个数(仅统计参数数量,不包含脚本名$0 $#3(参数abc共3个);无参数调用时$#0 可用于脚本参数校验(如if [ $# -ne 2 ]; then echo "请输入2个参数"; exit 1; fi
$* 所有外部参数合并为一个字符串(参数间以空格分隔,等价于"$1 $2 $3..." 遍历示例(不加双引号):
for arg in $*; do echo $arg; done → 输出a
b
c(但逻辑上视为单字符串拆分)
加双引号:"$*""a b c"(整体字符串)
- 不加双引号时,与$@表现类似;
- 加双引号时,所有参数强制合并为一个整体字符串,遍历场景极少使用
$@ 所有外部参数保持独立元素(每个参数为独立个体,遍历时分隔为多个元素) 遍历示例(加双引号,推荐写法):
for arg in "$@"; do echo $arg; done → 输出a
b
c(每个参数独立)
- 不加双引号时,与$*表现类似;
- 加双引号时,每个参数仍保持独立(遍历参数时必用此写法,避免参数含空格时被拆分)
$? 上一个命令的返回值0表示成功,非0表示失败;不同命令失败返回值含义不同,需单独记忆) - 脚本成功执行后:$?0
- 执行grep "test" nofile.txt(文件不存在)后:$?2grep特有错误码)
- 执行grep "test" file.txt(无匹配)后:$?1
- 需立即查看,否则会被后续命令的返回值覆盖;
- 常见命令返回值:ls文件不存在返回2cp权限不足返回1

外部传参示例

示例 1:获取并输出所有外部参

#!/bin/bash
# 功能:输出外部传参信息
echo "脚本文件名:$0"
echo "外部参数总个数:$#"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "所有参数($*):$*"
echo "所有参数($@):$@"# 遍历所有参数(用$@)
echo "遍历所有参数:"
for arg in $@; doecho "- $arg"
done

调用与输出

# 调用脚本,传递3个参数:a、b、c
./demo.sh a b c# 输出结果:
# 脚本文件名:./demo.sh
# 外部参数总个数:3
# 第一个参数:a
# 第二个参数:b
# 所有参数($*):a b c
# 所有参数($@):a b c
# 遍历所有参数:
# - a
# - b
# - c

示例 2:传递路径,输出路径下的文件

#!/bin/bash
# 功能:传递一个路径参数,输出该路径下的文件
# 判断是否传递了1个参数
if [ $# -ne 1 ]; thenecho "使用方法:$0 <路径>"  # $0显示脚本名,提示正确用法exit 1  # 退出脚本,返回1(表示错误)
fi# 判断参数是否为合法目录
path=$1
if [ -d $path ]; thenecho "路径$path下的文件:"ls $path  # 列出路径下的文件
elseecho "错误:$path 不是合法目录或不存在"exit 1
fi

调用与输出

# 传递合法目录(/home)
./demo.sh /home# 输出结果:
# 路径/home下的文件:
# lmx  test  user

正则表达式与过滤器

正则表达式(Regular Expression,简称 RE)是 “用元字符组合成的匹配模式”,用于精准筛选文本;过滤器是 “按行处理文本的工具”,常与正则表达式结合使用,核心工具:grepawksed

image

正则表达式基础

正则表达式用于 “匹配文本中的字符或字符串”,比通配符更灵活、更复杂,核心是 “元字符”(具有特殊含义的字符)。

常用元字符表

image

关键说明

  • 扩展正则:元字符+?{}|需用 “扩展正则” 支持,在grep中需加E选项(或用egrep);
  • 转义字符:若需匹配元字符本身(如.、),需用\转义(如\.匹配 “.”,\*匹配 “*”)。

过滤器:grep

grep(Global Regular Expression Print)是最常用的过滤器,功能是 “在文件或输入中查找匹配正则表达式的行,并输出这些行”。

grep 基本语法

grep [选项] "正则表达式" 文件名/目录

常用选项

选项 含义 示例
-i 忽略大小写(不区分大小写匹配) grep -i "abc" file.txt
-n 显示匹配行的行号 grep -n "abc" file.txt
-v 反向匹配(输出不匹配的行) grep -v "abc" file.txt
-E 支持扩展正则(等价于egrep grep -E "a+" file.txt
-r 递归查找(遍历子目录,仅对目录有效) grep -r "abc" /home
-l 仅显示匹配的文件名(不显示行内容) grep -l "abc" *.txt
--include=*.ext 仅查找指定后缀的文件(递归时用) grep -r --include=*.c "main" /home

grep 示例(基于文件 grade.txt)

假设grade.txt内容如下:

NAME    ENROL   DATE    CLASS   LEVEL   AGE     SCORE
M.Tansley   48311   05/2013 Green   2       8       90
J.Lulu      48317   04/2012 Green   3       12      88
P.bunny     48      02/2013 Yellow  9       70      70
J.Troll     4842    09/2013 Brown-1 11      95      95
M.J         3892    06/2011 white   8       70      70
L.Tansley   4712    05/2013 Brown-2 10      85      85
Vincent     4712    07/2012 Black   11      87      87

示例 1:查找包含 “Brown” 的行(基础匹配)

grep "Brown" grade.txt  # 匹配包含“Brown”的所有行
# 输出:
# J.Troll     4842    09/2013 Brown-1 11      95      95
# L.Tansley   4712    05/2013 Brown-2 10      85      85

示例 2:查找以 “M” 开头的行(行首匹配^

grep -n "^M" grade.txt  # -n显示行号,^M匹配行首为M的行
# 输出:
# 2:M.Tansley   48311   05/2013 Green   2       8       90
# 6:M.J         3892    06/2011 white   8       70      70

示例 3:查找 SCORE 为 90 或 95 的行(扩展正则|

# -E支持扩展正则,90$|95$匹配行尾为90或95的行(SCORE在最后一列)
grep -E -n "90$|95$" grade.txt
# 输出:
# 2:M.Tansley   48311   05/2013 Green   2       8       90
# 5:J.Troll     4842    09/2013 Brown-1 11      95      95

示例 4:递归查找所有.c 文件中的 “main” 函数

# -r递归,--include=*.c仅找.c文件,"main"匹配包含main的行
grep -r --include=*.c "main" /home/lmx/project

过滤器:awk

awk(得名于发明者 Aho、Kernighan、Weinberger)是功能强大的文本处理语言,核心是 “按行处理文本,按列提取数据”,支持模式匹配和复杂逻辑。

awk 基本语法

awk [选项] '模式1 {动作1} 模式2 {动作2} ...' 文件名
  • 模式:条件(如行号NR==1、正则$0~/Brown/),省略模式则 “无条件执行动作”;
  • 动作:要执行的命令(如print $1输出第一列、printf "%.2f" $3格式化输出);
  • 选项:F 分隔符(指定列分隔符,默认是空格 / Tab)、v 变量=值(定义变量)。

awk 核心内建变量

awk 预定义了内建变量,用于获取文本的行 / 列信息:

内建变量/模式 核心含义 示例(处理文件:grade.txt 第2行内容为 M.Tansley 48311 05/2013 Green 2 8 90
$0 当前行的完整内容(未分割的整行文本,保留原始空格结构) 处理第2行时,$0M.Tansley 48311 05/2013 Green 2 8 90(与原行完全一致)
$n 当前行的第n列(列索引从1开始,按输入分隔符FS分割;$NF特指最后一列) $1 → "M.Tansley"(第1列:姓名);$7 → "90"(第7列:分数);$NF → "90"(最后一列,无需记列数)
FS 输入列分隔符(Field Separator),默认是连续空格/Tab(自动忽略多余分隔符) 处理/etc/passwd(冒号分隔):BEGIN { FS=":" },此时/etc/passwd第1行$1 → "root"(用户名);
默认情况:grade.txt中多空格视为1个分隔符,不影响列索引
OFS 输出列分隔符(Output Field Separator),默认是单个空格 设`OFS="
NR 总行号(Number of Records),多文件处理时累加(跨文件不重置) 仅处理grade.txt:第2行NR → 2;
先处理a.txt(共5行)再处理grade.txtgrade.txt第2行NR → 5+2=7
FNR 当前文件的行号,多文件处理时切换文件重置(仅对当前文件计数) 处理grade.txt第2行:FNR → 2;
切换到b.txt后:b.txt第1行FNR → 1(重新从1开始计数)
NF 当前行的总列数(Number of Fields,按FS分割后的列总数) grade.txt第2行共7列,故NF → 7;若某行内容为Alice 85(2列),则该行NF → 2
FILENAME 当前正在处理的文件名(多文件处理时自动切换为当前文件) 处理grade.txt时,FILENAME → "grade.txt";处理test.txt时,FILENAME → "test.txt"
BEGIN 特殊模式(非变量):读取任何文件内容前执行一次(初始化操作) 示例:`awk 'BEGIN { FS=":"; OFS="
END 特殊模式(非变量):所有文件内容读取完毕后执行一次(收尾操作) 示例:awk '{ sum+=$7 } END { print "总分数:", sum; print "平均分数:", sum/NR }' grade.txt → 计算所有行第7列的总和与平均值

awk 示例(基于 grade.txt)

示例 1:完整命令 + 执行逻辑

基础版:仅输出第 1 列(NAME)和第 7 列(SCORE)

# 命令:模式省略(无条件执行),动作:打印第1列($1)和第7列($7)
awk '{ print $1, $7 }' grade.txt

优化版:添加表头说明 + 规范分隔符(更易读)

如果想让输出更清晰(比如用 | 分隔列,或单独保留表头),可以补充 BEGIN 模式设置输出分隔符:

# 命令:读取文件前设输出分隔符为" | ";打印第1列和第7列
awk 'BEGIN { OFS=" | " } { print $1, $7 }' grade.txt

执行结果说明

基础版输出(默认空格分隔列):

NAME        SCORE
M.Tansley   90
J.Lulu      88
P.bunny     70
J.Troll     95
M.J         70
L.Tansley   85
Vincent     87

优化版输出(| 分隔列,更整齐):

NAME        | SCORE
M.Tansley   | 90
J.Lulu      | 88
P.bunny     | 70
J.Troll     | 95
M.J         | 70
L.Tansley   | 85
Vincent     | 87

关键逻辑解释

  • 模式省略awk 后直接写 { print $1, $7 },没有指定模式(如 NR==1 或 $0~/Brown/),意味着对文件的每一行都执行该动作(包括表头);
  • 列的引用$1 代表当前行的第 1 列(NAME),$7 代表第 7 列(SCORE),列的编号从 1 开始($0 是整行内容);
  • 输出分隔符 OFS:默认用空格分隔打印的列,通过 BEGIN { OFS=" | " } 可自定义分隔符(BEGIN 模式确保读取文件前就生效)。

如果只想输出 “数据行”(去掉表头),可添加模式 NR>1(行号大于 1 时执行):

awk 'NR>1 { print $1, $7 }' grade.txt  # 仅输出第2-8行的NAME和SCORE

综合示例

示例一:统计指定目录下所有普通文件的行数总和

#!/bin/bash# 处理目录参数:若用户未传入目录,默认使用当前目录
# $1 代表脚本接收的第一个参数,-z 判断参数是否为空
if [ -z "$1" ]; thentarget_dir="."  # 默认目录:当前工作目录
elsetarget_dir="$1" # 用户指定的目录
fi# 验证目录是否存在:若目录不存在,提示错误并退出脚本
if [ ! -d "$target_dir" ]; thenecho "错误:目录 '$target_dir' 不存在或不是一个有效目录!"exit 1  # 非0退出码表示脚本执行失败
fi# 核心逻辑:统计目录下所有普通文件的行数总和
# find 命令:递归查找目标目录下的普通文件(-type f)
# wc -l:统计每个文件的行数(输出格式为“行数 文件名”)
# {} + 表示批量传递文件
# {}:find 的占位符,代表 “当前找到的文件路径”
# +:批量执行标记,表示将 find 找到的所有文件一次性传给 wc 执行
# awk:提取每行的第一个字段(行数)并累加,最终输出总和
total_lines=$(find "$target_dir" -type f -exec wc -l {} + | awk '{sum += $1} END {print sum}')# 输出结果:展示统计的目录和总行数,提升可读性
echo "====================================="
echo "统计目录:$target_dir"
echo "所有文件的行数总和:$total_lines"
echo "====================================="

tr 命令

# 基本格式
tr [选项] [字符集1(SET1)] [字符集2(SET2)]
  • 字符集 1(SET1):定义 “待处理的字符”(如要替换、删除的字符),必填(除非仅用特定选项)。
  • 字符集 2(SET2):仅用于 “字符替换” 功能,定义 “替换后的目标字符”(与 SET1 按位置对应),非必选(删除、压缩时无需)。
  • 选项:控制字符处理逻辑(如删除、压缩、取反),非必选;默认功能为 “字符替换”。

tr 常用选项(对应 “处理逻辑”)

tr 命令的核心功能围绕 “字符替换、删除、压缩、取反” 展开,通过选项精准控制处理方式,默认(无选项)为 “字符替换”:

选项 功能描述
无选项 字符替换:将字符集 1 中的字符,按位置对应替换为字符集 2 中的字符
-d 字符删除:删除字符集 1 中包含的所有字符(无需字符集 2)
-s 重复压缩:将字符集 1 中连续重复的字符,压缩为单个字符(无需字符集 2)
-c 取反匹配:匹配 “不在字符集 1 中的所有字符”(需配合 -d/-s 使用,无单独功能)

tr 命令的使用示例

  • 字符替换(默认功能,无选项):按字符集位置对应替换,适合固定字符映射场景。

    tr 'a-z' 'A-Z'  # 将输入的小写字母转为大写
    # 配合管道使用示例:
    echo "hello tr" | tr 'a-z' 'A-Z'
    # 输出:HELLO TR(小写字母全部转为大写)
    
  • 字符删除(d 选项):删除字符集 1 中的字符,适合过滤无关字符(如数字、标点)。

    tr -d '0-9'  # 删除输入中的所有数字
    # 配合管道使用示例:
    echo "test123abc456" | tr -d '0-9'
    # 输出:testabc(数字 123、456 被删除)
    
  • 重复压缩(s 选项):压缩连续重复字符,适合清理冗余空白、标点。

    tr -s ' '  # 将输入中连续的空格压缩为单个空格
    # 配合管道使用示例:
    echo "hello   world   !" | tr -s ' '
    # 输出:hello world !(连续空格被压缩为单个)
    
  • 取反处理(c 配合 d/s):先匹配 “非字符集 1 的字符”,再执行删除 / 压缩,适合保留特定字符(如仅留字母)。

    # 示例1:保留字母,删除所有非字母(-c 取反,-d 删除)
    echo "hello!123world" | tr -d -c 'A-Za-z'
    # 输出:helloworld(非字母的 !、123 被删除)# 示例2:非字母转空格并压缩(-c 取反,-s 压缩)
    echo "Hello,World;;how   are you?" | tr -cs 'A-Za-z' ' '
    # 输出:Hello World how are you(非字母转空格,连续空格被压缩)
    

sort 命令

# 基本格式
sort [选项] [文件...]
  • 文件:可选参数,指定要排序的文件路径;若不指定文件,默认读取标准输入(如管道传递的内容)。
  • 选项:控制排序规则(如排序方式、字段、去重等),非必选;默认按 “字典序(ASCII 码顺序)” 升序排序。

sort 常用选项(对应 “排序规则”)

sort 命令的核心功能是对文本行进行排序,通过选项可控制排序方式(升序 / 降序)、依据(数值 / 字符)、字段等,默认按整行字典序升序排列:

选项 功能描述
-n 按 “数值” 排序(默认按字符字典序,避免 10 排在 2 前)
-r 按 “降序” 排序(默认升序)
-u 去重排序:仅保留排序后相同的行中的第一行
-k N 按 “第 N 个字段” 排序(字段默认以空格 / 制表符分隔,N 为正整数)
-t 分隔符 自定义字段分隔符(如 -t ',' 表示以逗号分隔字段)
-f 忽略大小写排序(默认区分大小写,如 A 排在 a 前)

sort 命令的使用示例

  • 默认排序(字典序升序):按每行字符的 ASCII 码顺序排序(字母 > 数字 > 符号,同字母按大小写排序)。

    # 输入内容(test.txt):
    # apple
    # Banana
    # 123
    # orangesort test.txt
    # 输出:
    # 123
    # Banana
    # apple
    # orange
    # 逻辑:数字(123) < 大写字母(Banana) < 小写字母(apple, orange)
    
  • 数值排序(n 选项):针对含数字的文本,按数值大小排序(解决字典序排序的问题)。

    # 输入内容(nums.txt):
    # 10
    # 2
    # 25
    # 5sort -n nums.txt
    # 输出:
    # 2
    # 5
    # 10
    # 25
    # 逻辑:按数值从小到大排序(而非字典序的 10 < 2)
    
  • 降序排序(r 选项,常与 n 配合):按指定规则反向排序(升序 → 降序)。

    # 用上述 nums.txt 示例,按数值降序:
    sort -nr nums.txt
    # 输出:
    # 25
    # 10
    # 5
    # 2
    
  • 按字段排序(k 配合 t 选项):对结构化文本(如 CSV、日志),按指定字段排序(需先定义分隔符)。

    # 输入内容(scores.txt,格式:姓名,年龄,分数):
    # Bob,18,90
    # Alice,20,85
    # Tom,19,95# 按“第3个字段(分数)”数值降序排序(-t ',' 定义逗号为分隔符):
    sort -t ',' -k 3 -nr scores.txt
    # 输出:
    # Tom,19,95
    # Bob,18,90
    # Alice,20,85
    
  • 去重排序(u 选项):排序后自动去除重复行(仅保留第一行)。

    # 输入内容(duplicates.txt):
    # cat
    # dog
    # cat
    # birdsort -u duplicates.txt
    # 输出:
    # bird
    # cat
    # dog
    # 逻辑:排序后,重复的“cat”仅保留一次
    
  • 忽略大小写排序(f 选项):排序时不区分字母大小写(视为相同字符处理)。

    # 输入内容(cases.txt):
    # Apple
    # banana
    # Cherry
    # applesort -f cases.txt
    # 输出:
    # Apple
    # apple
    # banana
    # Cherry
    # 逻辑:A/a 视为相同,按字母顺序排在 b 前
    

示例二:统计一个文本文件中每个单词出现的次数

#!/bin/bash# 输入参数校验:确保用户传入了“文本文件路径”这一参数
if [ $# -ne 1 ]; thenecho "使用方法:$0 <待统计的文本文件路径>"echo "示例:$0 ./test.txt"exit 1  # 参数数量不对,退出脚本
fi# 验证文件是否存在且可读取
target_file="$1"
if [ ! -f "$target_file" ]; thenecho "错误:文件 '$target_file' 不存在或不是普通文件!"exit 1
fi
if [ ! -r "$target_file" ]; thenecho "错误:没有读取文件 '$target_file' 的权限!"exit 1
fi# 核心逻辑:统计单词频率并排序
# 步骤拆解:
# ① tr -cs 'A-Za-z' ' ':保留字母(A-Z、a-z),其他字符(如标点、数字)替换为空格;-c 取反,-s 压缩连续空格为单个
# ② tr 'A-Z' 'a-z':将所有大写字母转为小写,避免“Hello”和“hello”被视为不同单词
# ③ awk:遍历每个单词,用关联数组 count 统计出现次数,最后输出“次数 单词”
# ④ sort -nr:按“次数”(数值型,-n)降序(-r)排序,确保次数多的单词在前面
echo "====================================="
echo "文件 '$target_file' 的单词频率统计(按次数降序):"
echo "-------------------------------------"
tr -cs 'A-Za-z' ' ' < "$target_file" | tr 'A-Z' 'a-z' | awk '{# 遍历当前行的所有单词(NF 是当前行的单词总数)for (i = 1; i <= NF; i++) {word = $i  # $i 是当前行的第 i 个单词count[word]++  # 关联数组 count 存单词次数,出现一次加 1}# 所有行处理完后,输出统计结果
} END {for (word in count) {print count[word], word  # 输出格式:次数 单词 自动换行}
}' | sort -nr
echo "====================================="

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/951121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第10天(中等题 滑动窗口)

打卡第十天 2道中等题两个函数: 1.ans = move(t) 是将字符串 t 的内容转移给 ans,而不是进行拷贝。 详细2.substr 是 C++ 字符串的标准成员函数,用于从原字符串中提取子串。 详细 函数原型: string substr(size_t po…

树形dp部分题目总结

树形dp树形dp还是太难了 No.1 P2664 树上游戏 题目直接点开即可,这里不再赘述 我们发现其实直接统计每条路径上的颜色个数并不好统计,即使拆开贡献也是如此 举个例子,你要统计一个节点的贡献,那么你的贡献区间是不…

人工智能之编程基础 Python 入门:第三章 基础语法

人工智能之编程基础 Python 入门:第三章 基础语法人工智能之编程基础 Python 入门 第三章 基础语法@目录人工智能之编程基础 Python 入门前言一、基本语法第一个程序编码标识符行与缩进注释运算符一、算术运算符(Ari…

模块-文本

模块-文本模块:__init__文件中经常方常用的模块名用于快速导入 匿名函数递归:函数自己调用自己的写法叫递归异常处理 try: except excption as e:(报错执行) else: (没报错执行) finally:(无论是否报错都执行)

oier的呻吟

题记: 得之我幸,失之我命哎,马上就要考csp-j/s了 总感觉没复习好,啥都不会 好累 赛前遗言 如果这次没上,也不知道以后会有没有机会碰oi 那个oier曾没有远大的志向 突然想到那一句话 得之我幸,失之我命可能明后天…

进销存软件和ERP是包含关系吗?

进销存软件和ERP是包含关系吗?很多企业老板都会问我这个问题。 尤其是第一次接触信息化系统的时候,常常听人说:ERP比进销存高级、ERP包含进销存、我们先上个进销存,后面再上ERP。 听起来都对,但很多人其实并不清楚…

jenkins 权限控制(用户只能看指定的项目)

jenkins 权限控制(用户只能看指定的项目)转自:https://www.cnblogs.com/lfxx/p/17394304.html 1、在插件管理下载Role-based Authorization Strategy插件 2、打开全局安全设置,设置授权策略3、在管理角色里新增一个全…

CF1784C Monsters (hard version)

对于添加操作难做考虑时空倒流改为删数。我们先考虑不带修怎么做,显然考虑维护一个指针排序后从左往右扫。如果当前指针如果还是 \(\leq a_i\),则 \(x\leftarrow x+1\),答案加上 \(a_i-x\)。那么我们发现,答案只跟…

[Programming Tips]Teach Yourself Programming in Ten Years by Peter Norvig

https://www.norvig.com/21-days.html中文翻译版: https://web.archive.org/web/20090216071944/http://www.javaresearch.org/article/12568.htm10年编程无师自通FooSleeper 翻译 更新:2005-01-12 10:18:06 版本…

世界上最牛逼的人—黄景行

黄景行是世界上最牛逼的人

X991CN-个人自制计算器

实物图PCB-3D视图硬件架构图:应用处理器: ESP32-S3 CORE: Xtensa LX7 32bit 240MHZ SRAM:320KB SPI-NORFLASH:16MB PSRAM:8MB SPI-NANDFLASH:256MB TF卡(SPI模式) NORFLASH和NANDFLASH均采用6线QSPI模式,由于ESP32-S…

非计算机专业,保姆级申请软著教程

软著对认证高新企业、申请人才政策、或者大学加学分都很有用。 有很多朋友想自己申请软著,但是又不知道怎么申请。 这里小玖给大家分享一下,保姆级申请软著的流程。 一、前期准备 在申请软著前,需要完成一些前期准…

F5重大安全事件:国家级黑客窃取BIG-IP源代码与技术漏洞

网络安全公司F5确认遭受国家级黑客攻击,攻击者窃取了其旗舰产品BIG-IP的源代码和未公开漏洞信息。该事件对全球企业和关键基础设施构成严重威胁,文章详细分析了攻击过程、潜在影响及防御建议。F5重大安全事件:国家级…

2025年功效型洗发水品牌推荐榜:二硫化硒去屑洗发水/香氛洗发水/控油蓬松洗发水/MASIL玛丝兰以科技适配多元洗护需求​

随着消费者头皮护理认知升级,功效型洗发水市场在 2025 年持续扩容,去屑、控油、修护等细分需求日益突出。但市场增长也伴随产品功效参差不齐、成分温和性差异大等问题,消费者在选购时常面临 “功效与温和难兼顾”“…

10.30(续)

代码大全2的11-13章的读后感: 第 11 章谈变量命名,直指 “模糊命名导致的理解成本”:很多人写代码时觉得x“y”“tmp” 够用,可过了半个月回头调试,却要花十分钟回忆x到底代表用户 ID 还是订单编号;而 “名称反映…

Python字典 _ 创个秒查流行语的词典

Python字典 _ 创个秒查流行语的词典#声明字典slang_dict = {}slang_dict["觉醒年代"] = "A"slang_dict["双剑"] = "何必"slang_dict["加入"] = "“打不过”&q…

2025铝合金/工业/体育/机库/篷房推荐榜:华烨海特斯五星领跑!德国技术 + 多领域适配,3 家企业凭活动 / 仓储 / 特种场景显优势

随着 2025 年临时空间需求向 “安全化、定制化、长效化” 升级,篷房作为工业仓储、体育赛事、商业活动等场景的灵活空间解决方案,其技术标准与服务响应能力成为选型核心。综合安全性能、场景适配度、资质实力及用户反…

B3612 【深进1.例1】求区间和

本体考的是前缀和 前缀和可以简单理解为「数列的前n项的和」,是一种重要的预处理方式点击查看代码 #include <bits/stdc++.h>using namespace std;int main(){int n;cin >> n;vector<int> a(n + 10…

2025智慧康养实训室/专业建设/虚拟仿真/仿真实训室机构推荐榜:北京教之道五星领跑!全场景 AI 服务 + 居家社区适配,3 家企业凭硬件 / 平台 / 改造显实力

随着 2025 年康养服务对 “智能化效率提升、场景化需求适配” 的要求升级,智慧康养(涵盖智能安全监测、生活辅助、数据化服务管理)成为优化康养体验的核心方向。综合服务覆盖广度、技术适配性、用户反馈及落地效果,…