Shell 学习笔记

231 阅读7分钟

本篇笔记基于 Bourne Again Shell (bash)。参考文章:(link).

1.启动与退出

bash   # 启动 bash
exit   # 退出 bash

2. echo

echo xx  # 原样输出文本参数  XX

# 原样输出多行文本,单引号也可
echo "line1  
line2
...
lineN"

 默认情况下,echo 输出的文本末尾会有一个换行符。 -n 参数可以取消末尾的换行符。

echo a; echo b     # 输出为两行,一行为 a,另一行为 b
echo -n a; echo b  # 输出为 ab

 默认情况下,引号中的特殊字符会被作为普通字符输出, -e 参数会解释特殊字符。

echo "Hello\nWorld"     # 输出为一行字符
echo -e "Hello\nWorld"  # 输出为两行字符

3.反斜杠 \

可通过反斜杠 \ 将较长的命令拆成多行,便于阅读。

echo -e "a\nb"
# 等价于
echo -e \
"a\nb"

4.空格

Bash 通过空格区分不同的参数。若参数之间有两个及以上的空格,则 Bash 只会解释一个空格。

echo a           b  # 输出为 a b

5.分号、&&、||

 分号是命令的结束符,可用于在一行书写多个命令。

echo a ;  echo b    # 无论前一个命令是否被成功执行,下一个命令均会被执行。
echo a && echo b  # 仅当前一个命令成功执行,下一个命令才会被执行。
echo a || echo b  # 仅当前一个命令执行失败,下一个命令才会被执行。

6.type 命令

用于判断命令是内置命令,还是外部程序。

type echo
# 输出为 echo is a shell builtin
type ls
# 输出为 ls is hashed (/bin/ls)

7.自动补全

 命令输入到一半的时候,可以按下 Tab 键,Bash 会自动完成剩下的部分。比如,输入tou,然后按一下 Tab 键,Bash 会自动补上ch

 除了命令的自动补全,Bash 还支持路径的自动补全。有时,需要输入很长的路径,这时只需要输入前面的部分,然后按下 Tab 键,就会自动补全后面的部分。如果有多个可能的选择,按两次 Tab 键,Bash 会显示所有选项,让你选择。

8.模式扩展(globbing)

 Shell 接收到用户输入的命令以后,会根据空格将用户的输入拆分成一个个词元(token)。然后,Shell 会扩展词元里面的特殊字符,扩展完成后才会调用相应的命令。

 Bash 是先进行扩展,再执行命令。命令本身并不存在参数扩展。

 所有文件名扩展只匹配单层路径,不能跨目录匹配,即无法匹配子目录里面的文件。或者说,?*这样的通配符,不能匹配路径分隔符(/)。

 Bash 允许文件名使用通配符,即文件名包括特殊字符。这时引用文件名,需要把文件名放在单引号或双引号里面。

 关闭扩展:

set -o noglob
set -f

 打开扩展:

set +o noglob
set +f

 波浪线扩展:

echo ~        # ~ 会被扩展为当前用户的主目录
cd ~/Desktop  # ~/dir 会被扩展为主目录的子目录 dir
echo ~+       # ~+ 会被扩展为当前目录

 ? 字符扩展:? 匹配除空字符之外的任意单个字符,属于文件名扩展,可匹配文件或文件目录。

# 只有文件确实存在的前提下,才会发生扩展。
echo ?.txt
# 若当前目录有匹配文件 a.txt,则会输出 a.txt
# 若当前目录不存在匹配文件,则会输出 ?.txt

 * 字符扩展:* 字符匹配文件路径中的任意数量的任意字符,包括零个字符、空字符。

# 存在文件 b.txt 和 ab.txt
ls *b*
# 输出 b.txt ab.txt

 * 不会匹配隐藏文件(即以 . 开头的文件),要想匹配隐藏文件,需写成 .*

 * 扩展同样属于文件名扩展,只有文件确实存在的前提下才会进行扩展。否则会原样输出。

 * 扩展只匹配当前目录,不匹配子目录。

# 若子目录存在文件 a.txt
# 不会匹配子目录的文件
ls *.txt
# 可以匹配子目录的文件
ls */*.txt

 方括号扩展:同样属于文件名扩展,只有文件确实存在的前提下才会进行扩展。否则会原样输出。

# [ab] 可以匹配 a 或 b
# 存在文件 a.txt 和 b.txt
ls [ab].txt
# 输出 a.txt b.txt

# [^...]、[!...] 表示匹配不在方括号里的字符
# 存在文件 aaa、bbb、aba
ls ?[!a]?
# 输出 bbb aba

 如果需要匹配[字符,可以放在方括号内,比如[[aeiou]。如果需要匹配连字号-,只能放在方括号内部的开头或结尾,比如[-aeiou][aeiou-]

 [start-end]扩展表示匹配一个连续的范围。例如 [a-c] 等价于 [abc].

 [!start-end]表示匹配不属于此范围的字符。

 大括号扩展:分别扩展为大括号里的所有值。此扩展不属于文件名扩展,它会扩展成所有给定的值,而不管是否有对应的文件存在。

echo d{a,b,c}g
# 输出 dag dbg dcg

# 大括号中的逗号前后不能有空格,否则大括号扩展会失效
# 大括号中的某一项可以没有值,代表该项为空。但是该项仍会被扩展。
# 大括号扩展可以嵌套
# 大括号扩展可以与其他扩展连用,并且总是优先被扩展

 {start..end}扩展表示扩展为一个连续序列,并且该扩展支持逆序,且可以嵌套使用。

echo {a..b}{1..3}
# 输出 a1 a2 a3 b1 b2 b3

 变量扩展:Bash 将美元符号$开头的词元视为变量,将其扩展成变量值。变量名除了放在美元符号后面,也可以放在${}里面。${!string*}${!string@}返回所有匹配给定字符串string的变量名。

 子命令扩展:$(...)可以扩展成另一个命令的运行结果,该命令的所有输出都会被作为返回值。该扩展可以嵌套使用。

echo $(date)
# 2022年 7月17日 星期日 18时33分03秒 CST

echo `date`
# 具有相同效果

 算术扩展:$((...))可以扩展成整数运算的结果。

echo $((2+2))
# 输出 4

 量词语法:用于控制模式匹配的次数,只有在 Bash 的extglob参数打开的情况下才能使用。量词语法也属于文件名扩展,如果不存在可匹配的文件,就会原样输出。

shopt extglob     # 用于查询当前参数是否被打开
shopt -s extglob  # 打开 extglob 参数
  • ?(pattern-list):模式匹配零次或一次。
  • *(pattern-list):模式匹配零次或多次。
  • +(pattern-list):模式匹配一次或多次。
  • @(pattern-list):只匹配一次模式。
  • !(pattern-list):匹配给定模式以外的任何内容。
ls abc?(.)txt  # ?(.) 匹配零个或一个点

9.shopt 命令

# 打开某个参数
shopt -s [optionname]

# 关闭某个参数
shopt -u [optionname]

# 查询某个参数关闭还是打开
shopt [optionname]

dotglob参数可以让扩展结果包括隐藏文件(即点开头的文件)。默认情况下,扩展结果不包括隐藏文件。

nullglob参数在通配符不匹配任何文件名时,会让通配符返回空字符。默认情况下,通配符不匹配任何文件名时,会原样输出。

failglob参数在通配符不匹配任何文件名时,会让 Bash 直接报错,而不是让使用通配符的命令去处理匹配结果。

extglob参数使得 Bash 支持 ksh 的一些扩展语法,主要用于支持量词语法。

nocaseglob参数可以让通配符扩展不区分大小写。

globstar参数可以使得**匹配零个或多个子目录。

 假设存在如下文件结构。顶层目录、第一级子目录sub1、第二级子目录sub1\sub2里面各有一个文本文件。

a.txt
sub1/b.txt
sub1/sub2/c.txt

 使用通配符,查找出上述三个文件的方法:

ls *.txt */*.txt */*/*.txt
# 输出 a.txt  sub1/b.txt  sub1/sub2/c.txt

 因为*只能匹配当前目录,如果要匹配子目录,只能一层层写出来。

 打开globstar参数以后,使用**/*.txt就可以得到想要的结果。

shopt -s globstar
ls **/*.txt
# 输出 a.txt  sub1/b.txt  sub1/sub2/c.txt

10.sed 命令

sed -i '' "s/A/B/g" test.txt

sed -i '' "s/A/B/1" test.txt

#在 MAC 中,Shell 语句的换行符是 \n
sed -i '' "s/A/B \n C\g" test.txt

#需要对 / 进行转义
sed -i '' "s/A/'https:\/\/github.com'/g"

I)-i: 直接修改源文件。

II)g: 对数据中所有匹配到的内容进行替换。

III)n: 1~512 之间的数字,表示当要替换的字符串第 n 次出现时,才进行替换。

IV)'': 在 -i 之后加上一对引号,来指定备份格式。如果不需要备份,引号里的内容可以为空。

本节参考文章

  1. Linux sed命令完全攻略(超级详细)
  2. Linux sed命令详解