news 2026/9/28 8:52:59

Bash脚本入门指南:从Shell概念到实战脚本编写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bash脚本入门指南:从Shell概念到实战脚本编写

我看不少刚入行的朋友,对Linux或开发环境的第一道坎,往往不在某个具体命令,而是对bash本身没有概念。知道它是“终端里能输入命令的东西”,但搞不清楚Shell、bash、脚本之间到底什么关系,也不会写一个像样的脚本文件。这里我直接结合自己的使用经验和踩过的坑,把bash脚本编写的入门思路、必要基础、常见误区,以及一些可以直接上手抄的实例整理出来。这一篇先解决“最核心的那部分”——让你能自己动手写出、跑通第一个真正有用的脚本,而不是停留在复制粘贴命令的水平。

1. 先搞清楚bash是什么,以及为什么你非学不可

1.1 别把Shell、终端、bash当成同一回事

很多新手会混淆三个概念:终端(Terminal)、Shell、Bash。我见过有人把终端窗口称作bash,也有人把bash理解成“一连串黑框命令”,这些都是不准确的。

  • 终端(Terminal):是显示和输入的那个窗口程序。它本身不执行命令,只负责显示字符、接收键盘输入。
  • Shell(壳):是命令解释器,跑在终端和操作系统内核之间,负责把你输入的字符串翻译成系统调用。
  • Bash:是Shell的一种,全称Bourne Again SHell,是Linux发行版里最常用的默认Shell。

简单类比一下:终端是窗口柜台,Shell是窗口背后的接线员,bash就是其中最常见的那位接线员。你在终端里敲的每一条命令,真正处理它的是bash。写bash脚本,本质就是把这串要发给接线员的指令,提前整理成一张工单,按次序执行。

1.2 为什么第一个脚本语言选bash,而不是Python

你可能会有疑问:现在Python也很流行,为什么还要专门学bash脚本?我的观点是:Python和bash的定位不同,不该互相替代,而该配合使用。

  • bash天生就是操作系统的“本地人”:启动服务、批量重命名、监控日志、备份文件、定时任务,这些工作直接调用系统命令效率最高,不需要额外装Python环境。
  • bash零依赖:几乎任何Linux服务器上都有bash,写出来就能跑,没有解释器版本不兼容的问题。
  • bash擅长“把现成的命令串起来”:而Python则更适合需要复杂数据结构、网络请求、数据处理逻辑的场景。

所以合理的策略是:操作系统层面的自动化用bash解决,业务逻辑复杂的用Python。两个人各干各的,互不抢饭碗。

1.3 在Windows上也要了解bash的原因

如果你平时用Windows开发,可能会接触两道命令工具:Git Bash和WSL。

  • Git Bash提供一套模拟bash环境的工具,让你在Windows里能使用大部分Linux命令,比如ls、grep、awk。学习成本低,适合日常文件操作和运行简单脚本。
  • WSL(适用于Linux的Windows子系统)则是真正的Linux环境,可以安装完整的发行版。适合需要原生Linux系统调用的场景。

我的建议是:入门阶段用任一工具都行,重点是把bash命令的“感觉”找到。等你熟练了,自然会知道自己该选哪条路。

2. 动手写第一个脚本:环境、文件权限与执行方式

2.1 脚本文件是怎么被识别的:Shebang行

bash脚本,本质上就是一个文本文件,里面按行写命令,在文件第一行加上Shebang(也叫释伴行),告诉系统“这个文件该用哪个解释器执行”。

#!/bin/bash

这行本身不是被执行的命令,而是系统的“路标”,标记了该脚本要用bash来运行。你也可以写成#!/usr/bin/env bash,这种写法兼容性更好,它会自动去环境变量里找bash的位置。

需要注意一个小细节:Shebang一定得是第一行,前面不能有任何空行、空格或注释。否则系统会把它当成普通文件,用当前Shell去执行,有时候会导致诡异行为。

2.2 写脚本前先检查编辑器编码问题

Windows下用记事本或部分编辑器写脚本,保存出来的文件默认是带BOM头(Byte Order Mark)的UTF-8。这个BOM头在Windows程序里无感,但到Linux下,bash解释器会把它当成一个字符,导致第一行Shebang解析失败,报错类似bad interpreter。

我个人的习惯是:统一用VS Code或任何你熟悉的代码编辑器,把右下角编码明确设为UTF-8不带BOM,再开始写脚本。Windows下写脚本的人,十个里面至少有两三个栽过这个坑,而且是脚本死活报错找不到原因的那种。

2.3 为什么刚写完的脚本总是“权限不够”

在Windows里写文本文件,你可能习惯双击或直接打开。但在Linux下生成脚本文件时,默认没有可执行权限。如果你直接执行,会出现:

./hello.sh: Permission denied

解决办法是给脚本加上执行权限:

chmod +x hello.sh

这个加号代表给文件增加(add)“可执行”权限。若是你想精确控制,也可以写成chmod 755 hello.sh,这是给所有用户读执行权限,给属主写权限。

如果你只想临时测试脚本,不想改权限,还有另一种方式:用bash hello.sh直接调用bash解释器运行。这种方式下,脚本文件不需要可执行权限,相当于你主动告诉系统“帮我用bash去读这个文件”。

2.4 你的第一个脚本,逐步拆解

我来带你写一个极简但用得上的脚本,功能是输出当前时间、当前用户名和当前路径,并把结果记录到日志文件里。

先用编辑器创建一个文件myinfo.sh:

#!/bin/bash # 我的第一个bash脚本:输出系统与用户信息 echo "当前时间:$(date '+%Y-%m-%d %H:%M:%S')" echo "当前用户:$(whoami)" echo "当前路径:$(pwd)" echo "$(date '+%Y-%m-%d %H:%M:%S') 脚本执行完成" >> ~/myinfo.log

然后按顺序执行:

chmod +x myinfo.sh ./myinfo.sh

如果一切顺利,你会看到三行输出,并且家目录下出现一个myinfo.log文件,里面追加了一行记录。

这里有两个知识点值得拆开讲:

  • $(...)叫命令替换,含义是“先执行括号里的命令,用它的输出来替代这一整段”。如果不加,date命令就会原样显示为字符串,而不是输出的时间。
  • >>是追加符号,意思是把内容追加到文件末尾,而不是覆盖文件。如果要清空后写入,用单个>。

3. 变量、引号与命令替换:脚本的语法地基

3.1 变量定义:等号两边千万不能有空格

bash脚本里的变量,定义语法简单得让人意外,但新手踩坑频率最高的也是这里:

name="架构师"

注意,等号两边一定不能有空格。写name = "架构师"会变成执行命令name,参数=和"架构师",几乎肯定会报“command not found”之类的错误。这是bash和大多数编程语言不一样的地方,写多了C、Java的人最容易犯。

使用变量时,要在前面加$符号取它的值:

echo "你好,$name"

更稳妥的写法是给变量加花括号:

echo "你好,${name},欢迎使用bash"

为什么推荐加花括号?因为如果写成$name好,bash会理解成变量名是name好,结果输出空白。加上花括号以后,变量名边界一清二楚,就能避免类似问题。

3.2 单引号与双引号的差别:90%新手意识不到的事

在bash里,双引号内的$、反引号、\符号仍会触发特殊含义,单引号则完全是“原样输出”。我这里给一个非常直观的对比:

# 双引号:$变量会被展开为值 price=10 echo "这件商品的价格是 $price 元" # 单引号:$变量不会展开,显示为字符串 echo '这件商品的价格是 $price 元'

输出分别是:

这件商品的价格是 10 元 这件商品的价格是 $price 元

所以在写脚本时,需要记住:如果你希望把变量的值、命令的执行结果拼进字符串,用双引号;如果你想输出一个包含大量特殊符号的文本(比如正则表达式、SQL语句),最好用单引号,省得一个个转义。

3.3 命令替换:让脚本“拿到命令的输出”

命令替换在脚本里极其常用,它会先执行里面的命令,再把这个命令的输出作为值赋给变量或嵌入字符串。

now=$(date) echo "现在时间是:$now"

这种写法比先执行date命令,再把输出复制粘贴进脚本要可靠百倍。因为脚本是动态获取的值,不管你什么时候运行、跑了多少次,都能拿到当时的真实时间。

还有一种更老的写法是反引号:

now=`date`

我不建议新写的脚本继续用反引号,一来嵌套困难,二来可读性差。$(...)既清晰又能嵌套,推荐统一使用。

3.4 特殊变量:脚本自己携带的隐式“参数包”

写脚本时,你还经常要接收外部传入的参数。bash自带一套隐藏变量,用起来非常顺手:

变量含义
$0脚本本身的名称,包含调用路径
$1第一个参数
$2第二个参数,以此类推
$#参数总数
$@所有参数列表,每个独立
$*所有参数作为单个字符串
$$当前脚本运行的进程ID

比如你执行./backup.sh data /tmp,脚本内部$1就是data,$2就是/tmp,$#是2。

我建议脚本一开始就养成“核对参数”的习惯:

if [ $# -lt 2 ]; then echo "用法:$0 源目录 目标目录" exit 1 fi

这样比脚本运行到一半再发现少了参数要友好得多。

4. 条件判断与循环:让脚本学会“自己拿主意”

4.1 if语句:不是必须有else子句

有搜索热词把“bash if语句必须有else子句吗”作为疑问,这可能被其他编程语言的语法习惯影响。bash的if语句中,else子句是可选的,完全没必要时不必写。最常见的三种结构:

# 只有if if [ -f /etc/passwd ]; then echo "系统密码文件存在" fi # if + else if [ -d /tmp ]; then echo "临时目录存在" else echo "临时目录不存在" fi # if + elif + else if [ -f /etc/os-release ]; then echo "这是一个现代Linux发行版" elif [ -f /etc/redhat-release ]; then echo "这是一个老式RedHat系统" else echo "无法判断系统类型" fi

注意,bash的if结构以fi结尾(也就是if倒过来写),这是bash和很多语言差异很大的地方,我见过不少新手漏写fi,导致语法错误。

4.2 test命令与方括号的关系

if [ ... ]里的[实际上是一个命令,全称是test。所以[ -f file ]等价于test -f file。方括号里面每个参数之间必须有空格,否则bash会把[-f当做一个命令名。

常用的测试条件有:

表达式含义
-f 文件判断是否为一个普通文件
-d 目录判断是否为一个目录
-e 路径是否存在(文件或目录)
-z 字符串字符串长度为零
-n 字符串字符串长度不为零
-eq数值相等(用于整数比较)
-ne数值不相等
-lt数值小于
-gt数值大于

这里要特别提醒一个最坑的坑:数字比较不要用>或<,那是给字符串和重定向用的。在方括号里写[ 5 > 3 ],bash会把它当成“把3重定向到名为5的文件”,结果永远为真,且可能生成一个名为5的空文件。整数比较请用-gt、-lt、-eq这一套。

4.3 for循环:处理批量任务的一把好手

for循环在bash里的写法非常灵活,推荐掌握下面几种写法,覆盖几乎全部场景。

指定一组明确的值:

for file in a.txt b.txt c.txt; do echo "正在处理:$file" done

生成数字序列:

for i in {1..10}; do echo "第 $i 次循环" done

遍历目录下的文件名:

for file in /tmp/*.log; do if [ -f "$file" ]; then echo "$file 是日志文件" fi done

遍历命令结果:

for user in $(cut -d: -f1 /etc/passwd); do echo "系统用户:$user" done

for循环的结尾是done,这和if必须以fi结尾一个道理,千万别漏掉。

4.4 while循环:当条件满足时重复执行

当你需要“直到某个条件不满足才停”时,用while循环:

count=1 while [ $count -le 5 ]; do echo "第 $count 次执行" count=$((count + 1)) done

这里出现了一个新的运算符号$((...)),它是“算术扩展”语法,可以把里面的内容当作数学表达式计算。所以$((count + 1))就是count加一。

我还见过一个实用的场景:读取文件每一行,逐个处理:

while IFS= read -r line; do echo "读取到:$line" done < /etc/hosts

IFS=表示去掉行首行尾空白,-r表示禁止反斜杠转义,这是读取文件最安全、最推荐的姿势。

5. 一个能直接用的小脚本:批量备份目录

理论知识讲完,该有个完整的综合实例。我来写一个实用的备份脚本,适合备份指定目录到目标目录,并自动按日期生成压缩包。这个脚本是真实能跑的,日常你完全可以改一改直接用。

#!/bin/bash # 用途:将源目录打包压缩到目标目录,文件名带时间戳 # 用法:./backup.sh 源目录 [目标目录] SOURCE_DIR=$1 TARGET_DIR=${2:-$HOME/backups} # 校验参数 if [ -z "$SOURCE_DIR" ]; then echo "错误:请指定要备份的目录" echo "用法:$0 源目录 [目标目录]" exit 1 fi if [ ! -d "$SOURCE_DIR" ]; then echo "错误:源目录 $SOURCE_DIR 不存在" exit 1 fi # 创建目标目录(如果不存在) mkdir -p "$TARGET_DIR" # 生成备份文件名:源目录名_日期_时间.tar.gz BASENAME=$(basename "$SOURCE_DIR") STAMP=$(date '+%Y%m%d_%H%M%S') BACKUP_FILE="$TARGET_DIR/${BASENAME}_${STAMP}.tar.gz" # 执行打包 tar -czf "$BACKUP_FILE" "$SOURCE_DIR" if [ $? -eq 0 ]; then echo "备份成功:$BACKUP_FILE" echo "文件大小:$(du -h "$BACKUP_FILE" | cut -f1)" else echo "备份失败,请检查tar命令输出" exit 1 fi

这段代码里有几个值得留意的写法:

  • ${2:-$HOME/backups}的意思是如果第二个参数没传,就用默认值$HOME/backups。这是bash里很经典的缺省值语法,你以后看别人的代码会频繁碰到。
  • $(basename "$SOURCE_DIR")是取旅程的最后一段路径,比如/var/log/nginx会得到nginx,这样文件名不会出现斜杠。
  • $?是上一条命令的退出状态码,0表示成功,非0表示失败。判断成功与否用它最直接。

运行方式:

chmod +x backup.sh ./backup.sh /var/log/nginx

执行后,会在~/backups目录下生成类似nginx_20250115_103022.tar.gz的文件。

我还建议你把这样的脚本挂到crontab里做定期备份,比如每天凌晨3点执行:

0 3 * * * /home/用户名/backup.sh /var/log/nginx

配合“备份成功后清理7天前备份”的逻辑,就是一个完整的备份体系。

6. 调试脚本的实用技巧:别再用“肉眼找错”折磨自己

6.1 用 -x 参数追踪脚本执行过程

写脚本哪有不报错的?关键是快速找到问题在哪一行。bash自带的调试能力比很多人想象中强。

运行脚本时加-x参数:

bash -x backup.sh /var/log

你会看到每一个命令执行前先把这个命令打印出来,前缀带加号。这样能直观看到变量展开后的真实值、循环执行到第几次、哪个命令报错。

如果脚本里某些关键步骤想单独追踪,也可以在脚本内临时插入set -x(开启)和set +x(关闭):

set -x echo "这段会被详细追踪" set +x echo "这段正常输出"

6.2 排除语法错误:bash -n和shellcheck

bash -n只检查语法,不执行脚本。写了一大段脚本,先跑一遍语法检查,能提前发现很多粗心问题:

bash -n backup.sh

如果没有任何输出,就代表语法上通过了。但这不代表逻辑正确,只能说明没有拼写错误、括号匹配完整。

我更推荐的是装了shellcheck这个工具来检查,它在几乎所有主流发行版和macOS都能安装。它的检查能力远不止语法,会指出变量是否没加引号、该用[[ ]]却用了[ ]、哪些写法有歧义等。基本等于是给bash脚本配了一个私人Code Review。

这里有一条实际的调试经验分享:echo语句不光用来输出结果,也是排查逻辑的利器。在关键变量后面加上echo "调试信息:变量名=$变量名",能最快定位是哪一步出了问题。我见过不少同事调试半天找不到问题,把几个关键变量打一通,立刻看出来是参数顺序没对齐。

6.3 set -e让脚本“失败即停”

默认情况下,bash脚本里某一条命令失败并不会让整个脚本终止,后面的命令还是会继续执行。这在批量操作里很容易埋下隐患,可能导致进程继续往下走,做出一堆基于“失败前提”的错误操作。

如果你希望脚本在任意命令失败时立刻退出,可以在脚本开头加上:

set -e

这样只要某条命令返回非零状态,脚本就立刻终止。我建议所有正式运行的脚本都加上这个配置。

不过也得注意,set -e存在一些边界场景,比如在if条件里执行的命令不受影响。所以它适合作为“兜底”而不是百分百依赖的措施,关键命令还是自己判断$?更稳妥。

7. 脚本风格与通用规范:从“能跑”到“好维护”

7.1 变量命名与脚本头部注释

有人觉得脚本是一次性的,随手写完就不管了。但实际工作中,三年前的脚本可能还得跑,到时候没有人记得里面写的是什么意思。所以脚本头部至少要留下几行注释:

#!/bin/bash # 功能:备份指定目录并压缩 # 作者:你的名字 # 更新时间:2025-01-15 # 用法:./backup.sh 源目录 [目标目录]

变量命名方面,我建议脚本级别的全局变量用全小写加下划线(如source_dir),常量用大写(如MAX_RETRY)。别用无意义的a、b、c命名,一旦循环嵌套多了,自己都会搞混。

7.2 引号尽量加上,别让变量“裸奔”

在bash脚本中,只要变量可能包含空格或其他特殊字符,把变量用双引号包起来是习惯,也是底线:

# 错误写法 cp $source_dir/*.log $target_dir # 正确写法 cp "${source_dir}/"*.log "$target_dir"

不加引号会把字符串按空格拆成多个词,如果文件名里有空格,直接导致文件找不到或者复制错对象。具体的教训我踩过太多次,现在写脚本几乎见双引号就加。

同理,在判断语句里更要加:

# 如果不加引号且$name为空,测试条件会变成 [ = "admin" ],直接语法报错 if [ "$name" = "admin" ]; then echo "欢迎管理员" fi

7.3 用[[ ]]替代[ ],兼容性以外的好处

在bash里,更推荐使用[[ ]]双中括号来做条件判断。它比[ ]功能更丰富:支持正则匹配=~,支持&&、||逻辑运算,且即使变量为空也不会因字面解析而报错。

if [[ $name == "admin" ]]; then echo "欢迎管理员" fi if [[ $file == *.log ]]; then echo "这是日志文件" fi

关于兼容性这一块我要说明:[[ ]]是bash内建的关键字,而[ ]是POSIX标准。如果你要针对POSIX sh环境(比如某些嵌入式系统、系统初始化脚本)写,那必须用[ ];如果脚本明确是bash环境,那完全可以用[[ ]]。这个取舍没有绝对优劣,按你所处的环境选择即可。

7.4 函数:封装重复逻辑,让脚本不至于变成一坨

脚本写长了,反复执行的某段逻辑应该抽成函数,不然每次修改都得改多处,极易漏改。

function log_msg { local msg=$1 echo "$(date '+%Y-%m-%d %H:%M:%S') $msg" >> /var/log/myscript.log } # 使用 log_msg "开始备份" log_msg "备份完成"

这里值得注意的关键词是local,它声明局部变量,只在函数内部生效,不会污染全局变量。

我在写脚本时还有一个习惯:所有函数放在文件顶部,main逻辑放在底部。虽然bash不要求先声明后调用,但这样阅读顺序最顺畅,别人接手你的脚本会轻松很多。

8. 常见报错与排查方向:那些你一定会遇到的坑

8.1 “command not found”的多重含义

这是出现频率最高的报错,但背后的原因不止一种:

  • 命令真的不存在,输入有拼写错误。
  • 命令存在,但不在当前PATH路径里。比如你刚装了一个软件,装到了/usr/local/bin,但PATH没包含。
  • 脚本执行时用了sh而不是bash,导致部分bash特性不识别。

排查方法:先用which 命令名看能不能找到,再用echo $PATH确认路径,最后确认脚本Shebang是否指向bash。

8.2 “syntax error near unexpected token”

这种报错大多是因为:漏了fi或done、if和[之间少了空格、$(( ... ))括号不匹配。最快的定位方式是用bash -n做语法检查,它会指出具体哪一行有问题。

还有一个特殊情况:Windows下编辑的脚本换行符是CRLF(回车+换行),而Linux只认LF。CR会被bash当成命令的一部分,进而报各种奇怪的语法错误。用sed -i 's/\r$//' 脚本名可以快速修复,或者直接用VS Code右下角把换行符切换成LF。

8.3 “bash: $'\r': command not found”

这个报错是CRLF换行符问题的“典型面孔”,我一开始遇到时完全摸不着头脑。处理办法上面已经提到。平时写脚本时,编辑器先统一改成LF,能直接从根上避免这一系列问题。

8.4 如何看这个脚本在服务器上能不能用

跑脚本前先做几个体检:

  • 用file 脚本名看脚本类型,确认没有乱码、没有CRLF标记。
  • 用bash -n检查语法。
  • 用bash -x先小范围执行,看实际行为是否符合预期。

这几步做完,基本能挡住九成低级错误。剩下的逻辑问题,就要靠你对业务场景的理解了。

9. 从这篇到底,下一件该做的事

这一篇侧重的,是把bash从“一团模糊的命令行”变成一个你敢于动手写文件的东西。你学会了:什么是bash、怎么创建并运行脚本、变量和引号、判断和循环、调试手段、规范习惯。

接下来我建议你按下面的顺序继续往前走:

  1. 把文中的备份脚本改成适合自己目录结构的版本,给日志、配置文件做备份试试。
  2. 搞清楚find命令配合-exec批量操作文件,这会在写更复杂脚本时频繁出现。
  3. 掌握crontab定时任务,把脚本变成自动运行的后台帮手。
  4. 了解grep、awk、sed,这三剑客在文本处理时几乎无可替代。

bash脚本的进步路径不是靠看,而是靠真实的问题驱动。一个人在电脑前,一遍一遍调试,把错误信息读懂,把一个报错解决掉,那种成就感会越来越强烈。下一篇我会沿着脚本进阶的路子继续,到时候咱们在更具体的实战场景里见。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 8:52:59

C++模板函数声明定义分离引发的链接错误:原因与三种解决方案

1. 这个报错让人抓狂的第一现场如果你是第一次在 C 里把模板函数的声明和定义分开放在.h和.cpp文件里&#xff0c;大概率会在链接阶段收获一个极其经典的报错。我在最早写一个排序工具函数时遇到了同样的场景&#xff0c;头文件里明明干干净净写了声明&#xff0c;主文件里调用…

作者头像 李华
网站建设 2026/9/28 8:52:57

.NET的“代码即配置”文化:手工配置为何比可视化更可靠

先说一句可能得罪很多人的话&#xff1a;.NET 这门技术栈&#xff0c;你很难找到一套所谓的“可视化配置工具”&#xff0c;把项目里的依赖注入、服务注册、ORM 映射、中间件顺序、消息队列绑定全部拖拖拽拽就生成出来。绝大多数时候&#xff0c;你面对的就是 .csproj 文件、ap…

作者头像 李华
网站建设 2026/9/28 8:52:40

AI Agent工程化落地的五大核心实践

1. 别把AI Agent当“高级聊天框”&#xff1a;先搞清它到底在替你做什么事最近两周&#xff0c;我连续帮三拨朋友调试他们自己搭的AI Agent流程——有人想用Agent自动整理会议纪要&#xff0c;有人想让它每天抓取行业简报生成周报&#xff0c;还有人直接扔给Agent一句“帮我写个…

作者头像 李华
网站建设 2026/9/28 8:52:35

模型部署加速实战:量化、剪枝与蒸馏的统一优化管线

前阵子在把一个BERT类的语义模型部署到线上服务时&#xff0c;遇到一个让我非常头疼的问题&#xff1a;模型参数量接近1个G&#xff0c;单次推理的P99延迟超过800毫秒&#xff0c;线上8核容器CPU直接打满&#xff0c;QPS怎么压都上不去。身边同事有的建议换更小的模型&#xff…

作者头像 李华
网站建设 2026/9/28 8:52:09

C# FTP下载实例源码解析:FtpWebRequest原理、避坑与断点续传

简介&#xff1a;这份C# FTP下载实例源码面向具备一定.NET基础的开发者与计算机专业学生&#xff0c;用于解决在C#项目中实现FTP文件传输的实际问题。资源围绕FtpWebRequest与FtpWebResponse两个核心类展开&#xff0c;涵盖连接服务器、凭据登录、被动模式与SSL加密设置、获取目…

作者头像 李华