关键词:
写在前面:本篇内容来自于 MIT 推出的课程:计算机教育中缺失的一课,这门课程介绍了命令行、强大的文本编辑器的使用、使用版本控制系统提供的多种特性等等。中文课程主页:https://missing-semester-cn.github.io/。
本篇为学习第二节课所做的笔记,主题是 Shell 工具和脚本,在这节课中,介绍了 bash 作为脚本语言的一些基础操作,以及几种最常用的shell工具。
- 变量赋值:
foo=bar
,注意中间不能添加空格 - Bash中的字符串通过
'
和"
分隔符来定义,但是它们的含义并不相同。以'
定义的字符串为原义字符串,其中的变量不会被转义,而"
定义的字符串会将变量值进行替换。 - 函数编写:使用
"$1"
获取变量值
mcd () {
mkdir -p "$1"
cd "$1"
}
bash中的特殊变量
$0
- 脚本名$1
到$9
- 脚本的参数。$1
是第一个参数,依此类推。$@
- 所有参数$#
- 参数个数$?
- 前一个命令的返回值$$
- 当前脚本的进程识别码!!
- 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用sudo !!
再尝试一次。$_
- 上一条命令的最后一个参数。如果你正在使用的是交互式shell,你可以通过按下Esc
之后键入 . 来获取这个值。
- 特殊变量可以搭配短路运算符,来中断程序的运行
- 命令替换:以变量的形式获取一个命令的输出,当通过
$( CMD )
这样的方式来执行CMD
这个命令时,它的输出结果会替换掉$( CMD )
。例如,如果执行for file in $(ls)
,shell首先将调用ls
,然后遍历得到的这些返回值。 - 进程替换:还有一个冷门的类似特性是 进程替换(process substitution),
<(CMD)
会执行 CMD 并将结果输出到一个临时文件中,并将<( CMD )
替换成临时文件名。这在我们希望返回值通过文件而不是STDIN传递时很有用。例如,diff <(ls foo) <(ls bar)
会显示文件夹foo
和bar
中文件的区别。 - 程序示例,变量为文件名,如果文件名中包含“foobar”,不进行操作,否则加入”foobar“
#!/bin/bash
echo "Starting program at $(date)" # date会被替换成日期和时间
echo "Running program $0 with $# arguments with pid $$"
for file in "$@"; do
grep foobar "$file" > /dev/null 2> /dev/null
# 如果模式没有找到,则grep退出状态为 1
# 我们将标准输出流和标准错误流重定向到Null,因为我们并不关心这些信息
if [[ $? -ne 0 ]]; then
# 这里的空格不能少
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
批量处理文件时非常有用:bash 通配(globbing)。
- 通配符 - 当你想要利用通配符进行匹配时,可以分别使用
?
和*
来匹配一个或任意个字符。 - 花括号
{}
- 当你有一系列的指令,其中包含一段公共子串时,可以用花括号来自动展开这些命令。这在批量移动或转换文件时非常方便。
- 通配符 - 当你想要利用通配符进行匹配时,可以分别使用
convert image.{png,jpg}
# 会展开为
convert image.png image.jpg
cp /path/to/project/{foo,bar,baz}.sh /newpath
# 会展开为
cp /path/to/project/foo.sh /path/to/project/bar.sh /path/to/project/baz.sh /newpath
# 也可以结合通配使用
mv *{.py,.sh} folder
# 会移动所有 *.py 和 *.sh 文件
mkdir foo bar
# 下面命令会创建foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/h这些文件
touch {foo,bar}/{a..h}
touch foo/x bar/y
# 比较文件夹 foo 和 bar 中包含文件的不同
diff <(ls foo) <(ls bar)
# 输出
# < x
# ---
# > y
- 检查shell脚本中的错误工具:shellcheck,有程序,也有插件
- 查找帮助:
man
程序,但是输出太长,可以使用tldr
程序,输出几个例子(非常有用!) - 查找文件:
find
和fd
,fd
默认支持使用正则查找,更加符合直觉。locate
使用数据库的方式更加快速的搜索,但是缺陷是只能通过文件名来查找。 - 查找代码:
grep
有很多选项,这也使它成为一个非常全能的工具。其中我经常使用的有-C
:获取查找结果的上下文(Context);-v
将对结果进行反选(Invert),也就是输出不匹配的结果。举例来说,grep -C 5
会输出匹配结果前后五行。当需要搜索大量文件的时候,使用-R
会递归地进入子目录并搜索所有的文本文件。但是,我们有很多办法可以对grep -R
进行改进,例如使其忽略.git
文件夹,使用多CPU等等。因此出现了很多替代品:比较常用的是 ripgrep (rg) ,因为它速度快,而且用法非常符合直觉。例子如下:
# 查找所有使用了 requests 库的文件
rg -t py 'import requests'
# 查找所有没有写 shebang 的文件(包含隐藏文件)
rg -u --files-without-match "^#!"
# 查找所有的foo字符串,并打印其之后的5行
rg foo -A 5
# 打印匹配的统计信息(匹配的行和文件的数量)
rg --stats PATTERN
查找命令
- history 访问shell中输入的历史命令
- 对于大多数的shell来说,可以使用
Ctrl+R
对命令历史记录进行回溯搜索。敲Ctrl+R
后可以输入子串来进行匹配,查找历史命令行。反复按下就会在所有搜索结果中循环。在 zsh中,使用方向键上或下也可以完成这项工作。
导航
- ranger
- autojump,命令 j
习题部分
ls
命令- 显示所有文件:
ls -a
- 以可以理解的格式输出:
ls -lh
- 以最近访问顺序排序:
ls -tl
- 显示所有文件:
- 编写两个bash函数: marco 和 polo 执行下面的操作。 每当你执行 marco 时,当前的工作目录应当以某种形式保存,当执行 polo 时,无论现在处在什么目录下,都应当 cd 回到当时执行 marco 的目录。 为了方便debug,你可以把代码写在单独的文件 marco.sh 中,并通过 source marco.sh命令,(重新)加载函数。
macro () {
current="$(pwd)"
echo "$current saved to cache"
}
polo () {
cd "$current"
echo "jump to $current"
}
- 假设您有一个命令,它很少出错。因此为了在出错时能够对其进行调试,需要花费大量的时间重现错误并捕获输出。 编写一段bash脚本,运行如下的脚本直到它出错,将它的标准输出和标准错误流记录到文件,并在最后输出所有内容。 加分项:报告脚本在失败前共运行了多少次。
#!/usr/bin/env bash
count=0
while true; do
n=$((RANDOM % 100))
if [[ n -eq 42 ]]; then
echo "Something went wrong"
echo >&2 "The error was using magic numbers"
echo "Program successfully ran $count times\n"
exit 1
fi
((count += 1))
echo "ran successfully"
done
echo $count
echo "Everything went according to plan"
执行: sh [capture.sh](http://capture.sh/) 1> output 2> error; cat output; cat error
- 您的任务是编写一个命令,它可以递归地查找文件夹中所有的HTML文件,并将它们压缩成zip文件。注意,即使文件名中包含空格,您的命令也应该能够正确执行(提示:查看 xargs的参数-d,译注:MacOS 上的 xargs没有-d,查看这个issue)。如果您使用的是 MacOS,请注意默认的 BSD find 与GNU coreutils 中的是不一样的。你可以为find添加-print0选项,并为 xargs 添加 -0 选项。作为 Mac 用户,您需要注意 mac 系统自带的命令行工具和 GNU 中对应的工具是有区别的;如果你想使用 GNU 版本的工具,也可以使用 brew 来安装。
find *.html | xargs -d zip compressed.zip
- (进阶) 编写一个命令或脚本递归的查找文件夹中最近使用的文件。更通用的做法,你可以按照最近的使用时间列出文件吗?
shell脚本攻略(学习笔记)--1.2echo和printf打印输出
1.2.1echo的引号问题关于echo的用法,注意点就是单引号和双引号的特殊情况。以打印“HelloWorld!”为例。[[email protected]tmp]#echoHelloWorld!HelloWorld![[email protected]tmp]#echo‘HelloWorld!‘HelloWorld![[email prot 查看详情
shell脚本攻略(学习笔记)--1.5进行数学运算
使用let、(())或[]进行基本的整数运算,使用expr和bc进行高级的运算,包括小数运算。[[email protected]tmp]#str=10[[email protected]tmp]#letstr=str+6[[email protected]tmp]#letstr-=5[[email protected]tmp]#echo$str11还可以自 查看详情
shell脚本攻略(学习笔记)--1.7数组
bash里有两种数组:普通数组和关联数组。普通数组只能使用整型数值作为数组索引,关联数组可以使用字符串作为索引。1.7.1普通数组定义数组的方式一:[[email protected]tmp]#array_test=(1234)它们分别存储在索引位0-3的位置上,是... 查看详情
shell脚本攻略(学习笔记)--1.7expr命令全解
expr命令可以实现数值运算、数值或字符串比较、字符串匹配、字符串提取、字符串长度计算等功能。它还具有几个特殊功能,判断变量或参数是否为整数、是否为空、是否为0等。先看expr命令的info文档infocoreutils‘exprinvocation‘... 查看详情
shell脚本攻略(学习笔记)--2.5tr
tr主要用于映射结果集、压缩和删除字符。我个人感觉特别有用,特别是压缩连续空格(空行)为一个空格(空行),让不规则的信息变得规则。2.5.1tr映射tr[options][SET1][SET2]如果同时指定了SET1和SET2,则实现的是将SET1的符号按位... 查看详情
shell脚本学习笔记12-正则表达式
一、正则表达式介绍 正则表达式(RegularExpression、regex或regxp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符。 支持正则表达式的程序如:locate|find|vim|grep|sed|awk 二... 查看详情
shell脚本学习笔记八:流程控制
一、ifelse ///如果else分支没有语句执行,就不要写这个else 1、if语句 ifcondition then command1 &nb 查看详情
shell脚本——学习笔记(包含应用案例)(代码片段)
题头为本人编写shell脚本格式^v^#!/bin/bash#****************#Author:Pakho#Date:2021-00-00#FileName:.sh#****************循环Expect命令我们通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干... 查看详情
shell学习---shell脚本的静态检查工具shellcheck(代码片段)
Shell脚本的静态检查工具shellcheckubuntu下aptinstallshellcheck,即可安装shellcheck.写完shell脚本,记得用它检查一下,能给你点建议的.要检查现有项目的所有的脚本,findyour_project_folder-name"*.sh"|xargs-ishellcheck即可实现批量检查 查看详情
shell脚本攻略(学习笔记)--1.8别名
ll就是ls-l的别名。可以自定义别名来代替某些命令配合某些选项,也可以定义别名组合多个命令。例如定义rm的别名rmb使其删除某内容时自动备份到目录中去。[[email protected]tmp]#aliasrmb=‘cp-a[email protected]/tmp/backup/&&rm-... 查看详情
python学习笔记2--shell脚本传参数到python脚本中(代码片段)
...f1a;ubuntu20.04python3vscode背景:想一键串联多个分析数据脚本,但是面临一个路径传入问题。奈何找了很多shell传参数教程都没有奏效。1、set方式1.1材料准备1.pyimportsysprint("脚本1start")print(sys.argv[1])print(sys.argv[2])print("... 查看详情
shell脚本攻略(学习笔记)--1.12read基础
要与Linux交互,脚本获取键盘输入的结果是必不可少的,read可以读取键盘输入的字符。1.限制输入字符例如,输入了5个字符后就结束,将输入的5个字符存入变量。换句话说,依靠输入的字符数来作为结束标志。read-nnumvar:将输... 查看详情
shell脚本攻略(学习笔记)--1.4变量(基础)
假设变量str,设置或修改变量属性时,不带$号,只有引用变量时才使用$号。也就是说变量是str,而不是$str。$只是操作变量时的一种符号形式,还有比如${}、${#}、$(())等符号形式,它们分别代表不同的意思。这一点很容易搞错... 查看详情
gnuld脚本学习笔记
...ld属于binutils软件包。但是嵌入式开发时,下载的linaroGCC工具集中是包含arm-linux-gnueabihf-ld的。工作中我经常使用ARM的scatter文件,和这个LD脚本差不多,只不过scatter文件的功能要弱不少,这也是为什么ARM6中armclang也是推荐使用GNULD... 查看详情
shell脚本攻略(学习笔记)--1.3多命令逻辑执行顺序
Linux中可以使用分号“;”、双and号“&&”和双竖线“||”来连接多个命令。1.3.1分号;当多个命令想在写在一行上同时执行,可以在每个命令后使用分号“;”。多个命令之间没有任何逻辑关系,所有写... 查看详情
jmeter学习笔记——参数化
...p;一、准备脚本,测试数据1、录制一个脚本(可以用badboy工具录制),在jmeter中打开 查看详情
学习笔记之shell的文本处理工具(代码片段)
1.比较文件的不同diff,comm,cmp首先创建两个文件。内容如下<spanstyle="font-size:18px;">[root@wwwPractice]#catExample1abcdef[root@wwwPractice]#catExample2abcdefghi</span>1.diff比较 查看详情
svn学习笔记三——钩子的应用
钩子脚本的具体写法就是操作系统中shell脚本程序的写法,请根据自己SVN所在的操作系统和shell程序进行相应的写作所谓钩子就是与一些版本库事件触发的程序,例如新修订版本的创建,或是未版本化属性的修改。每个钩子都会被... 查看详情