文章目录
一、bash 中的特殊变量
$0 :脚本名
$1 -$9 :脚本的第1-9个参数
$@ :脚本的所有参数
$# :参数个数
$? :前一条命令的返回值
$_ :前一条命令的最后一个参数
!! :完整的前一条命令 ;常见应用:应权限不足而执行命令失败时,可用 sudo !!
$$ : 当前脚本的进程识别码
完整变量列表
二、变量赋值与访问
普通赋值: foo=bar (注意不能有空格,空格默认用来分隔命令与命令参数)
访问: $foo 访问foo的值
返回码可为变量赋值 ,
如:foo=$(pwd) ;
三、其他类型的脚本
脚本并不一定只有用 bash 写才能在终端里调用。比如说,这是一段 Python 脚本,作用是将输入的参数倒序输出:
#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
print(arg)
内核知道去用 python 解释器而不是 shell 命令来运行这段脚本,是因为脚本的开头第一行的 shebang
也可以使用env命令: #!/usr/bin/env python 更通配(两种方式的不同)env介绍
shell函数和脚本有如下一些不同点:
函数只能与shell使用相同的语言,脚本可以使用任意语言。因此在脚本中包含 shebang 是很重要的。
函数仅在定义时被加载,脚本会在每次被执行时加载。这让函数的加载比脚本略快一些,但每次修改函数定义,都要重新加载一次。
函数会在当前的shell环境中执行,脚本会在单独的进程中执行。因此,函数可以对环境变量进行更改,比如改变当前工作目录,脚本则不行。脚本需要使用 export 将环境变量导出,并将值传递给环境变量。
与其他程序语言一样,函数可以提高代码模块性、代码复用性并创建清晰性的结构。shell脚本中往往也会包含它们自己的函数定义。
四、查找文件
find
所有的类UNIX系统都包含一个名为 find 的工具,它是 shell 上用于查找文件的绝佳工具。find命令会递归地搜索符合条件的文件,例如:
(如果您希望模式匹配时是不区分大小写,可以使用-iname选项)
除了列出所寻找的文件之外,find 还能对所有查找到的文件进行操作。这能极大地简化一些单调的任务。
# 删除全部扩展名为.tmp 的文件
find . -name '*.tmp' -exec rm {} \;
# 查找全部的 PNG 文件并将其转换为 JPG
find . -name '*.png' -exec convert {} {}.jpg \;
尽管 find 用途广泛,它的语法却比较难以记忆。fd 就是一个更简单、更快速、更友好的程序,它可以用来作为find的替代品。它有很多不错的默认设置,例如输出着色、默认支持正则匹配、支持unicode并且我认为它的语法更符合直觉。以模式PATTERN 搜索的语法是 fd PATTERN。
locate
locate 使用一个由 updatedb负责更新的数据库,在大多数系统中 updatedb 都会通过 cron 每日更新。这便需要我们在速度和时效性之间作出权衡。而且,find 和类似的工具可以通过别的属性比如文件大小、修改时间或是权限来查找文件,locate则只能通过文件名。 这里有一个更详细的对比。
手动更新: updatedb
查找内容
grep
grep 有很多选项,这也使它成为一个非常全能的工具。其中经常使用的有
-C :获取查找结果的上下文(Context);
-v 将对结果进行反选(Invert),也就是输出不匹配的结果。
举例来说, grep -C 5 会输出匹配结果前后五行。
当需要搜索大量文件的时候,使用 -R 会递归地进入子目录并搜索所有的文本文件。
但是,我们有很多办法可以对 grep -R 进行改进,例如使其忽略.git 文件夹,使用多CPU等等。
因此也出现了很多它的替代品,包括 ack, ag 和 rg。它们都特别好用,但是功能也都差不多,我比较常用的是 ripgrep (rg) ,因为它速度快,而且用法非常符合直觉。例子如下:
# 查找所有使用了 requests 库的文件
rg -t py 'import requests'
# 查找所有没有写 shebang 的文件(包含隐藏文件)
## -u:不忽略隐藏文件 “^#!” 正则表达式 -t sh :只查找.sh文件
rg -u --files-without-match "^#\!" -t sh
# 查找所有的foo字符串,并打印其之后的5行
rg foo -A 5
# 打印匹配的统计信息(匹配的行和文件的数量)
rg --stats PATTERN
与 find/fd 一样,重要的是你要知道有些问题使用合适的工具就会迎刃而解,而具体选择哪个工具则不是那么重要。
查找 shell 命令
目前为止,我们已经学习了如何查找文件和代码,但随着你使用shell的时间越来越久,您可能想要找到之前输入过的某条命令。首先,按向上的方向键会显示你使用过的上一条命令,继续按上键则会遍历整个历史记录。
history 命令允许您以程序员的方式来访问shell中输入的历史命令。这个命令会在标准输出中打印shell中的里面命令。如果我们要搜索历史记录,则可以利用管道将输出结果传递给 grep 进行模式搜索。
history 1 打印从开始到此刻输入的所有命令
history | grep find 会打印包含find子串的命令。
对于大多数的shell来说,您可以使用 Ctrl+R 对命令历史记录进行回溯搜索。敲 Ctrl+R 后您可以输入子串来进行匹配,查找历史命令行。
反复按下就会在所有搜索结果中循环。
Ctrl+R 可以配合 fzf 使用。fzf 是一个通用对模糊查找工具,它可以和很多命令一起使用。这里我们可以对历史命令进行模糊查找并将结果以赏心悦目的格式输出。
另外一个和历史命令相关的技巧我喜欢称之为基于历史的自动补全。 这一特性最初是由 fish shell 创建的,它可以根据您最近使用过的开头相同的命令,动态地对当前对shell命令进行补全
你可以修改 shell history 的行为,例如,如果在命令的开头加上一个空格,它就不会被加进shell记录中。当你输入包含密码或是其他敏感信息的命令时会用到这一特性。 为此你需要在.bashrc中添加HISTCONTROL=ignorespace或者向.zshrc 添加 setopt HIST_IGNORE_SPACE。 如果你不小心忘了在前面加空格,可以通过编辑.bash_history或 .zhistory 来手动地从历史记录中移除那一项。
tree broot
二者均可以直观的形式打印多级目录(ls -R 不够形象)(broot功能更多)
五、零碎知识点
- 同一行 用 ; 分隔不同命令
- && || 的短路效果
- 返回码可于&& || 搭配使用,如: false || echo “”
- 返回码可为变量赋值 ,如:foo=$(pwd)
- 命令替换
- 进程替换
<(命令) :将命令的输出写入临时文件
- vim
- grep :在文件中捕捉特定字符串(print lines that match patterns)
- 通配符:
六、 shell工具
shellcheck :帮助你快速找到脚本中的错误
TLDR pages
有时候手册内容太过详实,让我们难以在其中查找哪些最常用的标记和语法。TLDR pages是一个很不错的替代品,它提供了一些案例,可以帮助您快速找到正确的选项。