【笔记】BV1x7411H7wa P2
笔记整理自BV1x7411H7wa P2
前注: shell 的语法可能和很多编程语言不同,所以最好不要带着其余编程语言的想法去带入 shell 语言中
shell中,可以通过diff命令判断两个文件是否相同
---
你可以在shell里面定义一个变量, 如下
上面的例子,给变量foo赋值为bar,然后使用 $foo 来访问 foo 的值。
你需要理解的一个地方是,在处理bash的时候,空格非常关键,因为空格是用于分隔参数而被保留的
例如不可以像下面这样。
是因为foo命令不起作用, foo命令不存在。刚刚发生的是,尝试调用foo程序, 第一个参数是"=",第二个参数是 bar
如果要处理名字带有空格的文件,需要注意这一点。在处理字符串的时候需要小心
定义字符串有两种方法。你可以使用双引号定义字符串,也可以使用单引号定义字符串
对于文字字符串,单引号和双引号等效,但是其余情况不一样
双引号中,变量会被代换为外壳程序中foo变量的值,而单引号不会如此
这就是引用变量的方式
一个重要的事情是你在shell中可以定义函数
我们可以访问我在此处定义的函数,例如视频示例中,当前目录下的 mcd.sh文件内容如下
在这里,如果我们调用mcd函数, 先会调用mkdir命令。在这里 $1像一个特殊变量。在其余编程语言中,可能是数组argv的第一个元素表示第一个变量,而在bash里面用$1表示。
注: mkdir ,如果在当前目录已经存在一个目录的名字与将要创建的目录名字相同, 会打印 cannot create directory。但是如果加上-p参数的话,如果已有同名目录, 并不会返回错误
在这里,我们会创建一个文件夹,然后用cd切换到该文件夹。
事实上,我们直接将这个文件的代码键入到shell中,它将起作用,并且定义此函数。但是有时候最好在文件中写入内容。
这样就可以在shell中执行脚本并加载它。虽然现在看起来好像什么都没发生,但是mcd函数已经在shell中定义了。我们然后可以执行 mcd test ,然后目录转换到了 /test 目录下
保留命令(reserved command)除了 $1 以外, 还有更多保留命令,比如 $0 是脚本的名字, $2到$9是第二个到第九个参数。
后面这些保留命令可以直接在shell中使用, 例如 $? 从上一条命令获取错误代码。 \$\_ 可以获得上一个命令的最后一个参数
另一个经常使用的命名是 !! ,效果如下所示
进程(process)可以通过不同的方式与其余进程和命令交流。交流方式除了标准输入和标准输出以外,还有标准错误流和错误码等。
因为echo "Hello" 没有出现任何问题,所以返回0。C语言通过返回0来表示一切正常,没有错误。
下面的示例中, grep 并没有正常运行
这是因为我们尝试搜索mcd.sh中的字符串,而字符串不存在,所以grep不打印任何东西,并且返回一个1 错误代码(error code, errno)。
有一些有趣的命令,例如命令true始终返回0 ,而false始终返回一个错误代码
可以使用逻辑运算符(logical operators)来实现条件语句
逻辑或运算符与逻辑与运算符具有短路特性
逻辑或运算符 || 会尝试执行第一个语句,如果第一个不起作用(即返回false),则执行第二个,否则不执行第二个
逻辑与运算符 && 会尝试执行第一个语句,如果第一个起作用,则第二个会被短路(short-circuited),不会被打印
还有一个是,在同一行可以使用分号命令
你可以将命令的输出转换为变量。这里所做的是,获取pwd命令的(标准输出流的)输出, 将其储存到foo变量中
更笼统的说, 我们可以做被称为命令替换(command substitution)的东西。
上面的示例只是将输出作为字符串扩展。
另一个鲜为人知的工具称为流程替换(process substitution)。
11:42
这个命令要做的是, 执行命令 ls ,将输出发送到一个,相当于临时文件的地方, shell 为ls和cat提供这个文件的句柄。第一个 ls 输出当前目录,输出结果放到这个临时文件中, 第二个 ls 输出父目录,将输出结果放到同一个临时文件。 cat 将两个输出结果串联在一起输出。
#下面是example.sh的文件内容
date命令仅显示当前日期。
$0 表示当前运行的脚本的名字
$# 表示提供的参数数量
\$\$ 此指令的pid
$@ 拓展到所有参数
for file in "$@" ,每次循环时, file 会按顺序表示 $@ 的每个参数。
在下一行, 我们运行 grep 命令,会在file文件中搜索 foobar。 如果我们关心程序的输出,我们可以将其重定向到某个文件, 以保存它或将其连接到其他文件。但这里我们可以实际上放弃输出,所以我们将输出重定向到 /dev/null 上。这个有点像unix中的特殊设备,无论写入多少到这里,都会被丢弃。
2> 这是用来重定向标准错误流(stderr)的。这两个流是分开的,你必须告诉bash如何处理每一个。
if 行,是用来检验grep的输出的。 -ne 是比较符,表示不相等。 其余编程语言使用"\=\=""!="等表示不相等。如果这个文件没有 foobar 内容,则在文件的末尾加入 \# foobar 内容。
在bash中,有保留的比较运算符,可以输入以下命令查看。
我们尝试运行上面的脚本
然后查看 mcd.sh 的内容
可以发现, mcd.sh 后面插入了 foobar 内容
在执行脚本的另一件事是, 如果我们想获得所有 .sh 脚本,可以使用 ls \*.sh 这样的指令
这样,我们可以得到 example.sh 和 mcd.sh
\* 星号可以扩展为一个或多个字符
? 问号只会扩展为一个字符
常用的方式是使用花括号
例如,我们想将一个.png格式的图片转为jpg格式
下面的一行会被展开为上面的一行。
再比如, 下面的第一行命令会被展开为第二行命令
再比如,
这里会像做笛卡尔乘积一样两次扩展, 会被扩展到6个文件
你也可以将星号和大括号括起来
这样会扩展到 foo/a foo/b 一直到foo/j, bar也是如此。
我们这里想比较 两个 ls 输入结果的差别
现在,我们只看到了bash版本, 很多时候, 对于有些脚本, bash可能不是最好的方法。你可以写脚本与很多实现的shell交互。比如下面的示例
首先导入库sys, 这里的sys.argv 有点类似于bash中得到的$0 $1 等,像参数的变量一样。
第一行的内容叫做 shebang , shell通过这个东西知道如何执行命令
例如上面, shell已经知道了如何执行脚本,所以不需要脚本的第一行。但是如果按照下面的内容执行的话,脚本的第一行就会起作用了。
如果机器可能将python安装到了上面示例以外的地方,而你想把同样的代码执行在同样的地方的话,可以使用env指令。这样就可以使用路径环境变量(path environment variable)搜索python的二进制文件,用于执行此脚本。
可以使用shellcheck工具来检查bash脚本的错误
在shell内定义变量,或在shell加载bash脚本,可能会有一些副作用。
下面的内容将讨论一些好用的工具,第一个是 man。 当你想知道某个标志(flag)或命令用途,比如下面的flag, -i 的用途时, 可以使用man来查看文档。
但是,使用man指令查看文档,有时可能会没有示例。有些文档可能过长或冗杂,例如指令convert和ffmpeg的文档
可以安装tldr工具来查看文档,这个工具的文档有很多示例。
下面介绍如何寻找文件。如果我们想查找所有名为src的文件, 可以使用find指令。 下面的示例,我们想找一个名为src,类型为目录(directory)的文件
查找有很多有用的标志(flag)。例如,-path可以匹配指定路径
你可以查看修改时间在指定时间内的文件,例如下面的示例会寻找当前目录下所有修改日期在一天内(24小时内)的文件
下面的命令会寻找所有格式为 .tmp 的文件,并删除。echo $? 返回0 ,表示find命令返回0, 即执行成功。
有一个较短的命令fd, 可以使用正则表达式并忽略gitfile。它们将使用颜色代码,具有更好的Unicode支持。
locate命令使用数据库来搜索文件。提前将目录存储在数据库中,执行locate命令时从数据库中搜索文件。
更新locate命令的数据库可以使用下面的命令
有时你可能更加关心文件的内容,这是可以使用grep命令。如果要再次递归搜索当前内容,可以使用前面提到的find --exec,也可以使用-R 标志。
你可以使用rg命令快速搜索。下面命令将会在 ~/scratch 目录下在 py 类型文件中搜索 import requests 内容。
也可以使用 ripgrep, 这个命令支持更精美的页面和Unicode。 下面的命令在上面的命令的基础上,支持显示代码内容10行上下文
下面的示例中, -u 表示不要忽略隐藏文件, --files-without-match表示打印所有不符合模式的内容, "^#\\!"是一个小的正则表达式, 表示在一行的开头,有一个\#和一个\!。前面的内容意思是说,搜索所有内容中不包括 \#\! 的内容。然后 -t sh 表示仅从 sh 文件搜索
我们可以加入 --stats 标志,这样指令会在最后输出关于此次运行的统计信息。
ack 命令是原有的 grep 命令的替代品,ag也是如此。这些命令几乎可以互换
你可以使用键盘上的上箭头慢慢浏览之前执行过的指令。bash提供了history命令,可以打印你的历史记录
history -l 可以打印所有内容
如果想获得有关于执行 convert 命令的历史记录,可以执行以下命令
几乎所有的shell都会提供ctrl+R来做向后搜索
fsf命令可以交互式查找输入内容
zsh可以动态搜索之前执行过的命令的历史记录。
可以使用 ls -R 来递归列出某些目录结构
tree命令可以更加友好的方式来显示目录结构
~/m/tools >>> tree
同样是显示目录结构,有一个非常好用的工具broot
nnn 命令也非常好用,可以使用箭头导航快速浏览