当前位置: 首页 > article >正文

shell脚本语法

shell脚本的变量

系统变量

系统变量是操作系统用来存储配置信息的变量,它们可以控制操作系统的行为和程序的运行环境。系统变量的种类和内容取决于操作系统的类型和版本。以下是一些常见的系统变量类别和它们可能包含的内容:

  1. 环境变量:这些变量为程序提供运行时所需的信息,如文件路径、系统配置、用户偏好等。

    • 例如:PATHHOMETEMPTMPDIRUSERPROFILE 等。
  2. 系统配置变量:这些变量定义了系统的基本配置,如系统启动参数、硬件配置、网络设置等。

    • 在 Linux 中,系统配置变量可能包括 HOSTNAME(主机名)、SHELL(默认 shell)、HISTSIZE(历史记录大小)等。
  3. 用户配置变量:这些变量存储了用户的个人设置,如桌面环境、主题、语言偏好等。

    • 用户配置变量可能包括 XDG_CONFIG_HOME(用户配置文件目录)、XDG_DATA_HOME(用户数据文件目录)等。
  4. 安全和权限变量:这些变量与系统的安全设置和用户权限有关。

    • 例如:在 Unix-like 系统中,UIDEUID(用户 ID 和有效用户 ID)用于控制用户权限。
  5. 网络配置变量:这些变量定义了网络相关的设置,如 DNS 服务器、网关、子网掩码等。

    • 例如:GATEWAYDNSNETMASK 等。
  6. 硬件配置变量:这些变量描述了硬件的配置,如内存大小、CPU 类型、磁盘分区等。

    • 例如:在 Linux 中,MEM_TOTAL(总内存大小)、CPUINFO(CPU 信息)等。
  7. 系统性能变量:这些变量与系统的性能和资源使用有关,如 CPU 使用率、内存使用情况、磁盘空间等。

    • 例如:LOAD_AVERAGE(系统负载平均值)。
  8. 系统状态变量:这些变量反映了系统当前的状态,如系统启动时间、运行时间、系统日志等。

    • 例如:UPTIME(系统运行时间)、BOOT_TIME(系统启动时间)。

系统变量可以通过不同的方式进行访问和修改,例如在命令行界面(CLI)中使用特定的命令,或者通过图形用户界面(GUI)中的系统设置工具。在编程和脚本编写中,系统变量经常被用来配置程序的行为或自动化任务。

环境变量

环境变量是系统变量的一个子集。
环境变量是为操作系统的程序提供配置信息的变量。它们可以在任何程序中被读取和修改,并且可以在程序启动时传递给程序。
这句话描述了环境变量在计算机操作系统中的作用和特点。

  1. 为操作系统的程序提供配置信息:环境变量存储了操作系统和运行在其上的程序所需的配置信息。这些信息可以包括文件路径、系统配置、用户偏好设置等。程序可以通过读取这些变量来获取必要的信息,而不需要在代码中硬编码这些信息。

  2. 变量:在计算机科学中,变量是存储数据的容器。环境变量是特殊的变量,它们不是由单个程序定义和管理的,而是由操作系统维护,并在系统范围内可用。

  3. 可以在任何程序中被读取和修改:这意味着任何运行在操作系统上的程序都可以访问和修改环境变量。这为程序提供了灵活性,因为它们可以根据环境变量的值来调整自己的行为。

  4. 可以在程序启动时传递给程序:当一个程序启动时,它可以接收到一组环境变量,这些变量可以作为参数传递给程序。这样,程序就可以根据这些变量的值来决定如何运行。例如,一个程序可能会检查一个特定的环境变量来决定是否启用调试模式。

总结来说,环境变量是操作系统中的一种机制,它允许程序在运行时获取和使用配置信息,而无需在代码中硬编码这些信息。这增加了程序的可移植性和灵活性,因为相同的程序可以在不同的环境(具有不同的配置)中运行,只需通过设置不同的环境变量即可。

环境变量的例子包括:

PATH  		命令所示路径,以冒号为分割;
HOME  		打印用户家目录;
SHELL 		显示当前Shell类型;
USER  		打印当前用户名;
ID    		打印当前用户id信息;
PWD   		显示当前所在路径;
TERM  		打印当前终端类型;
HOSTNAME    显示当前主机名;
PS1         定义主机命令提示符的;
HISTSIZE    历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间;
RANDOM      随机生成一个 0 至 32767 的整数;
HOSTNAME    主机名

创建坏境变量

#!/bin/bash

# 创建一个新的环境变量
export MY_VAR="Hello, World!"

# 打印当前的环境变量
echo "MY_VAR is set to: $MY_VAR"

# 读取环境变量
read -p "Enter a new value for MY_VAR: " new_value
export MY_VAR=$new_value

# 打印修改后的环境变量
echo "MY_VAR is now set to: $MY_VAR"

# 删除环境变量
unset MY_VAR

# 尝试打印已删除的环境变量(会显示空值)
echo "MY_VAR is set to: $MY_VAR"

# 退出脚本
exit 0
  • 1.给脚本执行权限:chmod +x env_variables.sh。
  • 2运行脚本:./env_variables.sh。
    在这里插入图片描述
    请注意,这个脚本中设置的环境变量只在脚本运行的当前 shell 会话中有效。一旦脚本结束,这些环境变量就会消失。如果你想要永久设置环境变量,你可以将 export 命令添加到你的 shell 配置文件中,如 ~/.bashrc 或 ~/.profile。

特殊变量

$0 		当前脚本的名称;
$n 		当前脚本的第n个参数,n=1,2,…9;
$* 		当前脚本的所有参数(不包括程序本身);
$# 		当前脚本的参数个数(不包括程序本身);
$? 		令或程序执行完后的状态,返回0表示执行成功;
$$ 		程序本身的PID号。
#!/bin/bash

# 打印脚本名称
echo "Script name: $0"

# 打印所有参数
echo "All arguments: $*"
echo "Arguments as array: $@"

# 打印参数个数
echo "Number of arguments: $#"

# 打印上一个命令的退出状态
echo "Last command exit status: $?"

# 打印当前脚本的 PID
echo "Script PID: $$"

# 打印每个参数
for i in "$@"; do
    echo "Argument $i"
done

# 模拟一个命令执行
echo "This is a test command."
exit 42  # 模拟命令退出状态为 42

在这里插入图片描述

用户变量

在 Linux 系统中,用户变量通常指的是用户定义的环境变量,这些变量可以由用户自己设置,用于存储个人偏好、配置信息或用于程序运行时的参数。

用户变量可以通过以下几种方式设置:

  1. 临时变量:在当前终端会话中设置,只对当前会话有效。

    export VARIABLE_NAME="value"
    
  2. 持久变量:在用户的 shell 配置文件中设置,如 ~/.bashrc~/.bash_profile~/.profile 等,这样每次新的终端会话开始时,这些变量都会被加载。

    echo 'export VARIABLE_NAME="value"' >> ~/.bashrc
    source ~/.bashrc  # 重新加载配置文件
    
  3. 局部变量:在脚本或程序中定义,只在这个脚本或程序的上下文中有效。

    VARIABLE_NAME="value"
    

用户变量可以用于多种用途,例如:

  • 指定程序的配置文件路径。
  • 定义程序的运行参数。
  • 存储用户的个人偏好设置,如编辑器、语言环境等。
  • 用于脚本中的逻辑判断和流程控制。

一些常见的用户变量示例包括:

  • EDITOR:用户偏好的文本编辑器,如 vimnano 等。
  • BROWSER:用户偏好的网页浏览器。
  • DISPLAY:用于 X Window 系统的显示环境变量。
  • LANGLC_ALL:用于设置语言和区域设置。
  • XDG_CONFIG_HOME:用户配置文件的目录。
  • XDG_DATA_HOME:用户数据文件的目录。

用户可以通过 env 命令查看当前会话中的所有环境变量,或者使用 printenv 命令查看特定的环境变量:

env
printenv VARIABLE_NAME

要修改或删除用户变量,可以使用 unset 命令:

unset VARIABLE_NAME

这些用户变量为 Linux 系统提供了灵活性和个性化的配置选项。

变量操作

声明变量:

variable_name="value"

读取变量:

echo $variable_name

修改变量:

variable_name="new_value"

删除变量:

unset variable_name

字符串操作:

1.获取字符串长度

echo ${#newString}
#!/bin/bash
#In variable assignment, there should be no spaces around the equal sign
string1="hello world!"
echo ${#string1}

2.拼接字符串:

variable_name="new_value"
new_string="$variable_name""additional_text"

实际上下面这种方式也是可行的

new_string=$variable_name"additional_text"

注意事项
避免歧义:虽然这种拼接方式有效,但在复杂的脚本中,为了提高可读性和避免歧义,建议使用双引号明确地包裹变量和字符串。
特殊字符处理:如果字符串字面量中包含特殊字符,使用双引号可以确保这些字符被正确处理。
3.截取字符串:

${variable_name:start:length}
# 从第0个字符开始截取,截取3个字符
echo ${variable_name:0:3} 
#!/bin/bash
variable_name="new_value"
new_string=${variable_name:0:3}
echo $new_string

这个0-3的范围是左闭右开的:[0,3),所以输出为
new

缺省length

${variable_name:start}

截取会从索引start处,包含start,一直截取到字符串末尾。

#!/bin/bash
variable_name="newvalue"
new_string=${variable_name:3}
echo $new_string

输出为value
4.负索引
Shell也支持负索引,这意味着你可以从字符串的末尾开始计算位置。例如:

echo ${variable_name: -3}
#!/bin/bash
variable_name="new_value"
new_string=${variable_name: -3}
echo $new_string 

这会从 variable_name 的末尾开始,向前截取3个字符,输出lue
使用#和%截取字符串的四种方式

${varible##*string}	#从左向右截取最后一个string后的字符串
#!/bin/bash
variable="one_two_three_two_three_one"
new_string="${variable##*two}"
echo "$new_string"  # 输出: "_three_one"
${varible#*string}	#从左向右截取第一个string后的字符串
#!/bin/bash
variable="one_two_three_two_three_one"
new_string="${variable#*two}"
echo "$new_string"  # 输出: "_three_two_three_one"
${varible%%string*}	#从右向左截取最后一个string后的字符串
#!/bin/bash
variable="one_two_three_two_three_one"
new_string="${variable%%two*}"
echo "$new_string"  # 输出: "one_"
${varible%string*}	#从右向左截取第一个string后的字符串
#!/bin/bash
variable="one_two_three_two_three_one"
new_string="${variable%two*}"
echo "$new_string"  # 输出: "one_two_three_"

5.默认值:
如果变量未设置或为空,你可以提供一个默认值:

echo ${variable_name:-default_value}
#!/bin/bash
variable=""
new_string="${variable:-"dsgsd"}"
echo "$new_string" 

如果 variable_name 未设置或为空,将输出 default_value。
6.大小写转换:
参数扩展还允许你转换字符串的大小写:

echo ${variable_name,,}  # 转换为小写
echo ${variable_name^^}  # 转换为大写
echo ${variable_name,,^^}  # 先转小写再转大写
#!/bin/bash
variable="HELLO WORLD!"
new_string="${variable,,}"
echo "$new_string" 
new_string="${new_string^^}"
echo "$new_string"
new_string="${new_string,,}"
echo "$new_string"
new_string="${new_string,,^^}"
echo "$new_string"

替换字符串中的某个值:

new_string="${variable_name/old_value/new_value}"

示例

text="Hello World"
new_text="${text/World/Earth}"
echo $new_text

字符串运算符

等于 (=):
#用于比较两个字符串是否相等。
#通常在 [[ ]] 或 [ ] 测试中使用。
[[ "$str1" = "$str2" ]]
[ "$str1" = "$str2" ]

不等于 (!= 或 <>):
#用于比较两个字符串是否不相等。
#在 [[ ]] 中使用 !=,在 [ ] 中使用 <>。
[[ "$str1" != "$str2" ]]
[ "$str1" != "$str2" ]  # 错误,[ ] 不支持 !=,应使用 [ "$str1" != "$str2" ] 来避免解析错误
[ "$str1" <> "$str2" ]

模式匹配 (== 或 =~):
#用于检查字符串是否符合特定的模式。
#在 [[ ]] 中使用 == 进行模式匹配,=~ 用于正则表达式匹配。
[[ "$str" == "pattern"* ]]
[[ "$str" =~ regex ]]

大于 (>):
#用于按字典顺序比较两个字符串。
#通常在 [[ ]] 或 [ ] 测试中使用。
[[ "$str1" > "$str2" ]]
[ "$str1" > "$str2" ]

小于 (<):
#用于按字典顺序比较两个字符串。
#通常在 [[ ]] 或 [ ] 测试中使用。
[[ "$str1" < "$str2" ]]
[ "$str1" < "$str2" ]

长度检查 (-n 和 -z):
#用于检查字符串的长度。
#-n 检查字符串是否非空,-z 检查字符串是否为空。
[[ -n "$str" ]]  # 字符串非空
[[ -z "$str" ]]  # 字符串为空


数值操作:

算术扩展方法

增加数值:

((variable_name++))

减少数值:

((variable_name--))

计算数值:

variable_name=$(($variable_name + 1))

数值操作示例

#!/bin/bash

# 定义一个变量并初始化为10
variable_name=10

# 打印初始值
echo "Initial value: $variable_name"  # 输出将会是 10

# 增加数值
((variable_name++))
echo "After increment: $variable_name"  # 输出将会是 11

# 减少数值
((variable_name--))
echo "After decrement: $variable_name"  # 输出将会是 10

# 计算数值,将变量的值增加 5
variable_name=$(($variable_name + 5))
echo "After addition: $variable_name"  # 输出将会是 15

# 计算数值,将变量的值乘以 2
variable_name=$(($variable_name * 2))
echo "After multiplication: $variable_name"  # 输出将会是 30

# 计算数值,将变量的值除以 3
variable_name=$(($variable_name / 3))
echo "After division: $variable_name"  # 输出将会是 10

expr命令方法

a=1
b=2
expr $a + $b
expr $a - $b
expr $a \* $b
expr $b / $a
expr $b % $a

请注意,在使用 expr 时,每个操作数和运算符之间需要有空格,并且某些运算符(如乘法 * 和除法 /)可能需要使用反斜杠 \ 进行转义。

在编写脚本时,推荐使用算术扩展 (( … )),因为它更直观、易于阅读,并且比 expr 更安全,因为它不需要担心某些字符的转义问题。
关系运算符
在Shell脚本中,关系运算符用于比较两个数值或字符串。

当条件为真时,返回 0(在Shell中,0 通常表示成功或真)。
当条件为假时,返回 1(非零值通常表示有错误或假)。
检测两个数是否相等:

if [ $a -eq $b ]; then
  echo "a is equal to b"
fi

检测两个数是否不相等:

if [ $a -ne $b ]; then
  echo "a is not equal to b"
fi

检测左边的数是否大于右边的:

if [ $a -gt $b ]; then
  echo "a is greater than b"
fi

检测左边的数是否小于右边的:

if [ $a -lt $b ]; then
  echo "a is less than b"
fi

检测左边的数是否大于等于右边的:

if [ $a -ge $b ]; then
  echo "a is greater than or equal to b"
fi

检测左边的数是否小于等于右边的:

if [ $a -le $b ]; then
  echo "a is less than or equal to b"
fi

在这些示例中,$a 和 $b 可以是数值变量。这些关系运算符通常用在 if、while、until 等条件语句中。

请注意,当使用这些运算符时,方括号 [ ] 内的表达式两侧需要有空格,否则Shell可能无法正确解析命令。

另外,这些运算符也可以用于字符串的比较,但字符串比较时要注意,它们是按照字典顺序进行比较的。如果需要字符串相等的比较,应使用 = 运算符,而字符串不等的比较则使用 != 运算符。
在编程和脚本编写中,布尔运算符和逻辑运算符都是用来进行条件判断和控制程序流程的重要工具。它们在功能上有所重叠,但用途和上下文有所不同。下面详细解释它们的区别:

布尔运算符

布尔运算符主要涉及逻辑真值(true)和假值(false),用于构造布尔表达式。这些运算符通常包括:

  1. 等于 (== 在某些语言中,如 Bash) 或 (= 在其他语言如 C、Java 中)
  2. 不等于 (!=<>)
  3. 大于 (>)
  4. 小于 (<)
  5. 大于等于 (>=)
  6. 小于等于 (<=)

这些运算符通常用于比较两个值,并根据比较结果返回布尔值(真或假)。它们在决策结构(如 if 语句)中非常有用。

逻辑运算符

逻辑运算符用于组合多个布尔表达式,以创建更复杂的条件。这些运算符包括:

  1. 逻辑与 (&& 在大多数编程语言中,包括 Bash)
  2. 逻辑或 (|| 在大多数编程语言中,包括 Bash)
  3. 逻辑非 (!)

逻辑运算符允许你根据多个条件的组合结果来控制程序的流程。例如,你可以使用逻辑与来要求两个条件同时为真时才执行某段代码。

区别

  1. 用途

    • 布尔运算符:用于比较两个值或变量,并根据比较结果返回一个布尔值。
    • 逻辑运算符:用于组合多个布尔表达式,以便在单一表达式中评估多个条件。
  2. 操作对象

    • 布尔运算符:通常操作具体的数据值或变量。
    • 逻辑运算符:操作的是布尔表达式的结果。
  3. 返回值

    • 布尔运算符:返回布尔值(真或假)。
    • 逻辑运算符:也返回布尔值,但这个值是由多个布尔表达式通过逻辑运算得到的。

示例

在 Bash 脚本中,你可以这样使用它们:

# 布尔运算符示例
if [ $a -eq $b ]; then
    echo "a is equal to b"
fi

# 逻辑运算符示例
if [ $a -gt $b ] && [ $c -lt $d ]; then
    echo "a is greater than b and c is less than d"
fi

if [ $a -ne $b ] || [ $c -eq $d ]; then
    echo "a is not equal to b or c is equal to d"
fi

if [ $a -lt 10 ] && [ $a -gt 5 ]; then
    echo "a is between 5 and 10"
fi

在这些示例中,-eq-gt-lt 是布尔运算符,用于比较数值。而 &&|| 是逻辑运算符,用于组合多个条件。

理解这些运算符的区别对于编写逻辑清晰、易于维护的代码至关重要。

文件测试运算符

运算Shell中的实现主要符号
检测文件是否是块设备文件[ -b $file ]-b file
检测文件是否是字符设备文件[ -c $file ]-c file
检测文件是否是目录[ -d $file ]-d file
检测文件是否是普通文件(既不是目录,也不是设备文件)[ -f $file ] 返回 true-f file
检测文件是否设置了 SGID 位[ -g $file ]-g file
检测文件是否设置了粘着位(Sticky Bit)[ -k $file ]-k file
检测文件是否是有名管道[ -p $file ]-p file
检测文件是否设置了 SUID 位[ -u $file ]-u file
检测文件是否可读[ -r $file ]-r file
检测文件是否可写[ -w $file ]-w file
检测文件是否可执行[ -x $file ]-x file
检测文件是否为空(文件大小是否大于0)[ -s $file ]-s file
检测文件(包括目录)是否存在[ -e $file ]-e file

数组操作:

声明数组:

array_name=(element1 element2 element3)

读取数组元素:

echo ${array_name[0]} # 读取第一个元素

修改数组元素:

array_name[0]="new_element"

添加数组元素

array+=("new_element")

删除数组元素

unset array[1]

删除整个数组

a=(1 2 3 4 5 5)
unset a

获取数组长度:

echo ${#array_name[@]}

遍历数组
你可以使用 for 循环来遍历数组中的所有元素。

for element in "${array_name[@]}"; do
    echo $element
done

遍历数组示例

a=(1 2 3 4 5 6 7 8 7 9)
for element in "${a[@]}";do
        echo $element
done
 

需要注意的是数组的元素用空格分隔

合并数组

使用数组拼接

#!/bin/bash

# 定义两个数组
array1=("element1" "element2" "element3")
array2=("element4" "element5")

# 合并数组
merged_array=("${array1[@]}" "${array2[@]}")

# 打印合并后的数组
echo "Merged Array: ${merged_array[@]}"

使用循环

#!/bin/bash

# 定义两个数组
array1=("element1" "element2" "element3")
array2=("element4" "element5")

# 创建一个新的数组,用于存储合并后的结果
merged_array=()

# 将第一个数组的元素添加到新数组中
for element in "${array1[@]}"; do
  merged_array+=("$element")
done

# 将第二个数组的元素添加到新数组中
for element in "${array2[@]}"; do
  merged_array+=("$element")
done

打印合并后的数组

echo "Merged Array: ${merged_array[@]}"

高级字符串处理:

使用case语句进行模式匹配:

case "$variable_name" in
  pattern1)
    echo "Matched pattern1"
    ;;
  pattern2)
    echo "Matched pattern2"
    ;;
  *)
    echo "No match"
    ;;
esac

case语句示例

#!/bin/bash

# 假设我们有一个变量,存储了用户的输入
user_input="start"

# 使用case语句来匹配不同的模式
case "$user_input" in
  # 如果用户输入是 "start",则执行以下命令
  start)
    echo "The process has started."
    ;;
  # 如果用户输入是 "stop",则执行以下命令
  stop)
    echo "The process has stopped."
    ;;
  # 如果用户输入是 "pause",则执行以下命令
  pause)
    echo "The process has been paused."
    ;;
  # 如果用户输入是 "resume",则执行以下命令
  resume)
    echo "The process has been resumed."
    ;;
  # 匹配任何其他值,执行以下命令
  *)
    echo "Invalid command."
    ;;
esac

echo

在Shell脚本中,echo 命令有多种用途,主要用于输出文本、变量的值、命令的结果等。以下是一些常见的使用场景:

  1. 输出文本
    直接输出一段文本信息到终端。

    echo "Hello, World!"
    
  2. 输出变量的值
    输出变量的内容。

    name="Kimi"
    echo "My name is $name."
    
  3. 输出命令的输出
    使用命令替换($(command)`command`)来输出命令的结果。

    echo "Current date: $(date)"
    
  4. 格式化输出
    使用转义字符(如 \n 换行、\t 制表符)来格式化输出。

    echo -e "First line.\nSecond line\tIndented."
    
  5. 条件输出
    在条件语句中输出信息。

    if [ $USER == "root" ]; then
        echo "You are the superuser."
    fi
    
  6. 循环输出
    在循环中输出一系列值。

    for i in {1..5}; do
        echo "Number $i"
    done
    
  7. 重定向输出到文件
    将输出保存到文件中。

    echo "This will be saved to a file." > file.txt
    
  8. 使用 echo 进行字符串操作
    echo 可以用于字符串拼接。

    first="Hello"
    last="World"
    echo "${first}, ${last}!"
    
  9. 输出错误信息
    在脚本中输出错误信息,通常用于调试或错误处理。

    echo "Error: Unable to process file." >&2
    
  10. 输出环境变量
    输出环境变量的值。

    echo "PATH: $PATH"
    
  11. 输出数组
    输出数组的内容。

    array=("Apple" "Banana" "Cherry")
    echo "Fruits: ${array[*]}"
    
  12. 使用 echo 进行算术运算
    echo 可以用于简单的算术运算。

    echo "Sum: $((3 + 5))"
    

http://www.kler.cn/a/303417.html

相关文章:

  • Qwen2-VL:发票数据提取、视频聊天和使用 PDF 的多模态 RAG 的实践指南
  • 【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)
  • ubuntu20.04安装FLIR灰点相机BFS-PGE-16S2C-CS的ROS驱动
  • mac终端使用pytest执行iOS UI自动化测试方法
  • python装饰器的使用以及私有化
  • 设计模式:工厂方法模式和策略模式
  • ASP.NET MVC 迅速集成 SignalR
  • 【spring】IDEA 新建一个spring boot 项目
  • 【无人机设计与控制】旋转无人机摆锤的SDRE仿真
  • VSCode 编写 vue 项目之一键生成 .vue 页面模版
  • 计算机网络:概述 - 性能指标
  • 【Linux 从基础到进阶】Docker Compose 编排工具使用
  • CategoriesController
  • 什么是图像的边缘?说说边缘检测的任务以及基本原理?
  • 无人机应用新纪元:图形工作站配置推荐与硬件解析
  • 网络安全-dom破坏结合jq漏洞以及框架漏洞造成的xss-World War 3
  • Python | Leetcode Python题解之第402题移掉K位数字
  • labview禁用8080端口
  • 合宙低功耗4G模组Air780EX——硬件设计手册01
  • 同步和异步是两种不同的程序执行方式
  • duckdb 连接postgres 和 jdbc 的使用
  • Prism 教程
  • Java集合框架(Collections Framework)入门
  • Compose Multiplatform+kotlin Multiplatfrom第三弹
  • 车辆检测与分类系统源码分享
  • [图论]街道赛跑