文章目录
- shell 脚本
- 赋值语法
- 函数
- 逻辑运算符
- 命令替换
- 进程替换
- 通配
- shell工具
- 查看命令如何使用
- 查找文件
- 查找代码
- 查找shell指令
shell 脚本
很多情况下需要执行一系列的操作并使用条件或循环这样的控制流。
大多数shell都有自己的一套脚本语言,包括变量、控制流和自己的语法。
shell脚本与其他脚本语言不同之处在于,shell 脚本针对 shell 所从事的相关工作进行来优化。因此,创建命令流程(pipelines)、将结果保存到文件、从标准输入中读取输入,这些都是 shell 脚本中的原生操作,这让它比通用的脚本语言更易用。
bash脚本
赋值语法
foo=bar
root1@test:~$ foo=bar
root1@test:~$ echo $foo
bar
root1@test:~$ echo "$foo"
bar
root1@test:~$ echo '$foo'
$foo
函数
root1@test:~$ vim mcd.sh
$1
是脚本的第一个参数,bash使用了很多特殊的变量来表示参数、错误代码和相关变量
$0
脚本名
$1
到$9
脚本的参数。$1
是第一个参数,依此类推。
$@
所有参数
$#
参数个数
$?
前一个命令的返回值
$$
当前脚本的进程识别码
!!
完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用 sudo !!再尝试一次。
$_
上一条命令的最后一个参数。如果你正在使用的是交互式 shell,你可以通过按下Esc
之后键入.
来获取这个值。
root1@test:~$ echo "hello"
hello
root1@test:~$ echo $_
hello
root1@test:~$ echo "hello"
hello
root1@test:~$ !!
echo "hello"
hello
root1@test:~$ echo "hello"
hello
root1@test:~$ echo $?
0
#0表示正确执行
命令通常使用 STDOUT来返回输出值,使用STDERR 来返回错误及错误码,便于脚本以更加友好的方式报告错误。
root1@test:~$ source mcd.sh
root1@test:~$ mcd test
root1@test:~/test$
source跟./xxx.sh或sh xxx.sh有什么不同呢?
最大的不同就是前者在文件中设置的变量对当前shell是可见的,而后者设置的变量对当前shell是不可见的。要知道./xxx.sh和sh xxx.sh都是在当前shell的子shell中执行的,子shell中的变量不会影响父shell,而source是把文件中的命令都读出来一个个执行,所有的变量其实就是在父shell中设置的。
[引自http://suntus.github.io/2015/11/27/source%E5%91%BD%E4%BB%A4/
root1@test:~$ sh mcd.sh
root1@test:~$ mcd test
root1@test:~/test$
逻辑运算符
root1@test:~$ false || echo "Oops, fail"
Oops, fail
root1@test:~$ true || echo "Oops, fail"
root1@test:~$ true && echo "Oops, fail"
Oops, fail
root1@test:~$ false && echo "Oops, fail"
root1@test:~$ false ; echo "Oops, fail"
Oops, fail
命令替换
通过$( CMD )
这样的方式来执行CMD
这个命令时,它的输出结果会替换掉 $( CMD )
root1@test:~$ echo "start at $(date)"
start at Sat Jul 8 11:43:48 UTC 2023
进程替换
<( CMD )
会执行 CMD
并将结果输出到一个临时文件中,并将 <( CMD )
替换成临时文件名。这在希望返回值通过文件而不是STDIN传递时很有用。例如, diff <(ls foo) <(ls bar) 会显示文件夹 foo 和 bar 中文件的区别。
root1@test:~$ cat <(ls) <(ls ..)
a.txt
hello.txt
hello2.txt
last-modeified.txt
mcd.sh
semester
test
root1
root1@test:~$ ls ..
root1
root1@test:~$ ls
a.txt hello2.txt mcd.sh test
hello.txt last-modeified.txt semester
通配
当执行脚本时,经常需要提供形式类似的参数。bash可以轻松的实现这一操作,它可以基于文件扩展名展开表达式。
通配符
可以使用?
或*
来匹配一个或任意个字符
例如,对于文件foo, foo1, foo2, foo10 和 bar, rm foo?这条命令会删除foo1 和 foo2 ,而rm foo* 则会删除除了bar之外的所有文件。
root1@test:~$ ls
bar.md bar.txt foo.md foo.txt mcd.sh semester
root1@test:~$ ls *.md
bar.md foo.md
root1@test:~$ ls foo.?
ls: cannot access 'foo.?': No such file or directory
root1@test:~$ ls foo.??
foo.md
花括号{}
当有一系列的指令,其中包含一段公共子串时,可以 用花括号来自动展开这些命令。
root1@test:~$ ls
mcd.sh semester
root1@test:~$ touch {foo,bar}.{txt,md}
root1@test:~$ ls
bar.md bar.txt foo.md foo.txt mcd.sh semester
shell工具
查看命令如何使用
tldr
https://tldr.sh/
https://tldr.inbrowser.app/pages.zh/common/
查找文件
一般:
find
root1@test:~$ find . -name '*.md' -exec rm {} \;
root1@test:~$ ls
bar.txt foo.txt mcd.sh semester
更高效的方法:
locate
https://man7.org/linux/man-pages/man1/locate.1.html
查找代码
一个最常见的场景是希望查找具有某种模式的全部文件,并找它们的位置。
为了实现这一点,很多类UNIX的系统都提供了grep
命令,它是用于对输入文本进行匹配的通用工具。
-C
:获取查找结果的上下文(Context);
grep -C 5 会输出匹配结果前后五行
-v
将对结果进行反选(Invert),也就是输出不匹配的结果。
当需要搜索大量文件的时候,使用 -R
会递归地进入子目录并搜索所有的文本文件。
有很多办法可以对 grep -R 进行改进,例如使其忽略.git 文件夹,使用多CPU等等。因此也出现了很多它的替代品,包括 ack, ag 和 rg。
ripgrep (rg) ,速度快,而且用法非常符合直觉。
查找shell指令
history
命令允许以程序员的方式来访问shell中输入的历史命令。
如果要搜索历史记录,则可以利用管道将输出结果传递给 grep
进行模式搜索。 history | grep find
会打印包含find子串的命令。
对于大多数的shell来说,可以使用 Ctrl+R
对命令历史记录进行回溯搜索。敲 Ctrl+R
后可以输入子串来进行匹配,查找历史命令行。
Ctrl+R
可以配合 fzf 使用。fzf 是一个通用对模糊查找工具,它可以和很多命令一起使用。