BNU-FZH

fengzhenhua@outlook.com

一、流程图是什么

流程图(Flowchart)也称为框图,通俗的讲就是把“流程”用“图”的形式展现出来,也就是“流程”+“图”。通常会使用一些特定图形加上相应的文字描述表示流程的具体步骤,并用含有箭头的线将这些图形连接起来,表示步骤执行的算法与先后顺序。

流程图简单画法

二、流程图的常见作用

  1. 流程图可以把复杂的过程简单而又直观地呈现出来,帮助我们梳理流程的先后顺序,让整个流程的思路更加清晰、有逻辑。
  2. 帮助管理者了解流程内的具体流程,用于查缺补漏,避免流程内出现多余或遗漏的内容,简化整理步骤的同时确保流程的完整性。
  3. 帮助分析流程内出现的问题,让流程内参与者能准确了解自己的职责所在,提高工作效率、优化管理水平。
流程图的常见作用

三、流程图符号

  常用流程图符号
  流程图的类型有很多,例如程序流程图、数据流程图或工艺流程图等。虽然流程图的分类众多,但在绘制过程中,不管是什么类型的流程图,通常会使用相同符号表示同类型流程。
  如文档、卡片、角色由于流程类型存在差异,其使用的符号也会大不相同。下图给大家分享一些常用流程图符号供以参考。

常用流程图符号

  基本流程图符号
  虽然流程图符号如此之多,但在实际制图时能够频繁使用到的符号却并不多。用得较多且较为基础的有五种:起止框、处理框、判断框、输出输入框和流程线。

基本流程图符号

四、流程图结构

  流程图通常由三种结构组成:顺序结构、选择结构、循环结构。
  顺序结构:这是流程图三种结构中最简单的一种,只需按照流程内箭头方向依次执行内容即可。
  选择结构:选择结构也称之为判断结构,根据给定的条件进行判断,并以该判断结果来控制运行的流程。
  循环结构:循环结构就是在一定的条件下重复执行某一操作,用于判断程序继续执行某一操作或退出循环。分为直到型和当型两种循环结构。

流程图结构

五、流程图注意事项

  1. 为了使流程图直观且有逻辑,一般会遵循从上到下、从左到右的顺序排列。
  2. 一份完整的流程图会涵盖开始框和结束框,且一般情况下开始框和结束框分别只出现一次。
  3. 流程图可以出现包含和并列关系,但不能交叉,除非给予适当的判断。
  4. 对于判断框需要有两个或两个以上的执行条件,且每一条件要有相应的注释和执行结果。

六、流程图绘制

  流程图分为在线和软件两种绘制方式。在绘制流程图时有一个清晰的思路非常重要,鉴于不少小伙伴刚接触流程图时可能会分不清主次的情况,所以可以从运用模板开始学习。合理的使用模板可以加快学习绘制速度,便于寻找他人的绘制优点,并将其化为己用。

流程图绘制

在近十年的发展,显示设备的接口发生了巨大的改变。以前使用比较多的是蓝色VGA接口,接著出现了白色的DVI接口,当遇到不同接口时,还得买转接头进行转接。后来,又有了HDMI等接口,现在则出现DP和USB Type-C接口。那么,这么多接口,到底有什么区别,又应该用哪个呢?

VGA

VGA接口也称为D-Sub接口。在CRT显示器时代,VGA接口是必备的。因为CRT是模拟设备,而VGA采用的也是模拟协议,所以它们理所当然被匹配来使用。VGA接口采用15针插针式结构,里面传输分量、同步等信号,是很多老显卡、笔记本和投影仪所使用的接口。后来液晶显示器出现,也带有VGA接口。显示器内置了A/D转换器,将模拟信号转换为数字信号在液晶显示器上显示。

由于线材与信号干扰等一系列问题,VGA使用时一般仅能够达到1080p分辨率,在高分辨率下字体容易虚,信号线长的话,图像有拖尾现象。在数字设备高度发展的今天,VGA接口已逐渐退出舞台,一些显示器也不再带有VGA接口。

DVI

DVI的种类非常多,接口上有DVI-A、DVI-D、DVI-I,又可分为单通道与双通道。DVI-A(DVI-Analog)接口只传输模拟信号,实质就是VGA模拟传输接口规格,常用于转接显卡的DVI-I输出到VGA显示器接口。DVI-D(DVI-Digital)接口是纯数字接口,不兼容模拟信号。DVI-I(DVI-Integrated)接口,兼容DVI-I和DVI-D两种插头,兼容数字和模拟信号。它们的接口形状不同,如果接口不匹配就无法插入使用。

 以下是它们支持的最大分辨率与频率:

 现在有些支持4K分辨率的显示器,也带有DVI接口。为了达到4K的分辨率,在接口使用上就需要注意,不要使用DVI接口,否则是达不到4K分辨率的。

HDMI

HDMI接口在2002年提出,现在已经发展到HDMI 2.1标准,而且随着行业发展,HDMI 2.1标准已经能够支持4K 120Hz及8K 60Hz,支持高动态范围成像(HDR),可以针对场景或帧数进行优化,向后兼容HDMI 2.0、HDMI 1.4。最主要的是,它是视音频同时传输的。

如今很多的显示器内置音箱,使用一根HDMI线,就可以同时完成图像和声音的传输,这也是HDMI的一大优势。也因为如此,HDMI成为了当今显示器最常见的接口。我们在使用时,也应该优选这个接口。

HDMI在物理接口上,有几种类型。主要有标准HDMI接口,mini HDMI接口和Micro HDMI接口。对于长距离传输的HDMI线,一般线材较硬,这样,就尽量使用带有标准的HDMI接口的设备,以得到稳固的连接。Mini HDMI接口和Micro HDMI接口则更适合于小设备的使用。

DisplayPort

DP(Display Port)接口是HDMI接口的竞争对手。在一些显卡与显示器上,除了有HDMI接口,也带有DP接口。目前DP接口发展到1.4版本,能传输10bit的4K 120Hz视频,也可以支持8K 60Hz视频。DP1.4兼容USB Type-C接口,这就意味着,我们可以使用DP1.4协议,在USB 3.1传输数据的同时,同步传输高清视频。从趋势来看,这对于推动周边影音及影像设备的升级,是非常有好处的。而且,DP接口也可以单根线同步传输声音,达到使用显示器声音的使用方式。

DP口定义了两种接头,全尺寸(Full Size)和迷你DP(Mini),两种接头皆是20针,但迷你接头的宽度大约是全尺寸的一半。明基PD2500Q、PD2700U等显示器均带有Mini DP接口。在DP接口旁,都有一个“D”型的标志。

有些高端的显示器,会带有DP的输出口。这是一个非常好的功能。这样就能以菊花链的形式,完成显示器扩展级联。菊花链是最基本的拓扑结构,一般需要多屏扩展时,多是从显卡的多个端口输出到显示器,这不仅连线不便,而且对于不同的接口和分辨率,也不一定能满足一致。而使用支持菊花链技术的显示器,就能从一个显示器直接连接到另一个,满足多个级联。

(注在电子电气工程中菊花链代表一种配线方案,例如设备A和设备B用电缆相连,设备B再用电缆和设备C相连,设备C用电缆和设备D相连,在这种连接方法中不会形成网状的拓扑结构,只有相邻的设备之间才能直接通信,例如在上例中设备A是不能和设备C直接通信的,它们必须通过设备B来中转,这种方法同样不会形成环路。因为最后一个设备不会连向第一个设备。这种连线方法能够用来传输电力,数字信号和模拟信号)

Type-C

USB接口早已被大家所熟知,以往USB接口被设计用来传输数据,所以它在移动硬盘等存储设备的使用上非常常见。现在USB 3.1 Gen2的速度已经达到10Gbps,设计标准也可以满足视频、数据兼顾传输,所以在一些显示器上,也能够发现USB 3.1接口。现在的USB3.1接口,已经能够满足4K 30P的分辨率显示,大致与HDMI1.4在同一水平。但它以后再进行升级,必将能成为最通用的显示传输方式。需要注意的是,USB 3.1 Gen1就是USB 3.0,而USB 3.1 Gen2才是真正的USB3.1。

USB3.1是传输协议,而USB接口形状有好多种,拿USB 3.1的接口来说,有Type-A、Type-B以及Type-C。Type-C的形状最小,也被宣传的最多。我们所说的Type-C接口,仅仅是接口形状,它并不对等于USB3.1。Type-C接口,也被用在了雷电3上。

Type-C接口的好处在于可以正反插拔,部分Type-C接口也同样具有数据、电力、信号传输的功能,被应用到显示器上时可以带来很多方便。Type-C笔记本用户可以透过一个接口完成充电和连接显示器,还能使用显示器上拓展出来的USB接口,极大地减少了笔记本的负担。

雷电

雷电(thunderbolt)接口有Intel提出来,在苹果电脑上用的非常多,苹果的中文官网将其译为雷雳。它以速度快,传输稳定而著称。雷电接口的技术融合了PCI Express和DisplayPort(就是上面说的DP口)两种通信协议。其中PCI Express用于数据传输,可以非常方便地进行任何类型设备扩展;DisplayPort用于显示,能同步传输1080p乃至4K视频和最多八声道音频。并且两条通道在传输时都有自己单独的通道,不会产生任何干扰。

雷电接口到现在已经发展到三代。雷电1的速度达到10Gbps,雷电2的速度达到20Gbps,现在的雷电3达到40Gbps,依然处于民用传输速度的顶端。容易让人混淆的是,雷电接口的形状在雷电1和雷电2外形上采用的是Mini DP的外形,而雷电3采用的是USB Type-C的外形,但即使外形不同,它们采用的数据传输方式都是雷电协议。不像其它接口能直观地从形状就能分辨出来,使用雷电接口需要认准接口旁的闪电标志,以作区分。

雷电口也支持菊花链连接。而且,我们也可以在显示器下级连上雷电口的硬盘,作为扩展使用。这也是雷电口菊花链的优势所在。

总结来看,雷电接口是集信号、数据、电力传输为一身的集成性接口,可以极大地提高使用效率,目前这个接口多应用于苹果相关设备上。

这些常用的接口在各类显示器上都可以看见,对于我们日常使用的显示器,如今HDMI占有绝对的主导位置。它成熟稳定,也能够满足日常的需要,但是应用于显示器硬件校色时,并不推荐使用HDMI接口,它的RGB范围会有缺失,校色时选择DP接口是更好的选择。其余接口的实用性也很高,一些相对“高冷”的接口目前也在不断普及。因此,根据需要进行选择,才是最根本的解决方式。

一、先与电脑连接好两个显示器

显示器插口
  1. 电脑必须要有两个显示器插口,如HDMIDVIVGA都可以,其他参考【科普】显示器VGA、DVI、HDMI、DP等各种接口详细科普

  2. 使用相应线材连接显示器,可以一个连接DVI一个连接VGA,或是HDMI。使用什么线,取决于你的显示器支持什么接口。

  3. 连接完成,开机一般两个显示器可以正常显示。

注意事项:使用两个信号线分别将两个显示器连接到显卡上两个视频输出接口。电脑显卡必须支持双输出,有两个用于连接不同显示器的视频接口。

二、Linux(Gnome)连接两个显示器设置方法

Archlinux Gnome 双屏设置
  1. 打开设置显示器

  2. 选择加入镜像等设置. 其中加入是指两个屏显示不同内容,而镜像是副屏与主屏完全一样,适合教学演示之用。

三、WIN7连接两个显示器设置方法

  1. 在桌面空白处右击鼠标选择“屏幕分辨率”选项; 进入分辨率设置对话框。

  2. 如果电脑主机显卡连接有两台显示器的,就会有显示。单击“检测”进行检测。然后点击多显示器后面的按钮框,就可以看到“复制这些显示”和“扩展这些显示”两个选项。

  3. 选择的“复制这些显示”,那么在两个显示器上你会看到一模一样的Windows 窗口。适合教学演示、为多人展示相同内容的场所,也适合会场和户外等商业展示。

四、WIN10连接两个显示器设置方法

  1. Windows 10桌面上单击右键,选择“显示设置”。或通过快捷键Win + I打开设置,然后导航至“系统”>“显示”。

  2. 检测并配置显示器:系统将自动检测到两个显示器。在“显示”页面,你可以看到显示器的代表图标,点击“标识”按钮帮助区分哪个是显示器1,哪个是2。随后,选择“多显示器设置”,设定为“扩展这些显示器”,以实现不同内容的显示。

  3. 自定义布局:通过拖动显示器图标,你可以根据实际摆放位置调整虚拟布局,确保操作顺畅。

  4. 调整分辨率与方向:进一步个性化每个显示器的显示设置,包括分辨率和方向调整,确保最佳视觉效果。

  5. 保存设置:调整完毕后,设置会自动保存,无需额外确认,你可以立即体验双屏带来的便捷

今天终于使用上了双屏显示器,其中主屏使用的是三星S27D590, 副屏是DELL的一个22寸的屏幕。其中,主屏通过VGA接口直接与主机连接,同时副屏通过HDMI接口也直接与主机相连,顺利使用上了双显示器。测试通过三星S27D590上的HDMI转接到副屏上没有成功,这留待以后再研究。连接两块屏幕后,一个最显著的地方是它们的亮度不同,于是需要调整屏幕的亮度,而这个三星S27D590只有一个按键,一时有点让人琢磨不透。经过一翻百度,获得答案,原来三星显示器的按键是一个叫做五维式按键的东西,百度问答给出了答案:

根据三星显示器的不同型号,调整屏幕亮度的方法,请参考以下方式:

  1. 普通按(或触摸)键:按(或触摸)机身的【MENU】键,依次选择【图像】-【亮度】 , 根据个人的需求调整亮度数值即可。
  2. 五维式按键:按【电源】键,将摇杆向上拔动,光标移至菜单位置,按【电源】键,依次选择【图像】-【亮度】,进行调整。
  3. 没有菜单选择的显示器,需要在电脑上安装MagicTune(魔调软件),通过此软件进行调整。

参考网址:三星显示器亮度在显示器上怎么调

JASON 语言简介

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
  • JSON 是轻量级的文本数据交换格式
  • JSON 独立于语言:JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持 JSON
  • JSON 具有自我描述性,更易理解

详细参考:jason数据格式

Shell脚本解析JASON数据

使用jq解析JASON数据

jq 是一个小型跨平台解决方案,用于以更短、更简单、更轻松的方式管理 JSON 数据。安装jq

1
sudo pacman -S jq
  1. jq . 命令美化 json 数据。
1
curl "https://jsonplaceholder.typicode.com/posts" | jq .

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
},
{
"userId": 1,
"id": 3,
"title": "fugiat veniam minus",
"completed": false
}
... // remaining list of data
]
  1. 从 JSON 获取特定字段的值

我们可以使用 jq.[].field_name 从 JSON 数据数组中获取任何特定字段的值。

1
curl "https://jsonplaceholder.typicode.com/posts" | jq '.[].id'

输出:

1
2
3
4
1
2
3
...
  1. 从 JSON 中获取第一个项目的标题
1
curl "https://jsonplaceholder.typicode.com/posts" | jq '.[0].title'

输出:

1
"delectus aut autem"

使用grep解析JSON

grep 命令也可用于解析 JSON 数据。

示例 JSON 文件:

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"id": 1,
"name": "Andres Gustov",
"email": "andddy7@gmail.com"
},
{
"id": 2,
"name": "Anthony Marklov",
"email": "antman33@gmail.com"
}
]

示例脚本:

1
grep -o '"email": "[^"]*' examplejsonfile.json | grep -o '[^"]*$'

我们使用 -o 选项仅选择与给定模式匹配的行。然后,我们指定模式'"email": "[^"]*',这意味着我们想要键 email 的所有值。之后,我们传递 JSON 文件来查找模式。最后,我们使用另一个 grep -o 命令将结果通过管道输出,以删除除值之外的所有内容。

输出:

1
2
andddy7@gmail.com 
antman33@gmail.com

使用python3解析JSON

我们还可以使用 python 的 json 模块来处理 JSON 操作。

1
curl -s 'https://jsonplaceholder.typicode.com/posts' | \ python3 -c "import sys, json; print(json.load(sys.stdin))"

获取特定字段值

1
2
curl "https://jsonplaceholder.typicode.com/posts" | \ 
python3 -c "import sys, json; data=json.load(sys.stdin); print([d['id'] for d in data])"

输出:

1
2
3
4
1
2
3
...

获取第一个项目的标题

1
2
curl "https://jsonplaceholder.typicode.com/posts" | \ 
python3 -c "import sys, json; print(json.load(sys.stdin)[0]['title'])"

输出:

1
"delectus aut autem"

YAML语言简介

AML 是"YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。

YAML 的语法和其他高级语言类似, 并且可以简单表达清单、散列表, 标量等数据形态。 它使用空白符号缩进和大量依赖外观的特色, 特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。

YAML常在Linux作为一种配置文件出现,详细使用规则参考:YAML入门教程

Shell中解析YAML

在 Bash 中直接操作 YAML 需要一点创造性,因为 Bash 没有内置支持解析 YAML。然而,你可以使用外部工具,如 yq(一个轻量级且便携的命令行 YAML 处理器),来高效地与 YAML 文件交互。

安装YAML解析器

1
sudo pacman -S yq

假设你有一个名为 config.yaml 的文件,内容如下:

1
2
3
4
5
6
database: 
host: localhost
port: 5432
user:
name: admin
password: secret
  1. 要读取数据库主机,你可以如下使用 yq
1
2
yq e '.database.host' config.yaml
>localhost
  1. 要在 config.yaml 中更新用户的名称,使用带 -i(就地)选项的 yq eval 命令:
1
yq e '.user.name = "newadmin"' -i config.yaml

用以下命令验证更改:

1
2
yq e '.user.name' config.yaml
> newadmin
  1. 要在数据库部分下添加一个新字段 timeout
1
yq e '.database.timeout = 30' -i config.yaml
  1. 要移除用户下的密码:
1
yq e 'del(.user.password)' -i config.yaml

此操作将从配置中删除密码字段。

记住,yq 是一款强大的工具,具有更多功能,包括将 YAML 转换为 JSON、合并文件,甚至更复杂的操作。请参考 yq 文档以进一步探索。

参考文章

变量a是一个带空格的字符串,现在用"hdpusr400"替换变量a中的"hduser302":

字符串变量的替换方式

定义变量a
1
2
[liusiyi@localhost ~]$ echo $a
hduser302 hduser302 /apps/hduser302/student/

用变量替换${a//}做字符替换

用变量替换${a//}做字符替换
1
2
3
4
5
6
7
#替换第一个匹配的字符串 
[liusiyi@localhost ~]$ echo ${a/hduser302/hdpusr400}
hdpusr400 hduser302 /apps/hduser302/student/

#替换所有匹配的字符串 
[liusiyi@localhost ~]$ echo ${a//hduser302/hdpusr400}  
hdpusr400 hdpusr400 /apps/hdpusr400/student/

用 sed 做字符串替换

用sed做字符串替换
1
2
3
4
5
6
7
#替换第一个匹配的字符串 
[liusiyi@localhost ~]$ echo $a | sed 's/hduser302/hdpusr400/'   
hdpusr400 hduser302 /apps/hduser302/student/

#替换所有匹配的字符串 
[liusiyi@localhost ~]$ echo $a | sed 's/hduser302/hdpusr400/'   
hdpusr400 hdpusr400 /apps/hdpusr400/student/

用 awk 做字符串替换

用awk做字符串替换
1
2
3
4
5
6
7
#替换第一个匹配的字符串 
[liusiyi@localhost ~]$ echo $a | awk '{gsub(/hduser302/,"hdpusr400",$3);print $0}'
hdpusr400 hduser302 /apps/hduser302/student/

#替换所有匹配的字符串 
[liusiyi@localhost ~]$ echo $a | awk '{gsub(/hduser302/,"hdpusr400");print $0}'   
hdpusr400 hdpusr400 /apps/hdpusr400/student/

数组所有元中的字符串替换

数组所有元替换
1
2
Arr=($(ls -d /run/media/$USER/*/*))
Brr=(${Arr[*]//"/run/media/$USER"/"$HOME"})

Arr数组元中,所有元包括绝对路径/run/media/$USER, 而Brr数组将其全部替换为$HOME.

已经完成了ugit.sh程序,利用它可以方便的将仓库建立在U盘上,这也就达成了随身携带U盘仓库而不依赖网络的问题。但是在最初实现时,对Shell的理解不够深入,一些基础的命令没有用好,从而增加了一些循环判断,尽管在使用体验上不会有什么影响,但是仔细测试效率时还是有差别的,同时从维护脚本的角度讲不是足够简洁。本着精益求精的原则,决定进一步精简脚本,实现Shell层次上的效率极大化!在所有的问题中,一步列出所有U盘并且将U盘中的目录都添加绝对路径是一个重要的步骤,本文实现了这个一步操作。代码如下:

列出子目录并添加绝对路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ls -d /run/media/$USER/*/*
# 运行结果
/run/media/feng/BNU-FZH/article.git
/run/media/feng/BNU-FZH/cexam.git
/run/media/feng/BNU-FZH/config.git
/run/media/feng/BNU-FZH/document.git
/run/media/feng/BNU-FZH/latex.git
/run/media/feng/BNU-FZH/python.git
/run/media/feng/BNU-FZH/script.git
/run/media/feng/BNU-FZH/System Volume Information
/run/media/feng/feng/20230203_094347.jpg
/run/media/feng/feng/2023_07_19 17_25 Office Lens (1).jpg
/run/media/feng/feng/2023_07_19 17_25 Office Lens.jpg
/run/media/feng/feng/截图 2024-06-18 18-03-16.png
/run/media/feng/feng/截图 2024-06-18 18-06-42.png
/run/media/feng/feng/PDF文件
/run/media/feng/feng/System Volume Information

可以看到在路径/run/media/feng/下有两个U盘,分别是BNU-FZHfeng, 这是路径/run/media/$USER/*/*中左起第一个*所代表的,而最后一个*表示列出此目录下的内容。-d选项,则控制ls命令列出路径。一条命令实现了列出所有U盘目录的功能,极大的提高了效率!

Rust的官方网站是https://www.rust-lang.org, 它是一门赋予每个人构建可靠且高效软件能力的语言. 按官方介绍,选择Rust的原因主要为:

  • 高性能:Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
  • 可靠性:Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
  • 生产力:Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具——包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。

学习Rust语言,本博客提供以下几个途径:

本博客决定边学习边记录,以期完成一篇入门级的简洁实用教程。第一课当然是编写Hello World程序,其源码为:

hello_world.rs
1
2
3
4
5
// hello_world.rs
fn main() {
let s = "hello world!";
println!("{}", s);
}

由于其没有复杂的依赖关系,直接使用rustc编译即可,其他所有选项使用默认值:

编译hello_world.rs
1
rustc hello_world.rs

编译完成后,在本地文件夹中生成了可执行程序:hello_world, 执行程序./hello_world, 控制台上输出了hello world!字符串, 第一个Rust程序已经成功! 根据这个最简单的例子,我们得到一些基本的规则:

  • 一般Rust源代码的后缀名使用.rs表示。源码一定要注意使用utf-8编码。

  • 第一行是注释语句,Rust的注释是C语言系列风格的,行注释采用//开头,块注释使用/**/包围。它还支持更高级的文档注释,将在后文中详细展开说明。

  • fn是一个关键字(key word),函数定义必须以这个关键字开头。函数体使用大括号来包含。fn是单词function的缩写,在Rust中,设计者比较偏向使用单词缩写,即使是关键字也不例外。在代码风格上,某些读者可能开始会有点不习惯。但总体而言,这只是个审美偏好而已,不必过于纠结,习惯就好。

  • 默认情况下,main函数是可执行程序的入口点,它是一个无参数,无返回值的函数。如果我们要定义的函数有参数和返回值,可以使用以下语法(参数列表使用逗号分开,冒号后面是类型,返回值类型使用->符号分隔):

    1
    2
    3
    fn Foo( input1 : i32, input2 : u32) -> i32 {
    ...
    }

  • 局部变量声明使用let关键字开头,用双引号包含起来的部分是字符串常量。Rust是静态强类型语言,所有的变量都有严格的编译期语法检查。关于Rust的变量和类型系统将在后文详细说明。

  • 每条语句使用分号结尾。语句块使用大括号。空格、换行和缩进不是语法规则的一部分。这都是明显的C语言系列的风格。

最简单的标准输出是使用println!宏来完成。请大家一定注意println后面的感叹号,它代表这是一个宏,而不是一个函数。Rust中的宏与C/C++中的宏是完全不一样的东西。简单点说,可以把它理解为一种安全版的编译期语法扩展。这里之所以使用宏,而不是函数,是因为标准输出宏可以完成编译期格式检查,更加安全。

linux shell有交互式与非交互式两种工作模式。我们日常使用shell输入命令得到结果的方式是交互式的方式,而shell脚本使用的是非交互式方式。

shell提供了alias功能来简化我们的日常操作,使得我们可以为一个复杂的命令取一个简单的名字,从而提高我们的工作效率。在shellalias扩展功能是,因此我们可以键入自己定义的alias别名来执行对应的命令。

alias扩展功能,此时仍然可以定义alias别名,但是shell不会将alias别名扩展成对应的命令,而是将alias别名本身当作命令执行,如果shell内置命令和PATH中均没有与alias别名同名的命令,则shell会“抱怨”找不到指定的命令。

在编写脚本时为了提高脚本的通用性,一般使用linux内置的通用命令,例如ls,cat等. 由于脚本是, 所以在脚本中直接使用系统命令即可。

现在有人要问了,在非交互模式的脚本中如何启用alias扩展呢? 答案是可以使用shell的内置命令shopt来开启alias扩展选项。

shopt的使用
1
2
3
shopt -s opt_name                 Enable (set) opt_name.
shopt -u opt_name Disable (unset) opt_name.
shopt opt_name Show current status of opt_name.

alias扩展功能的选项名称是expand_aliases,我们可以在交互式模式下查看此选项是否开启:

1
2
3
sw@gentoo ~ $ shopt expand_aliases
expand_aliases on
sw@gentoo ~ $

可见在交互式模式下alias扩展功能的确是开启的,因此我们才能使用alias别名。我们编写一个脚本来验证一下非交互式模式下alias扩展的设置:

验证alias扩展
1
2
3
4
5
6
7
8
9
#!/bin/bash --login

alias echo_hello="echo Hello!"
shopt expand_aliases
echo_hello

shopt -s expand_aliases
shopt expand_aliases
echo_hello

执行结果为:

1
2
3
4
5
6
sw@gentoo ~ $ ./test.sh
expand_aliases off
./test.sh: line 5: echo_hello: command not found
expand_aliases on
Hello!
sw@gentoo ~ $

另外,alias别名只在当前shell有效,不能被子shell继承,也不能像环境变量一样export。可以把alias别名定义写在.bashrc文件中,这样如果启动交互式的子shell,则子shell会读取.bashrc,从而得到alias别名定义。但是执行shell脚本时,启动的子shell处于非交互式模式,是不会读取.bashrc的。

如果你一定要让执行shell脚本的子shell读取.bashrc的话,可以给shell脚本第一行的解释器加上参数:

1
#!/bin/bash --login

我们有三种方法可以使脚本变成交互式:

  • --login使得执行脚本的子shell成为一个login shelllogin shell会读取系统和用户的profilerc文件,因此用户自定义的.bashrc文件中的内容将在执行脚本的子shell中生效。
  • 让执行脚本的shell读取.bashrc,在脚本中主动source ~/.bashrc即可。
  • bash脚本首行加上-i参数就变成交互式了,即#!/bin/bash -i.