读书笔记-Bash(网道(WangDoc.com)教程)
学习资料来自于 https://wangdoc.com/bash/grammar.html
下列按原章节名称记录
bash启动conda的时候需要
source /Users/admin/anaconda3/etc/profile.d/conda.sh
conda activate test
不然conda activate会报错
1 简介
2 基本语法
- echo -n 取消末尾的回车 -e 会解释\n 这种,否则直接打印
- ls -是短参数 --是长参数
3 模式拓展
另一个需要注意的地方是,大括号内部的逗号前后不能有空格。否则,大括号扩展会失效。
略读, 主要是一些正则和小技巧,难记
$ echo {a..c}{1..3}
a1 a2 a3 b1 b2 b3 c1 c2 c3
4 引号和转义
- 单引号用于保留字符的字面含义,各种特殊字符在单引号里面,都会变为普通字符,比如星号(*)、美元符号($)、反斜杠(\)等。
- 双引号: 三个特殊字符除外:美元符号($)、反引号(`)和反斜杠(\)。这三个字符在双引号之中,依然有特殊含义,会被 Bash 自动扩展。
- 双引号的另一个常见的使用场合是,文件名包含空格。这时就必须使用双引号(或单引号),将文件名放在里面。
- 双引号会原样保存多余的空格。
- 双引号还有一个作用,就是保存原始命令的输出格式。如
echo "$(cal)"
5 变量
env命令或printenv命令,可以显示所有环境变量。
set命令可以显示所有变量(包括环境变量和自定义变量),以及所有的 Bash 函数。
unset命令用来删除一个变量。
export命令用来向子 Shell 输出变量。export NAME=value
上面命令执行后,当前 Shell 及随后新建的子 Shell,都可以读取变量$NAME。
- 等号左边是变量名,右边是变量句号。等号两边不能有空格
- 事实上,读取变量的语法
$foo
,可以看作是${foo}的简写形式。 - 如果变量的值本身也是变量,可以使用${!varname}的语法,读取最终的值。
- 特殊变量:
(1)$?
为上一个命令的退出码,用来判断上一个命令是否执行成功。返回值是0,表示上一个命令执行成功;如果是非零,上一个命令执行失败。
(2)$$
为当前 Shell 的进程 ID。
(3)$_
为上一个命令的最后一个参数。
(4)$!
为最近一个后台执行的异步命令的进程 ID。
(5)$0
为当前 Shell 的名称(在命令行直接执行时)或者脚本名(在脚本中执行时)。
(6)$-
为当前 Shell 的启动参数。
(7)$#
表示脚本的参数数量,$@
表示脚本的参数值,参见脚本一章。
变量的默认值
${varname:-word}
${varname:=word}
${varname:+word}
${varname:?message}
declare 命令: declare命令可以声明一些特殊类型的变量,为变量设置一些限制,比如声明只读类型的变量和整数类型的变量。
readonly 命令: readonly命令等同于declare -r,用来声明只读变量,不能改变变量值,也不能unset变量。
let 命令:let命令声明变量时,可以直接执行算术表达式。
6 字符串操作
字符串的长度 ${#varname}
子字符串 ${varname:offset:length} 如果省略length,则从位置offset开始,一直返回到字符串的结尾。 可负数语法
搜索和替换
(1)字符串头部的模式匹配。
# 如果 pattern 匹配变量 variable 的开头,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable#pattern}
# 如果 pattern 匹配变量 variable 的开头,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable##pattern}
--
如果要将头部匹配的部分,替换成其他内容,采用下面的写法。
# 模式必须出现在字符串的开头
${variable/#pattern/string}
# 示例
$ foo=JPG.JPG
$ echo ${foo/#JPG/jpg}
jpg.JPG
(2)字符串尾部的模式匹配。
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable%pattern}
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable%%pattern}
如果要将尾部匹配的部分,替换成其他内容,采用下面的写法。
# 模式必须出现在字符串的结尾
${variable/%pattern/string}
# 示例
$ foo=JPG.JPG
$ echo ${foo/%JPG/jpg}
JPG.jpg
(3)任意位置的模式匹配。
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,但仅替换第一个匹配
${variable/pattern/string}
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,所有匹配都替换
${variable//pattern/string}
前面提到过,这个语法还有两种扩展形式。
# 模式必须出现在字符串的开头
${variable/#pattern/string}
# 模式必须出现在字符串的结尾
${variable/%pattern/string}
改变大小写
# 转为大写
${varname^^}
# 转为小写
${varname,,}
8 Bash 的算术运算
((...))语法可以进行整数的算术运算。会自动忽略内部的空格,只要算术结果不是0,命令就算执行成功。支持加减乘 整除 余数 自增自减以及指数(**)
$((...))的圆括号之中,不需要在变量名之前加上$,不过加上也不报错。使用不存在的变量,会当作0处理。
最后,$[...]是以前的语法,也可以做整数运算,不建议使用。
算术表达式$((...))可以执行赋值运算。
- expr命令支持算术运算,可以不使用((...))语法。
- let命令用于将算术运算的结果,赋予一个变量。 这个式子里面不能有空格
9 Bash 行操作
!e表示找出操作历史之中,最近的那一条以e开头的命令并执行。 (!string语法只会匹配命令,不会匹配参数。所以!echo H不会执行echo Hello World,而是会执行echo Goodbye,并把参数H附加在这条命令之后。同理,!echo H G也是等同于echo Goodbye命令之后附加H G)
由于!string语法会扩展成以前执行过的命令,所以含有!的字符串放在双引号里面,必须非常小心,如果它后面有非空格的字符,就很有可能报错。
$ echo "I say:\"hello!\""
bash: !\: event not found
上面的命令会报错,原因是感叹号后面是一个反斜杠,Bash 会尝试寻找,以前是否执行过反斜杠开头的命令,一旦找不到就会报错。解决方法就是在感叹号前面,也加上反斜杠。
$ echo "I say:\"hello\!\""
I say:"hello\!"
* !$:代表上一个命令的最后一个参数。
* !*:代表上一个命令的所有参数,即除了命令以外的所有部分。
* ^string1^string2:执行最近一条包含string1的命令,将其替换成string2。
9 堆栈目录
- cd - 可以返回上一次的目录
- pushd,popd 记忆多级目录 但是好像是相对路径奇怪, 别用
- dirs 命令 显示堆栈
10 脚本入门
- Shebang 行: #!balabala 写上的话, 可以直接./sh文件, 不然就要
/bin/sh ./script.sh
这样子执行 - env 命令
- shift 命令
- getopts 命令
- 配置项参数终止符 --
- source命令最大的特点是在当前 Shell 执行脚本,不像直接执行脚本时,会新建一个子 Shell。所以,source命令执行脚本时,不需要export变量。
11 read命令
- 如果read命令之后没有定义变量名,那么环境变量REPLY会包含所有的输入。
- read命令除了读取键盘输入,可以用来读取文件。
#!/bin/bash
filename='/etc/hosts'
while read myline
do
echo "$myline"
done < $filename
上面的例子通过read命令,读取一个文件的内容。done命令后面的定向符<,将文件内容导向read命令,每次读取一行,存入变量myline,直到文件读取完毕。
- 参数:
-t参数,设置了超时的秒数
-p参数指定用户输入的提示信息。
-a参数把用户的输入赋值给一个数组,从零号位置开始。
-n参数指定只读取若干个字符作为变量值,而不是整行读取。 - read命令读取的值,默认是以空格分隔。可以通过自定义环境变量IFS(内部字段分隔符,Internal Field Separator 的缩写),修改分隔标志。
12 条件判断
if结构的判断条件,一般使用test命令,有三种形式。
# 写法一
test expression
# 写法二
[ expression ]
# 写法三
[[ expression ]]
上面三种形式是等价的,但是第三种形式还支持正则判断,前两种不支持。
注意,第二种和第三种写法,[和]与内部的表达式之间必须有空格。 实际上,[这个字符是test命令的一种简写形式,可以看作是一个独立的命令,这解释了为什么它后面必须有空格。
- 注意,test命令内部的>和<,必须用引号引起来(或者是用反斜杠转义)。否则,它们会被 shell 解释为重定向操作符。
[[ string1 =~ regex ]]
正则表达式- [ -n string ]:如果字符串string的长度大于零,则判断为真。
- [ -z string ]:如果字符串string的长度为零,则判断为真。
- AND运算:符号&&,也可使用参数-a。 (没有AND吧)
- OR运算:符号||,也可使用参数-o。
- NOT运算:符号!。
case结构
case结构用于多值判断,可以为每个值指定对应的命令,跟包含多个elif的if结构等价,但是语义更好。它的语法如下。
case expression in
pattern )
commands ;;
pattern )
commands ;;
...
esac
13 循环
- while 循环
while condition; do
commands
done
until 循环与while相反
- for
for variable in list; do
commands
done
in list的部分可以省略,这时list默认等于脚本的所有参数$@。但是,为了可读性,最好还是不要省略
for循环还支持 C 语言的循环语法。
for (( expression1; expression2; expression3 )); do
commands
done
14 函数
# 第一种 不能省略小括号()
fn() {
# codes
}
# 第二种 小括号可选
function fn() {
# codes
}
15 数组
- 创建ARRAY[INDEX]=value 或者 ARRAY=(value1 value2 ... valueN)
$ echo ${array[i]} # i 是索引
@和*是数组的特殊索引,表示返回数组的所有成员。
一般把${activities[@]}放在双引号之中。 ${activities[*]}不放在双引号之中,跟${activities[@]}不放在双引号之中是一样的。 ${activities[*]}放在双引号之中,所有成员就会变成单个字符串返回。
- 数组的长度
${#array[*]} ${#array[@]}
- 提取数组序号
${!array[@]}或${!array[*]},可以返回数组的成员序号,即哪些位置是有值的。
- 提取数组成员
${array[@]:position:length}的语法可以提取数组成员。
- 数组末尾追加成员,可以使用+=赋值运算符
16 set 命令,shopt 命令
种一棵树最好的时间是十年前,其次是现在。