BNU-FZH

fengzhenhua@outlook.com

今天终于使用上了双屏显示器,其中主屏使用的是三星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.

ZshZ-shell)是一款用于交互式使用的shell,也可以作为脚本解释器来使用。其包含了 bashkshtcsh 等其他shell中许多优秀功能,也拥有诸多自身特色。Zsh拥有许多功能强大的插件,其中zsh-autosuggestions可以根据历史记录自动补全命令,但是在使用过程中每次重启终端后zsh-autosuggestions总是清空历史记录,这导致了每次补全都会以当前的输入为基础, 这极大的影响了工作效率。经过研究,原来是我的zsh没有配置历史文件,这导致了输入过的命令没有被记录存储下来,所以就出现了清空历史记录的假象!解决方法是在.zshrc文件中加入历史文件配置,具体如下:

~/.zshrc
1
2
3
4
5
6
7
# History file for zsh
HISTFILE=~/.zsh_history
# How many commands to store in history
HISTSIZE=10000
SAVEHIST=10000
# Share history in every terminal session
setopt SHARE_HISTORY

配置好.zshrc后,再使用zsh时它就会自动记录历史命令了,所以zsh-autosuggestions就可以完美的工作了。

在编写shell脚本时往往需要判断变量是否为整数,然后根据其类型执行不同的操作, 借助expr命令可以方便的实现此功能。expr命令是一个手工命令行计数器,用于在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串。

语法

语法
1
expr 表达式

表达说明

  • 用空格隔开每个项;
  • 用反斜杠 \ 放在 shell 特定的字符前面;
  • 对包含空格和其他特殊字符的字符串要用引号括起来

使用举例

计算字符串长度

计算字符串长度
1
2
> expr length “this is a test
14

抓取字符串

抓取字符串
1
2
> expr substr “this is a test” 3 5
is is

抓取第一个字符数字串出现的位置

抓取第一个字符数字串出现的位置
1
2
> expr index "sarasara"  a
2

整数运算

整数运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 > expr 14 % 9
5
> expr 10 + 10
20
> expr 1000 + 900
1900
> expr 30 / 3 / 2
5
> expr 30 \* 3 (使用乘号时,必须用反斜线屏蔽其特定含义。因为shell可能会误解显示星号的意义)
90
> expr 30 * 3
expr: Syntax error
> expr a + 1 (当使用一个字符与整数1相加时会返回提示“expr: 参数不是整数”)
expr: 参数不是整数
> echo $? (检测返回码,得到1, 这是后面进行整数判断的依据。)
1

判断整数的实现

整数判断
1
2
3
4
5
6
7
8
9
10
11
12
expr "$TLS_SNum" + 1 &> /dev/null
if [ $? -eq 0 ]; then
if [ $TLS_SNum -lt $1 -o $TLS_SNum -gt $2 ]; then
echo "编号超出范围,请重新选择编号!"
exit
else
NEO_OUT_H=$TLS_SNum
fi
else
echo "输入非数字,请重新输入编号!"
exit
fi

上述代码源于diary.sh脚本,其使用expr计算变量TLS_SNum1之和,如果变量TLS_SNum是数字,则变量$?=0, 否则$?=1, 只有当变量是整数时才能比较它与其他数字的大小关系。这里需要特别注意,网络上一些教程计算TLS_SNum0的和,但是当TLS_SNum=0时,返回的$?=1, 这会导致误判!!所以,我修改其为$TLS_SNum +1.

1. 单括号[ ] 和 test

左括号[test本质是一样的,是shell的内部命令,所以><等会被解释为重定向符号,而不是比较符号:

1
2
3
4
bash-3.2$ type [ [[ test
[ is a shell builtin
[[ is a shell keyword
test is a shell builtin

右括号]表示判断结束,可以通过man [查看支持的判断语句:

1
$ man [

1.1 文件相关判断

  • -d file: True if file exists and is a directory.
  • -f file: True if file exists and is a regular file.
  • -h file: True if file exists and is a symbolic link.
  • -S file: True if file exists and is a socket.
  • file1 -nt file2: True if file1 exists and is newer than file2.

1.2 字符串相关判断

  • string: True if string is not the null string.
  • s1 = s2: True if the strings s1 and s2 are identical.
  • s1 < s2: True if string s1 comes before s2 based on the binary value of their characters.

1.3 整型判断

  • n1 -eq n2: True if the integers n1 and n2 are algebraically equal.
  • n1 -ne n2: True if the integers n1 and n2 are not algebraically equal.
  • n1 -gt n2: True if the integer n1 is algebraically greater than the integer n2.
  • n1 -ge n2: True if the integer n1 is algebraically greater than or equal to the integer n2.
  • n1 -lt n2: True if the integer n1 is algebraically less than the integer n2.
  • n1 -le n2: True if the integer n1 is algebraically less than or equal to the integer n2.

1.4 多个判断连接

  • ! expression: True if expression is false.
  • expression1 -a expression2: True if both expression1 and expression2 are true.
  • expression1 -o expression2: True if either expression1 or expression2 are true.

2. 双括号[[ ]]

双括号是shell的关键字,会返回一个状态码,所以也可以作为判断条件使用。(更加通用)

  • [[支持字符串的模式匹配,=~支持正则匹配
  • [[返回状态码,所以可以与shell中的 &&||一起使用:
1
2
$ [[ 1 < 2 ]] && echo "1 < 2" || echo "1 >= 2"
1 < 2