Linux系列文章目录
补充内容 Windows通过SSH连接Linux
第一章 Linux基本命令的学习与Linux历史
第二章(上) Vim课堂与实验
文章目录
- Linux系列文章目录
- 一、前言
- 二、学习内容:
- 2.1 上课内容
- 2.1.1 为什么要学习脚本编写?
- 2.1.2 Bash编程语言
- 2.1.3 SheBang
- 2.1.4 Shell的变量
- 2.1.5 expr 命令算数运算
- 2.1.6 read 命令用户输入
- 2.1.7 test 命令条件判断
- 2.1.8 bool、if与case 判断条件
- 2.1.9 loops 循环
- 2.1.10 动手试试
- 2.1.11 function 方法
- 2.1.12 stream 流操作
- 2.1.13 推荐阅读
- 2.2 实验内容
- 2.2.1 一些让事情变得更简单的提示
- 2.2.2 脚本参考
- 三、问题描述
- 3.1编写具有以下行为的 shell 脚本:phonebook
- 3.2 骨架代码
- 3.2.1 Bash的框架代码
- 3.2.2 Python的框架代码
- 四、解决方案:
- 4.1 制作一个电话簿(Bash)
- 4.2 制作一个电话簿(Python)
- 五、总结:
- 5.1 学习总结:
一、前言
原因:学校里没有开设相关课程但是学校其他课程与学习开发过程却需要用到相关知识,所以写此专栏以总结课程与实验内容
目的:记录学习过程内容做到条例清晰,通俗易懂,最好的学习方式是教会别人,以后忘记若复习能立即掌握(麻辣香蝈蝈)
- 第二堂课本章拆成两节本节掌握Shell
- 伯克利大学Linux系统管理大部分课程与实验内容都会涵盖
- 题目为方便已翻译成中文,可在B站查找视频教程
- 学习地址官网首页
二、学习内容:
2.1 上课内容
- 上课主要讲的是Shell脚本编写
- 为了方便可直接在命令行输入学习
2.1.1 为什么要学习脚本编写?
- 需要频繁运行一些命令
- 希望避免重复操作(DRY:Don’t Repeat Yourself)
- 可以将任务描述为一系列步骤,让计算机执行
2.1.2 Bash编程语言
注:这些是他们公开课的ppt可去官网下载
- Bash是一个shell,也是一种编程语言
运行Bash脚本的方法:
- 使用bash /path/to/script命令(通常是.sh文件)
- 使用chmod +x /path/to/script命令赋予脚本执行权限,然后运行/path/to/script
使用shebang指定解释器(例如#!/bin/bash)
2.1.3 SheBang
- Shebang(#!)是脚本文件的第一行,用于指定该脚本文件将使用的解释器。Shebang行以字符#!开头,后跟解释器的路径
Shebang的作用:
- 指定解释器:告诉操作系统使用哪个解释器来运行脚本文件。
- 提高可移植性:通过指定Shebang,可以确保在不同环境中运行脚本时使用正确的解释器。
- 方便执行:无需在命令行中手动指定解释器,只需直接执行脚本文件即可。
使用Shebang的步骤:
- 在脚本的第一行添加Shebang:例如#!/bin/bash。
- 确保脚本具有执行权限:使用chmod +x script.sh命令赋予脚本执行权限。
- 直接执行脚本:在命令行中输入./script.sh来执行脚本。
2.1.4 Shell的变量
- Shell变量
- 空格很重要!
- echo:这是一个Shell命令,用于在终端输出文本。
- “
$
Name”:这是对变量的引用。 - 在变量名前加上$符号,可以获取变量的值。
- 双引号确保变量在被输出时内容会被解析和显示
2.1.5 expr 命令算数运算
- 使用 expr 命令进行算术运算
- 变量没有类型(都是字符串)
2.1.6 read 命令用户输入
2.1.7 test 命令条件判断
- 0是正确,1是错误
他的解释是运行失败后1是退出
- 符号判断
记忆方法:
- -eq(equal)
- -ne(not equal)
- -gt(greater than)
- -ge(greater than or equal to)
- -lt(less than)
- -le(less than or equal to)
命令执行:
- 当你在Shell中执行任何命令时,Shell会启动一个子进程来运行该命令。
- 当子进程完成时,它会返回一个退出状态给父进程(即Shell)。
获取退出状态:
- Shell会将最近一个命令的退出状态存储在特殊变量?中。
- 通过引用
$
?,你可以获取并使用这个退出状态。 $
# 用于表示传递给 shell 脚本的参数数量。$# 将返回这些参数的数量。
进行错误处理和控制流:
- 在脚本中使用$?来检查命令是否成功执行
变量引用概念
- Shell:通过变量名直接修改值,没有指针的间接修改概念。例如,name="Bob"直接修改变量name的值。
- 指针(如C语言):通过指针可以间接修改值。例如,*ptr = 100修改ptr指向的变量的值。
2.1.8 bool、if与case 判断条件
自己实验试试 if then 与 case in 判断
- 我们写的时候要保证[ ]方括号里面内容的间隔别出错
2.1.9 loops 循环
想想不同语言之间循环的区别
2.1.10 动手试试
课堂上的动手实验
运行 ./mycoolscript.sh
前要授权
chmod +x mycoolscript.sh
动手操作
这是在 .sh 脚本文件下的内容
这题对实验有帮助
可用vim打开
课上第一行写解释器路径的时候他能跳出选项
当时我不知道操作但在后来vim的实验中与作弊表中找到方法
详细可看上节课内容 Vim的使用与作弊表
我直接写出实现的方法
关键词补全方法
- Ctrl + P
补全路径的方法
- 使用 Vim 的内置补全机制。
- 按 Ctrl-X 然后按 Ctrl-F 可进行文件名补全
- 按Ctrl-X 然后按 Ctrl-K 可进行关键词补全。
2.1.11 function 方法
解释:
在 Bash 脚本中,$1 是一个位置参数,它代表传递给脚本或函数的第一个参数。
类似的$2就是第二个
- greet是函数名
- 后面跟的则是参数
2.1.12 stream 流操作
对文件操作
管道
- 别跟或混在一起
- | 左边的输出是右边的输入
2.1.13 推荐阅读
2.2 实验内容
上节学Vim
这节主要是Shell部分
- Vim速查表
2.2.1 一些让事情变得更简单的提示
- bash有一个 append 运算符,正如您可能猜到的那样,它会附加 从第一个参数到第二个参数结束的数据。
>>
$ cat foobar.txt
foobar
$ echo "hello, reader" >> foobar.txt
$ cat foobar.txt
foobar
hello, reader
- bash还有一个重定向算子,它接受一个的输出命令并将其输出到文件中。
>
$ cat foobar.txt
foobar
$ echo "hello" > foobar.txt
$ cat foobar.txt
hello
$ > foobar.txt
$ cat foobar.txt
$
- 只需写入文件和读取文件即可持久保存数据。
在 bash 中,可以通过命令更改行。
sed 是独立且强大的文本处理命令行工具
-i 选项使得 sed 会就地(in-place)编辑文件。
- sed:流编辑器(Stream Editor),用于处理和转换文本数据。
- -i:直接修改文件内容,不会将结果输出到终端。
“
s/<old>/<new>/g
”:这是 sed 的主要操作部分,表示进行替换操作。
具体的含义如下:
s
:表示“替换”(substitute)。<old>
:旧字符串(被新字符串替代)。<new>
:新字符串。g
:表示全局替换,在每一行中找到所有出现的 字符串都进行替换,而不仅仅是替换每行中的第一个出现的实例。./filename
:这是要进行编辑的目标文件的路径和文件名。./ 表示当前目录,后面跟着的 filename 代表文件名。
$ echo "hello 123" > foobar.txt # writes hello to foobar.txt
$ cat foobar.txt
hello 123
$ sed -i "s/h/j/g" foobar.txt
$ cat foobar.txt
jello 123
# You can also use regex: learn more at regex101.com
$ sed -i "s/[0-9]\{3\}/world/g" foobar.txt
$ cat foobar.txt
jello world
- 通过位置参数公开了其命令行参数:bash
$<Integer>
#!/bin/bash
# contents of argscript.sh
echo "$1"
echo "$2"
$ ./argscript.sh foo bar
foo
bar
- 上面的命令用于执行这个脚本,foo 和 bar 是传递给脚本的参数。
对于引号原文给了复杂的定义说简单点就下面两点
- 单引号:内容就是字面意思,适合你不想让内容变的情况。
- 双引号:里面可以有变量,会自动插入变量的值。适合你需要动态内容的情况。
$ echo '$LANG'
$LANG
$ echo "$LANG"
en_US.UTF-8
- 您可以通过列表与命令行参数进行交互
这里是讲python相关的
在 Python 中,sys.argv 是一个列表,它包含了命令行参数。
- 您可以通过列表与命令行参数进行交互
- 命令行运行一个 Python 脚本时,你可以传递参数给这个脚本。
- 这些参数会被存储在 sys.argv 列表中。
- 首先导入 sys 模块使用 sys.argv
- sys.argv[0] 是脚本本身的名称。
- sys.argv[1] 开始是传给脚本的第一个参数。
- 可以根据需要继续使用 sys.argv[2], sys.argv[3] 等等。
# !/usr/bin/python
# contents of argscript.py
import sys
print(sys.argv[1])
print(sys.argv[2])
# end of file
$ ./argscript.py foo bar
foo
bar
- python允许您使用常用的功能操作文件 具有控制结构
# !/usr/bin/python
# contents of fileman.py
with open('./newfile.txt', 'w') as f: f.write("hello from python\n")
# end of file
$ python fileman.py
$ cat newfile.txt
hello from python
- 把phonebook.sh文件设置为可执行文件:
chmod +x phonebook.sh
2.2.2 脚本参考
几乎都是PPT的内容
可以进去写一写例题
链接地址:脚本参考
三、问题描述
- 您将完成一个经典的第一个 shell 脚本任务:制作一个 电话簿。
- 两种语言脚本区别
3.1编写具有以下行为的 shell 脚本:phonebook
./phonebook new <name> <number>
将条目添加到电话簿中。不要担心重复(始终添加一个新条目,即使名称相同)。./phonebook list
显示电话簿中的每个条目(无特别说明) 订单)。如果电话簿没有条目,则显示phonebook is
empty./phonebook remove <name>
删除与该名称关联的所有条目。如果该名称不在电话簿中,则不执行任何操作。./phonebook clear
删除整个电话簿./phonebook lookup <name>
显示与该名称关联的所有电话号码。您可以假设所有电话号码都采用以下形式:其中是 0-9
之间的数字。ddd-ddd-ddddd
注意:您可以打印每行的名称和编号。对于额外的挑战,请尝试打印所有不带其姓名的电话号码。(有关详细信息,请参阅下面的示例)
$ ./phonebook new "Linus Torvalds" 101-110-0111
$ ./phonebook list
Linus Torvalds 101-110-1010
$ ./phonebook new "Tux Penguin" 555-666-7777
$ ./phonebook new "Linus Torvalds" 222-222-2222
$ ./phonebook list
Linus Torvalds 101-110-1010
Tux Penguin 555-666-7777
Linus Torvalds 222-222-2222
# OPTIONAL BEHAVIOR
$ ./phonebook lookup "Linus Torvalds"
101-110-1010
222-222-2222
# ALTERNATIVE BEHAVIOR
$ ./phonebook lookup "Linus Torvalds"
Linus Torvalds 101-110-1010
Linus Torvalds 222-222-2222
$ ./phonebook remove "Linus Torvalds"
$ ./phonebook list
Tux Penguin 555-666-7777
$ ./phonebook clear
$ ./phonebook list
phonebook is empty
python的测试文本(选做)
# Add new entries
python phonebook.py new "Linus Torvalds" 101-110-0111
python phonebook.py new "Tux Penguin" 555-666-7777
python phonebook.py new "Linus Torvalds" 222-222-2222
# List entries
python phonebook.py list
# Lookup an entry
python phonebook.py lookup "Linus Torvalds"
# Remove an entry
python phonebook.py remove "Linus Torvalds"
# List entries to verify removal
python phonebook.py list
# Clear the phonebook
python phonebook.py clear
# List entries to verify clearing
python phonebook.py list
3.2 骨架代码
填充下面代码
完成上面 3.1 题目的要求
3.2.1 Bash的框架代码
#!/bin/bash
PHONEBOOK_ENTRIES="bash_phonebook_entries"
if [ "$#" -lt 1 ]; then
exit 1
elif [ "$1" = "new" ]; then
# YOUR CODE HERE #
elif [ "$1" = "list" ]; then
if [ ! -e $PHONEBOOK_ENTRIES ] || [ ! -s $PHONEBOOK_ENTRIES ]; then
echo "phonebook is empty"
else
# YOUR CODE HERE #
fi
elif [ "$1" = "lookup" ]; then
# YOUR CODE HERE #
elif [ "$1" = "remove" ]; then
# YOUR CODE HERE #
elif [ "$1" = "clear" ]; then
# YOUR CODE HERE #
else
# YOUR CODE HERE #
fi
3.2.2 Python的框架代码
#!/usr/bin/env python
import sys
import os
PHONEBOOK_ENTRIES = "python_phonebook_entries"
def main():
if len(sys.argv) < 2:
exit(1)
elif sys.argv[1] == "new":
# YOUR CODE HERE #
elif sys.argv[1] == "list":
if not os.path.isfile(PHONEBOOK_ENTRIES) or os.path.getsize(
PHONEBOOK_ENTRIES) == 0:
print("phonebook is empty")
else:
# YOUR CODE HERE #
elif sys.argv[1] == "lookup":
# YOUR CODE HERE #
elif sys.argv[1] == "remove":
name = " ".join(sys.argv[2:])
# YOUR CODE HERE #
elif sys.argv[1] == "clear":
# YOUR CODE HERE #
else:
name = " ".join(sys.argv[1:])
with open(PHONEBOOK_ENTRIES, 'r') as f:
lookup = "".join(filter(lambda line: name in line, f.readlines()))
# YOUR CODE HERE #
if __name__ == "__main__":
main()
四、解决方案:
4.1 制作一个电话簿(Bash)
- 先自己写一点试一试
- 要注意 [ ] 里面参数之间的空格
- 要注意 Bash 脚本中的双引号
使用双引号原因如下
处理空格:
- 如果文件路径(在这里是变量 SAVE 的值)中含有空格,双引号的使用可以保证整个字符串被视为一个单一的参数。
- 例如,如果 SAVE 的值为 “My Documents/SAVEPLACE”,没有双引号有可能导致错误解析。
防止扩展错误:
-
尤其是当变量为空或未定义时,使用双引号可以避免一些潜在的错误。
-
如果没有双引号,脚本可能会尝试解析一个未定义的变量,导致意外的结果。
安全性:
- 双引号也能防止字元解释,比如 *、? 等通配符,确保它们不会被 Bash 解释为文件匹配。
SAVE="My Documents/SAVEPLACE"
echo "$SAVE" # 正确输出:My Documents/SAVEPLACE
echo $SAVE # 可能输出:My Documents/SAVEPLACE 这时会产生两个参数
- 参考代码
大部分参数都有解释
只解释没提到的
下面代码可以成功运行
#!/bin/bash
PHONEBOOK_ENTRIES="bash_phonebook_entries"
if [ "$#" -lt 1 ]; then
exit 1
elif [ "$1" = "new" ]; then
if [ "$#" -ne 3 ]; then
echo "Usage: $0 new <name> <number>"
exit 1
fi
echo "$2 $3" >> "$PHONEBOOK_ENTRIES"
echo "Added: $2 $3"
elif [ "$1" = "list" ]; then
if [ ! -e "$PHONEBOOK_ENTRIES" ] || [ ! -s "$PHONEBOOK_ENTRIES" ]; then
echo "phonebook is empty"
else
cat "$PHONEBOOK_ENTRIES"
fi
elif [ "$1" = "lookup" ]; then
if [ "$#" -ne 2 ]; then
echo "Usage: $0 lookup <name>"
exit 1
fi
if grep -q "$2" "$PHONEBOOK_ENTRIES"; then
grep "$2" "$PHONEBOOK_ENTRIES" | awk '{print $2}'
else
echo "No entry found for $2"
fi
elif [ "$1" = "remove" ]; then
if [ "$#" -ne 2 ]; then
echo "Usage: $0 remove <name>"
exit 1
fi
if grep -q "$2" "$PHONEBOOK_ENTRIES"; then
# 取出内容放入temp后再通过mv覆盖原文件
grep -v "$2" "$PHONEBOOK_ENTRIES" > temp && mv temp "$PHONEBOOK_ENTRIES"
echo "Removed all entries for $2"
else
echo "No entry found for $2"
fi
elif [ "$1" = "clear" ]; then
> "$PHONEBOOK_ENTRIES"
echo "Phonebook cleared"
else
echo "Usage: $0 {new|list|lookup|remove|clear}"
exit 1
fi
! -e "$PHONEBOOK_ENTRIES" 和 ! -s "$PHONEBOOK_ENTRIES":
- -e 是一个条件测试操作符,用于检查文件是否存在。
- -s 是一个条件测试操作符,用于检查文件是否为空。
awk '{print $2}':
- awk 是一个文本处理工具,用于对文本文件中的数据进行处理。
- 在这里,它用于打印匹配行的第二个字段(假设字段以空格分隔)。
- {print $2} 表示只打印每一行的第二个字段。
4.2 制作一个电话簿(Python)
此代码仅供参考
记得升级python版本
#!/usr/bin/env python
import sys
import os
PHONEBOOK_ENTRIES = "python_phonebook_entries"
def main():
if len(sys.argv) < 2:
print("Usage: <script> {new|list|lookup|remove|clear} [args]")
exit(1)
command = sys.argv[1]
if command == "new":
if len(sys.argv) != 4:
print("Usage: <script> new <name> <number>")
exit(1)
name, number = sys.argv[2], sys.argv[3]
with open(PHONEBOOK_ENTRIES, 'a') as f:
f.write(f"{name} {number}\n")
print(f"Added: {name} {number}")
elif command == "list":
if not os.path.isfile(PHONEBOOK_ENTRIES) or os.path.getsize(PHONEBOOK_ENTRIES) == 0:
print("phonebook is empty")
else:
with open(PHONEBOOK_ENTRIES, 'r') as f:
print(f.read(), end='')
elif command == "lookup":
if len(sys.argv) != 3:
print("Usage: <script> lookup <name>")
exit(1)
name = sys.argv[2]
found = False
with open(PHONEBOOK_ENTRIES, 'r') as f:
for line in f:
entry_name, entry_number = line.strip().split(maxsplit=1)
if entry_name == name:
print(entry_number)
found = True
if not found:
print(f"No entry found for {name}")
elif command == "remove":
if len(sys.argv) != 3:
print("Usage: <script> remove <name>")
exit(1)
name = sys.argv[2]
found = False
with open(PHONEBOOK_ENTRIES, 'r') as f:
lines = f.readlines()
with open(PHONEBOOK_ENTRIES, 'w') as f:
for line in lines:
if name not in line.split(maxsplit=1)[0]:
f.write(line)
else:
found = True
if found:
print(f"Removed all entries for {name}")
else:
print(f"No entry found for {name}")
elif command == "clear":
open(PHONEBOOK_ENTRIES, 'w').close()
print("Phonebook cleared")
else:
print("Usage: <script> {new|list|lookup|remove|clear}")
exit(1)
if __name__ == "__main__":
main()
五、总结:
5.1 学习总结:
主要是动手练习
Shebang:指定脚本使用的解释器,例如 #!/bin/bash。 运行脚本
bash /path/to/script
chmod +x /path/to/script
./path/to/script
变量
- 定义变量:NAME=“value”
- 输出变量:echo “$NAME”
- 变量类型:Bash 中的变量是无类型的,一切皆为字符串。
- 表达式求值:使用 expr 命令,例如 FOO=1 然后 expr $FOO + 1
操作符:
- 等于:-eq
- 不等于:-ne
- 大于:-gt
- 大于等于:-ge
- 小于:-lt
- 小于等于:-le
- 布尔操作符:&&(与),||(或)
条件语句
- if
if [ "$1" -eq 79 ]; then
echo "nice"
fi
- if-else
if [ "$1" -eq 79 ];then
echo "nice"
else
echo "darn"
fi
- elif:用于多条件判断
- case:用于多分支选择
循环
记住do done
- for 循环:
基本形式:for ITEM in $ITEMS; do …; done
支持范围:for x in {1…10}; do …; done
- while 循环:
while true; do …; done
函数
- 定义函数:function greet() { echo “hey there $1”; }
- 调用函数:例如 greet “sysadmin decal”
重定向和管道
- 输出重定向:echo “hello” > out.txt
- 追加输出:echo “hello” >> out.txt
- 输入重定向:sort < file
- 管道:command1 | command2
其他重要工具
- Python:用于更复杂的控制结构或需要高级功能的任务。
- argparse:简易 CLI
- fabric:简易部署
- salt:基础设施相关任务
- psutil:监控系统信息
- 其他 shell:例如 zsh、fish、ksh 等,每种 shell 可能有些许语法差异。
不同语言脚本的差异
- 语法差异:Bash 使用简单的命令行语法和工具组合,Python 使用更结构化和面向对象的编程语法。
- 功能和库:Bash 主要用于系统操作和自动化任务,Python 提供了丰富的库和模块用于各种编程任务。
- 执行环境:Bash 通常在 Unix/Linux 环境下运行,Python 可以跨平台运行。
声明:如本内容中存在错误或不准确之处,欢迎指正。转载时请注明原作者信息(麻辣香蝈蝈)。