历史命令与命令补全

history:历史命令

-c:清空历史

-w:把内存中的历史命令写入历史命令文件中,~/.bash_history(内存中的历史命令不会立马保存到文件中)

历史命令默认保存 1000 条,可以更改配置文件中的 HISTSIZE

1
2
3
4
5
$ cat ~/.bashrc | grep HIS
HISTCONTROL=ignoreboth
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

!n:重复执行第 n 条历史命令,先用 history 查看条数

!!:重复执行上条历史命令

!xxx:重复执行最后一条以 xxx 开头的命令

1
2
$ !echo
echo "alias ls='ls -lh'" >> ~/.bashrc

Bash 中,命令与目录均可以用 Tab 键即可补全。

脚本执行

  1. 赋予执行权限,通过绝对路径或相对路径执行

    chmod 755 xx.sh

    ./xx.sh

  2. 通过 bash 直接执行脚本,不需要执行权限

    bash xx.sh

命令行展开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ echo {1,2,3,4}
1 2 3 4

$ echo file{1,3,6}
file1 file3 file6

$ echo {1..5}
1 2 3 4 5

$ echo {1..5..2}
1 3 5

$ echo {01..05..2}
01 03 05

$ echo {a..e}
a b c d e

别名与快捷键

别名

alias 别名='原命令'

unalias 别名:取消别名

\ 命令:忽略别名执行命令

1
2
3
$ alias
alias grep='grep --color=auto'
alias ls='ls --color=auto'

命令行中定义的别名重启即失效,需要写入环境变量配置文件才能永久生效,如 ~/.bashrc

1
2
3
4
$ echo "alias ls='ls -lh'" >> ~/.bashrc
$ . ~/.bashrc
$ alias
alias ls='ls -lh'

命令执行顺序

  1. 通过相对路径或绝对路径调用

  2. 别名

  3. Bash 内部命令,如 cd

    1
    2
    3
    4
    $ whereis ls
    ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz
    $ whereis cd
    cd:
  4. PATH 环境变量定义的目录中查找到的第一个命令

Bash 常用快捷键

常用的

Ctrl+A:光标移到开头

Ctrl+E:光标移到结尾

Ctrl+C:强制终止当前命令

Ctrl+L:清屏,相当于 clear

Ctrl+U:删除或剪切光标之前输入的命令

Ctrl+K:删除或剪切光标之后输入的命令

Ctrl+Y:粘贴 Ctrl+UCtrl+K 剪切的内容

Ctrl+R:在历史命令中搜索

Ctrl+D:退出当前终端

输入输出重定向

输出重定向

Linux 中标准的输入输出和标准错误输出:

设备 文件名 文件描述符 类型
键盘 /dev/stdin 0 标准输入
显示器 /dev/stdout 1 标准输出
显示器 /dev/stderr 2 标准错误输出

正常情况下,Shell 通过显示器输出命令返回的内容,可以通过重定向将其存入文件。

  1. 将正确输出重定向至文件

    ls > filels >> file(追加写入)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ ls
    a b date_now
    $ ls > right
    $ cat right
    a
    b
    date_now
    right

    $ ls > /dev/null # 输出丢进黑洞
  2. 将错误输出重定向至文件

    lssss 2> file_error,注意 2 和 > 之间没有空格

    lssss 2>> file_error(追加写入)

    1
    2
    3
    $ lssss 2> file_error
    $ cat file_error
    lssss: command not found
  3. 将正确和错误输出同时重定向至文件

    ls > file 2>&1ls >> file 2>&1(追加写入)

    ls &> file

    1
    2
    3
    4
    5
    6
    7
    8
    $ ls > file 2>&1
    $ cat file
    a
    b
    file
    $ lss > file 2>&1
    $ cat file
    Command 'lss' not found, but there are 15 similar ones.
  4. 将正确和错误输出分开重定向至文件

    ls > right 2> errorls >> right 2>> error(追加写入)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ ls > right 2> error
    $ cat right
    right
    testfile
    $ cat error
    $ lss > right 2> error
    $ cat right
    $ cat error
    Command 'lss' not found, but there are 15 similar ones.

输入重定向

使用不多,了解即可。

wc [选项] [文件名]

-c:统计字节数

-w:统计单词数

-l:统计行数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Ctrl + D 结束输入,分别为行数、单词数、字节数
$ wc
my pony is a cat
my cat is a pony
2 10 34

# 统计文件的行数、单词数、字节数
$ cat testfile
dasd
asdas
asdasd sdc sdc sdc
$ wc testfile
3 6 30 testfile

$ wc -w testfile
6 testfile
$ wc -l testfile
3 testfile

$ wc < testfile
3 6 30
$ wc << hello
> 12
> 12112
> 121
> hello # 直到输入 hello 才结束统计
3 3 13

多命令顺序执行与管道符

多命令

符号 格式 作用
; ls;date 顺序执行,无逻辑关系,仅连接作用
&& make && make install 命令 1 成功执行后才会执行命令 2
` `

例:;

磁盘复制命令:dd

1
2
3
4
5
6
$ date;dd if=/dev/zero of=./testfile bs=1k count=102400;date
Sun Jul 3 09:01:03 AM CST 2022
102400+0 records in
102400+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.19336 s, 542 MB/s
Sun Jul 3 09:01:03 AM CST 2022

例:&&

1
2
3
4
5
$ ls && echo yes
linux_learn testfile
yes
$ lsssss && echo yes
lsssss: command not found

例:||

1
2
3
4
5
$ ls || echo yes
linux_learn testfile
$ lssss || echo no
lssss: command not found
no

命令 && echo yes || echo no,判断命令是否正确执行

1
2
3
4
5
6
$ ls && echo yes || echo no
linux_learn testfile
yes
$ lssss && echo yes || echo no
lssss: command not found
no

管道符

格式:命令1 | 命令2,命令 1 的正确输出作为命令 2 的操作对象

1
2
3
4
5
6
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
$ cat /etc/passwd | grep -n 'root'
1:root:x:0:0:root:/root:/bin/bash

grep [选项] '搜索内容' 文件名

-i:忽略大小写

-n:输出行号

-v:反向查找

--color=auto:关键字高亮(已在 .bashrc 中设置别名)

通配符及特殊符号

通配符

用来匹配文件名,不同于正则表达式。

通配符 作用
任意一个字符
* 0 个或任意多个字符,即任何内容
[] 匹配中括号中的任意一个字符。[abc]:a 或 b 或 c
[-] 匹配中括号中范围内的一个字符。[a-c]:a 或 b 或 c
[^] 逻辑非,排除此范围内的一个字符。[^0-9]:不是数字
1
2
3
4
5
6
7
8
9
10
11
12
$ ls
012 0abc abc abcd
$ ls ?abc
0abc
$ ls *abc
0abc abc
$ ls *abc*
0abc abc abcd
$ ls [0-9]*
012 0abc
$ ls [^0-9]*
abc abcd

Bash 中常见特殊符号

符号 作用
'' 单引号中的所有特殊符号都没有特殊含义,如:$ ```
"" 双引号中除 $ \ ``` 外,均无特殊含义,分别为调用变量值、转义符、引用命令
`` 反引号中的内容是系统命令,在 Bash中先执行它。与 $() 作用一致。
$() 与反引号作用一致,用来引用系统命令。
# 在 shell 脚本中,# 开头表示注释。
$ 用于调用变量值,如调用 name 变量,$name
\ 转义符,在 \ 之后的字符失去特殊含义,变为普通字符。

例:$

1
2
3
$ name=tim
$ echo $name
tim

例:'' ""

1
2
3
4
5
6
7
$ test=1
$ echo "$test"
1
$ echo '$test'
$test
$ echo $test
1

例:$()

1
2
3
4
$ echo '$(date)'
$(date)
$ echo "$(date)"
Sun Jul 3 09:41:34 AM CST 2022

例:\

1
2
3
4
$ echo $name
tim
$ echo \$name
$name

Bash 变量

Bash 变量规则

  • 变量名同其他编程语言,不能数字开头

  • Bash 中默认变量类型为字符串,如果进行数值运算,则必须指定变量类型为数值型

  • 变量用等号连接,左右两侧不能加空格

    1
    2
    $ a = 1
    a: command not found
  • 变量值若有空格,需要用引号括起来

    1
    2
    3
    $ a='1 1 1'
    $ echo $a
    1 1 1
  • 变量值中可以使用 \ 转义

  • 变量可以叠加,需要用 "$变量"${变量} 的形式

  • 可以把命令结果作为值赋给变量,需要用反引号或 $() 包含命令

    1
    2
    3
    4
    5
    6
    $ a=$(date)
    $ echo $a
    Sun Jul 3 02:15:57 PM CST 2022
    $ a=`date`
    $ echo $a
    Sun Jul 3 02:16:12 PM CST 2022
  • 环境变量名建议大写,便于区分

变量分类

  • 用户自定义变量
  • 环境变量:主要保存和操作系统环境相关的数据
  • 位置参数变量:主要用来向脚本传递参数或数据,变量名不能自定义,变量的作用是固定的
  • 预定义变量:Bash 中已经定义好的变量,变量名不能自定义,变量的作用也是固定的

用户自定义变量

用户自定义变量,也叫本地变量

变量定义:str1='123'

变量调用:$str1

变量叠加:str1="$str1"456str1=${str1}456

1
2
3
4
5
6
7
8
9
$ str1='123'
$ echo $str1
123
$ str1="$str1"456
$ echo $str1
123456
$ str1=${str1}789
$ echo $str1
123456789

变量查看:set(所有变量)

变量删除:set str1

1
2
3
4
5
$ set | grep str1
str1=123456789
$ unset str1
$ set | grep str1
_=str1

环境变量

用户自定义变量只在当前 shell 生效,而环境变量会在当前 shell 和此 shell 的子 shell 中生效;如果将环境变量写入配置文件,则此环境变量会在所有 shell 中生效。

子 shell 的定义

1
2
3
4
5
6
7
8
9
10
11
$ pstree
init─┬─init───init───bash───pstree
└─2*[{init}]
$ bash
$ pstree
init─┬─init───init───bash───bash───pstree
└─2*[{init}]
$ bash
$ pstree
init─┬─init───init───bash───bash───bash───pstree
└─2*[{init}]

设置环境变量:export 变量名=变量值

将已有变量设置为环境变量:export 变量名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ name=xiaoming
$ export gender=man
$ age=18
$ export age
$ pstree
init─┬─init───init───bash───pstree
└─2*[{init}]
$ bash
$ pstree
init─┬─init───init───bash───bash───pstree
└─2*[{init}]
$ echo $name

$ echo $age
18
$ echo $gender
man
# 本地变量只在当前 shell 生效,环境变量在子 shell 中也生效

查询环境变量:env

1
2
3
4
5
$ env
SHELL=/bin/bash
WSL_DISTRO_NAME=Ubuntu-22.04
gender=man
age=18

删除环境变量:unset 变量名

常见环境变量

PATH:冒号分割的文件路径

1
2
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Program Files (x86)/Common Files/Intel/Shared Libraries/redist/intel64/compiler:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/c/Users/liuao/AppData/Roaming/nvm:/mnt/c/Program Files/nodejs:/mnt/c/Program Files/Git/cmd:/mnt/c/Program Files/dotnet/:/mnt/c/R/Java/jre1.8.0_333/bin:/mnt/c/Users/liuao/AppData/Local/Programs/Python/Python310/Scripts/:/mnt/c/Users/liuao/AppData/Local/Programs/Python/Python310/:/mnt/c/Users/liuao/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/liuao/AppData/Local/Programs/Microsoft VS Code/bin:/mnt/c/Program Files/Bandizip/:/mnt/c/Users/liuao/AppData/Local/GitHubDesktop/bin:/snap/bin

输入命令时,系统会在 PATH 定义的路径中搜索命令并执行,省去了输入绝对路径的麻烦。

1
2
3
4
$ which ls
/usr/bin/ls
$ /usr/bin/ls
linux_learn testfile

一般习惯把可执行文件的路径追加到 PATH 中,这样便可以直接执行

1
2
PATH="$PATH":[路径]
PATH=${PATH}:[路径]

PS1:定义系统提示符的变量

$:分别表示用户,主机,路径,普通用户,可自定义格式

1
2
3
4
$ echo $PS1
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$
$ PS1='\u@\t \# \h '
aohui@15:13:51 4 lah-PC

位置参数变量

变量 作用
$n $0 表示命令本身,$1-9 表示第 1-9 个参数,10 以上要括起来,${10}
$* 代表命令中的所有参数,看成一个整体
$@ 代表命令中的所有参数,参数间独立
$# 命令中参数个数

例:$n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ cat arg1.sh
#!/bin/bash

echo $0
echo $1
echo $2
echo $3
$ ./arg1.sh
./arg1.sh # $0 是命令本身



$ ./arg1.sh 1 2 3 4
./arg1.sh
1
2
3

$ cat arg2.sh
#!/bin/bash

echo $(($1+$2)) # 数值运算要用 $(()) 括起来

$ ./arg2.sh 1 2
3

例:$* $@ $#

1
2
3
4
5
6
7
8
9
10
$ cat arg3.sh
#!/bin/bash

echo $#
echo $*
echo $@
$ ./arg3.sh 1 2 3 4 5 6
6
1 2 3 4 5 6
1 2 3 4 5 6

$*$@ 的区别:$* 把所有参数看成一个整体,$@ 每个参数单独区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cat arg4.sh
#!/bin/bash

for i in "$*"
do
echo $i
done
for j in "$@"
do
echo $j
done
$ ./arg4.sh 1 2 3 4
1 2 3 4
1
2
3
4

预定义变量

位置参数变量是预定义变量的一种。

变量 作用
&? 上一条命令的执行情况,0:正确执行,不同错误返回不同值
$$ 当前进程的 PID 号
$! 后台运行的最后一个进程的 PID 号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cat arg5.sh
#!/bin/bash

echo $$
find hello.sh &
echo $!
$ ./arg5.sh
410
411

$ find: ‘hello.sh’: No such file or directory
$ echo $?
130
$ ls
arg1.sh arg2.sh arg3.sh arg4.sh arg5.sh linux_learn testfile
$ echo $?
0

键盘输入

比用

位置参数变量接收输入信息更加直观。

read [选项] 变量

-t:时间限制,秒

-p:输入提示

-s:隐藏输入信息

-n:限制输入字符数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat arg5.sh
#!/bin/bash

read -t 30 -p "Input your name: " name
echo $name

read -t 30 -s -p "Input your age: " age
echo -e "\n"
echo $age

$ ./arg5.sh
Input your name: nb
nb
Input your age:

25

数值运算与运算符

数值运算

Bash 中默认变量为字符串型,不能进行数值运算

1
2
3
4
$ a=1
$ b=2
$ echo $a+$b
1+2

方法 1

declare:声明变量的类型

-:给变量设定类型属性

+:取消变量的类型属性

-i:将变量声明为整数型

-x:将变量声明为环境变量

-p:显示变量被声明的类型

declare -i c=$a+$b 即可。

1
2
3
4
5
6
7
8
9
10
11
$ declare -p a
declare -- a="1"
$ declare -i a
$ declare -p a
declare -i a="1"
$ declare -x a
$ declare -p a
declare -ix a="1"
$ declare -i c=$a+$b
$ echo $c
3

方法 2

exprlet(不常用)

+ 号左右空格不能省略

1
2
3
$ d=$(expr $a + $b)
$ echo $d
3

方法 3

$(())$[](常用)

总结:$,表示变量值;$(),表示系统命令;$(()),表示数值运算

1
2
3
4
5
6
$ f=$(($a+$b))
$ echo $f
3
$ g=$[$a+$b]
$ echo $d
3

运算符

$(()) 包起来即可,里面正常写算式,通用优先级。

变量测试与内容替换

变量置换方式 y 不存在 y 为空 y 不为空
x=${y-new} x=new x 为空 x=$y

内容太多,容易记混,遇到查表即可

1
2
3
4
5
6
7
8
9
10
11
12
$ unset y
$ x=${y-new}
$ echo $x
new
$ y=""
$ x=${y-new}
$ echo $x

$ y="old"
$ x=${y-new}
$ echo $x
old

环境变量配置文件

将环境变量写入相应配置文件中,便于在所有 shell 中生效。

修改环境变量后,source 配置文件. 配置文件,不用重新登录即可使配置文件生效。

默认的系统环境变量主要有:PATHHISTSIZEPS1HOSTNAME等。

环境变量配置文件类型:

  • /etc/profile
  • /etc/profile.d/*.sh
  • /etc/bashrc
  • ~/.bash_profile
  • ~/.bashrc

前三个对所有用户均生效,后两个只对当前用户生效。

配置文件作用

配置文件的调用顺序

1
2
3
4
5
6
7
8
9
graph LR
A["/etc/profile"] -->B["~/.bash_profile"]
B --> C["~/.bashrc"]
C --> D["/etc/bashrc"]
D --> E[命令提示符]
A --> F["/etc/profile.d/*.sh"]
D --> F
F --> G["/etc/profile.d/lang.sh"]
G --> H["/etc/sysc config/i18n"]

/etc/profile:shell 登录即读取,作用如下:

  • 定义 USER 变量

  • LOGNAME

  • MAIL

  • PATH

  • HOSTNAME

  • HISTSIZE

  • unmask

  • 调用 /etc/profile.d/*.sh 中的所有文件

~/.bash_profile 作用:

  • 调用 ~/.bashrc

  • PATH 变量后加入了 $HOME/bin 目录

~/.bashrc 作用:

  • 定义别名

  • 调用 /etc/bashrc

/etc/bashrc:无 login 的 shell 读取(子 shell)

  • PS1
  • umask
  • PATH
  • 调用 /etc/profile.d/*.sh 中的所有文件

越往后的配置文件优先级越高

其他环境配置文件

注销生效的配置文件 :~/.bash_logout,如注销后清空历史即可将命令写入该文件。

1
2
3
4
5
6
7
8
$ cat ~/.bash_logout
# ~/.bash_logout: executed by bash(1) when login shell exits.

# when leaving the console clear the screen to increase privacy

if [ "$SHLVL" = 1 ]; then
[ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q
fi

历史命令:~/.bash_history,排错时使用。

终端登录信息

本地终端:/etc/issue,远程登录无效

1
2
$ cat /etc/issue
Ubuntu 22.04 LTS \n \l

远程登录:/etc/issue.net

1
2
$ cat /etc/issue.net
Ubuntu 22.04 LTS

登录后欢迎信息:/etc/motd

正则表达式

正则表达式与通配符(* ? [])的区别

  • 正则表达式用于匹配文件中的字符串,grepawksed 等命令支持,包含匹配

  • 通配符用来匹配文件名,lsfindcp 支持,且不支持正则,完全匹配

    1
    2
    3
    4
    5
    $ touch aa aabb
    $ ls aa
    aa
    $ ls aa*
    aa aabb

语法

基础正则表达式

操作符 说明 例子
. 表示任何单个字符(除换行符,即空行)
[] 给出单个字符的取值范围 [abc]:a 或 b 或 c,[a-z]:a-z 单个字母
[^] 排除此取值范围 [^abc]:非 a 或 b 或 c
* 前一个字符出现 >=0 abc*:ab、abc、abcc、abccc 等
\{m\} 前一个字符出现 m ab{2}c:abbc
\{n,\} 前一个字符出现 >=n ab{2,}c:abbc、abbbc、abbbbc 等
\{m,n\} 前一个字符出现 m-n 次(含 n ab{1,3}c:abc、abbc、abbbc
^ 匹配字符串的开头 ^123:字符串以 123 开头
$ 匹配字符串的结尾 123$:字符串以 123 结尾
\ 转义字符 \.:普通的 .

拓展正则表达式(使用较少)

操作符 说明 例子
+ 前一个字符出现 >=1 abc+:abc、abcc、abccc 等
? 前一个字符出现 01 abc?:ab、abc
` ` 左右表达式中的一个
() 分组标记,内部只能使用 \ (abc):abc,`(abc

注意,Linux 系统下,文本每行均以 $ 结尾,所以,匹配空行可以用 ^$

1
2
3
4
5
6
7
$ cat -en ~/Desktop/sort_file_name.sh # 显示所有字符,包括非打印字符
1 #!/bin/bash$
2 if [ -d $1 ];then$
3 cd $1$
4 pwd$
5 $
6 i=1$

字符串处理

字符串截取命令

cut 提取列

cut [选项] 文件名

-f:列号,提取第几列

-d:分隔符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ cat student.txt
ID Name Gender Mark
1 Liming M 86
2 Sc M 90
3 Gao M 83
$ cut -f 1,2 student.txt
ID Name
1 Liming
2 Sc
3 Gao

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Program Files (x86)/Common Files/Intel/Shared Libraries/redist/intel64/compiler:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/c/Users/liuao/AppData/Roaming/nvm:/mnt/c/Program Files/nodejs:/mnt/c/Program Files/Git/cmd:/mnt/c/Program Files/dotnet/:/mnt/c/R/Java/jre1.8.0_333/bin:/mnt/c/Users/liuao/AppData/Local/Programs/Python/Python310/Scripts/:/mnt/c/Users/liuao/AppData/Local/Programs/Python/Python310/:/mnt/c/Users/liuao/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/liuao/AppData/Local/Programs/Microsoft VS Code/bin:/mnt/c/Program Files/Bandizip/:/mnt/c/Users/liuao/AppData/Local/GitHubDesktop/bin:/snap/bin
$ echo $PATH | cut -d : -f 5
/sbin

# 提取普通用户名
$ cat /etc/passwd | grep /bin/bash
root:x:0:0:root:/root:/bin/bash
aohui:x:1000:1000:aohui:/home/aohui:/bin/bash
$ cat /etc/passwd | grep /bin/bash | grep -v root
aohui:x:1000:1000:aohui:/home/aohui:/bin/bash
$ cat /etc/passwd | grep /bin/bash | grep -v root | cut -d : -f 1
aohui

局限:无法提取以多个空格分割的列,单个可以

需要用 awk 命令

1
2
3
4
5
6
7
8
9
10
11
12
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sdb 251G 1.5G 237G 1% /
none 3.9G 4.0K 3.9G 1% /mnt/wsl
$ df -h | grep /dev/sdb
/dev/sdb 251G 1.5G 237G 1% /
$ df -h | grep /dev/sdb | cut -f 5
/dev/sdb 251G 1.5G 237G 1% /
$ df -h | grep /dev/sdb | cut -d ' ' -f 5

$ echo 'a b c d' | cut -d ' ' -f 2,4
b d

printf 格式化打印

printf '输出格式' 输出内容

%s:输出 n 个字符串

%i:输出 n 个数字的整数

%.nf:输出 n 位小数的浮点数

常用输出格式:

\r:回车

\n:换行符

\t:制表符

1
2
3
4
5
6
7
8
9
$ printf '%s\n' hello world
hello
world
$ printf '%s\n' 'hello world'
hello world
$ printf '%i\n' 100
100
$ printf '%.3f\n' 100.23355
100.234

awk 提取列

awk '条件1{动作1} 条件2{动作2}…' 文件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ cat student.txt
ID Name Gender Mark
1 Liming M 86
2 Sc M 90
3 Gao M 83
$ awk '{printf $2 "\t" $4 "\n"}' student.txt
Name Mark
Liming 86
Sc 90
Gao 83

$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sdb 251G 1.5G 237G 1% /
none 3.9G 4.0K 3.9G 1% /mnt/wsl
$ df -h | awk '{printf $1 "\t" $5 "\n"}'
Filesystem Use%
/dev/sdb 1%
none 1%

$ df -h | grep /dev/sdb | awk '{printf $5 "\n"}' | cut -d '%' -f 1
1

BEGIN:在读取数据之前执行命令

1
2
3
4
5
6
$ awk 'BEGIN{printf "Printing!!!\n"} {printf $2 "\t" $4 "\n"}' student.txt
Printing!!!
Name Mark
Liming 86
Sc 90
Gao 83

FS:指定分隔符

1
2
3
4
5
6
7
8
9
10
11
12
13
$ awk '{FS=":"} {print $1 "\t" $6}' /etc/passwd
root:x:0:0:root:/root:/bin/bash # 第一条数据读入后才开始执行动作
daemon /usr/sbin
bin /bin
sys /dev
sync /bin

$ awk 'BEGIN{FS=":"} {print $1 "\t" $6}' /etc/passwd
root /root
daemon /usr/sbin
bin /bin
sys /dev
sync /bin

END:最后执行动作

1
2
3
4
5
6
7
$ awk 'BEGIN{printf "Printing!!!\n"} {printf $2 "\t" $4 "\n"} END{printf "Done!!!\n"}' student.txt
Printing!!!
Name Mark
Liming 86
Sc 90
Gao 83
Done!!!

条件判断

1
2
3
4
5
6
7
$ cat student.txt | grep -v Name
1 Liming M 86
2 Sc M 90
3 Gao M 83
$ cat student.txt | grep -v Name | awk '$4>85{print $2 "\t" $4}'
Liming 86
Sc 90

sed 编辑操作

轻量级流编辑器,主要用来将数据进行选取、替换、删除、新增。

sed [选项] '[动作]' 文件名

选项

  • -n:只输出经过 sed 命令处理的行

  • -e:允许对输入数据应用多条 sed 命令编辑,多条件 ; 号分割

  • -i:数据修改后不光输出数据,也更改文件的原数据

动作

  • a\:追加,添加多行时,除最后一行,每行要用 \ 表示数据未完结
  • c\:替换,替换多行时,除最后一行,每行要用 \ 表示数据未完结
  • i\:插入,插入多行时,除最后一行,每行要用 \ 表示数据未完结
  • d:删除,删除指定行
  • p:打印,输出指定行
  • s:字符串替换,格式:行范围s/old/new/g
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
$ sed '2p' student.txt
ID Name Gender Mark
1 Liming M 86
1 Liming M 86
2 Sc M 90
3 Gao M 83
$ sed -n '2p' student.txt # 加 -n 才会只输出经过处理后的,一般 p 与 -n 配合
1 Liming M 86

$ sed '2,3d' student.txt
ID Name Gender Mark
3 Gao M 83

$ sed 's/M/W/g' student.txt
ID Name Gender Wark
1 Liming W 86
2 Sc W 90
3 Gao W 83

$ sed '3s/W/M/g' student.txt # 可只替换某一行
ID Name Gender Wark
1 Liming W 86
2 Sc M 90
3 Gao W 83

$ sed '2a hello\
> world' student.txt
ID Name Gender Wark
1 Liming W 86
hello
world
2 Sc W 90
3 Gao W 83

$ sed '2i hello\
world' student.txt
ID Name Gender Wark
hello
world
1 Liming W 86
2 Sc W 90
3 Gao W 83

$ sed '2,3c hello\
world' student.txt
ID Name Gender Wark
hello
world
3 Gao W 83

# 多命令同时执行
$ sed -e '4s/W/M/g;1d' student.txt
1 Liming W 86
2 Sc W 90
3 Gao M 83

字符串处理命令

sort 排序

sort [选项] 文件名

-f:忽略大小写

-n:以数值型排序,默认是字符串

-r:反向排序

-t:指定分隔符,默认制表符

-k n[,m]:按照指定字段范围排序。从第 n 字段开始,m 字段结束(默认到行尾)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ sort /etc/passwd
aohui:x:1000:1000:aohui:/home/aohui:/bin/bash
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin

$ sort -r /etc/passwd
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
uuidd:x:107:113::/run/uuidd:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin

$ sort -t ":" -k 3,3 /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin

$ sort -t ":" -n -k 3,3 /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin

wc 统计

wc [选项] 文件名

-l:行数

-w:单词数

-m:字数

1
2
3
4
5
6
7
8
$ wc /etc/passwd
31 43 1635 /etc/passwd
$ wc -l /etc/passwd
31 /etc/passwd
$ wc -w /etc/passwd
43 /etc/passwd
$ wc -m /etc/passwd
1635 /etc/passwd

条件判断和流程控制

条件判断

按文件类型判断

常用的有 4 个

  • -d 文件:文件是否存在,且为目录
  • -e 文件:文件是否存在
  • -f 文件:文件是否存在,且为普通文件
  • -L 文件:文件是否存在,且为符号链接文件

test [选项] 文件[ [选项] 文件 ](两端有空格)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ test -d /etc/passwd
$ echo $?
1
$ test -f /etc/passwd
$ echo $?
0
$ test -e /etc/passwd
$ echo $?
0
$ [ -e /etc/passwd ]
$ echo $?
0

$ [ -e /etc/passwd ] && echo yes || echo no
yes
$ [ -d /etc/passwd ] && echo yes || echo no
no

按文件权限判断

常用 3 个

-r 文件:文件是否存在,且有读权限

-w 文件:文件是否存在,且有写权限

-x 文件:文件是否存在,且有执行权限

1
2
3
4
5
6
7
8
9
10
11
12
$ ls -l /etc/passwd
-rw-r--r-- 1 root root 1635 Jul 2 19:52 /etc/passwd

# 只要一个有即为真,不管是所有者、所属组还是其他人,不能判断身份
$ [ -r /etc/passwd ] && echo yes || echo no
yes
$ [ -w /etc/passwd ] && echo yes || echo no
no
$ [ -x /etc/passwd ] && echo yes || echo no
no
$ [ -w student.txt ] && echo yes || echo no
yes

比较两个文件

文件1 -nt 文件2:文件 1 是否比文件 2 新

文件1 -ot 文件2:文件 1 是否比文件 2 旧

文件1 -ef 文件2:两个文件的 inode 是否一致,可以用来判断硬链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ls -l
-rw-r--r-- 1 aohui aohui 55 Jul 5 09:30 student2.txt
-rw-r--r-- 1 aohui aohui 55 Jul 4 19:55 student.txt
$ [ student.txt -nt student2.txt ] && echo yes || echo no
no
$ [ student.txt -ot student2.txt ] && echo yes || echo no
yes

$ ln student.txt stu.txt
$ ls -li
6348 -rw-r--r-- 1 aohui aohui 55 Jul 5 09:30 student2.txt
3616 -rw-r--r-- 2 aohui aohui 55 Jul 4 19:55 student.txt
3616 -rw-r--r-- 2 aohui aohui 55 Jul 4 19:55 stu.txt
$ [ student.txt -ef stu.txt ] && echo yes || echo no
yes
$ [ student.txt -ef student2.txt ] && echo yes || echo no
no

比较两个整数

整数1 -eq 整数2:整数1 = 整数2 ?

整数1 -ne 整数2:整数1 != 整数2 ?

整数1 -gt 整数2:整数1 > 整数2 ?

整数1 -lt 整数2:整数1 < 整数2 ?

整数1 -ge 整数2:整数1 >= 整数2 ?

整数1 -le 整数2:整数1 <= 整数2 ?

1
2
3
4
5
6
7
8
9
10
$ [ 1 -lt 2 ] && echo yes || echo no
yes
$ [ 2 -lt 2 ] && echo yes || echo no
no
$ [ 2 -ge 2 ] && echo yes || echo no
yes
$ [ 2 -le 2 ] && echo yes || echo no
yes
$ [ 2 -eq 2 ] && echo yes || echo no
yes

比较两个字符串

-z 字符串:是否为空

-n 字符串:是否非空

字符串1 == 字符串2:两个字符串是否相等

字符串1 != 字符串2:两个字符串是否不相等

1
2
3
4
5
6
$ [ -z '' ] && echo yes || echo no
yes
$ [ -z '111' ] && echo yes || echo no
no
$ [ '11' == '2' ] && echo yes || echo no
no

多重条件判断

判断1 -a 判断2:AND

判断1 -o 判断2:OR

!判断:逻辑非

1
2
3
4
5
6
7
8
$ [ 2 -gt 1 -a '11' == '2' ] && echo yes || echo no
no
$ [ 2 -gt 1 -a '11' == '11' ] && echo yes || echo no
yes
$ ! [ '1' == '1' ] && echo yes || echo no
no
$ ! [ '1' != '1' ] && echo yes || echo no
yes

流程控制

if

单分支写法

1
2
3
4
5
6
7
8
if [];then
do something
fi

if []
then
do something
fi

例:判断根分区占用率

1
2
3
4
5
6
7
8
9
10
$ cat script.sh
#!/bin/bash

rate=$(df -h | grep '/$' | awk '{print $5}' | cut -d '%' -f 1)
if [ $rate -lt 50 ];then
echo OK!!
fi

$ ./script.sh
OK!!

双分支写法

1
2
3
4
5
6
if []
then
do something
else
do something
fi

例:备份文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

date=$(date +%y%m%d)
size=$(du -sh /etc)

if [ -d /tmp/backup ]
then
cd /tmp/backup
echo "Date: $date" > backinfo.txt
echo "Data size: $size" >> backinfo.txt
tar -zcf etc_$date.tar.gz /etc backinfo.txt &> /dev/null
rm backinfo.txt
else
mkdir /tmp/backup
cd /tmp/backup
echo "Date: $date" > backinfo.txt
echo "Data size: $size" >> backinfo.txt
tar -zcf etc_$date.tar.gz /etc backinfo.txt &> /dev/null
rm backinfo.txt
fi

多分支写法

1
2
3
4
5
6
7
8
9
10
if []
then
do something
elif []
then
do something
...
else
do something
fi

例:判断用户输入文件的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash

read -p "Input a file or dir: " file

if [ -z $file ] # 先判断是否非空
then
echo "ERROR, reinput"
exit 69 # 以错误1退出
elif [ ! -e $file ] # 判断文件是否存在
then
echo "File not exist"
exit 2
elif [ -f $file ]
then
echo "A normal file"
elif [ -d $file ]
then
echo "A dir"
else
echo "Other file"
fi

$ ./file.sh
Input a file or dir: /etc
A dir
$ ./file.sh
Input a file or dir: /etc/passwd
A normal file
$ ./file.sh
Input a file or dir: sasasa
File not exist
$ ./file.sh
Input a file or dir:
ERROR, reinput
$ echo $?
69

case

语法

1
2
3
4
5
6
7
8
9
10
11
case $v in
case1)
do something
;;
case2)
do something
;;
*)
do something
;;
esac

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

read -p "Inpuy yes/no: " -t 30 a
case $a in
"yes")
echo yes!
;;
"no")
echo no!
;;
*)
echo error!
;;
esac

$ ./case.sh
Inpuy yes/no: no
no!
$ ./case.sh
Inpuy yes/no: yes
yes!
$ ./case.sh
Inpuy yes/no: 11
error!

for

格式 1

1
2
3
4
for i in a b c d...
do
do something
done

格式 2

1
2
3
4
for ((i=1;i<=100;i=i+1))
do
do something
done

例:批量压缩文件

1
2
3
4
for file in $(ls ~)
do
tar -cf $file.tar $file
done

例:指定循环次数

1
2
3
4
5
6
7
8
9
10
$ cat for1.sh
s=0
for ((i=1;i<=100;i++))
do
s=$(($s+$i))
done
echo "s=$s"

$ ./for1.sh
s=5050

while

1
2
3
4
while []
do
dosomething
done

例:求和

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

i=1
s=0

while [ $i -le 100 ]
do
s=$(($s+$i))
i=$(($i+1))
done
echo "sum = $s"

until

只要循环判断不成立就一直循环

1
2
3
4
until []
do
do something
done

例:求和

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

i=1
s=0

until [ $i -gt 100 ] # i > 100 时,循环终止
do
s=$(($s+$i))
i=$(($i+1))
done
echo "sum = $s"

$ 的用法总结

常见用法

  1. 引用 echo $x
  2. 位置参数变量 $0 $1-9 ${10}... $* $@ $#
  3. 预定义变量 $? $$ $!
  4. 数值运算 ${{}} $[]
  5. 引用系统命令 echo $(date)
  6. 数组索引 ${arr[1]}

shell 数组

bash 支持一维数组(不支持多维数组),并且没有限定数组的大小。

括号表示数组,数值之间用空格分隔:

1
2
3
4
5
6
7
arr=(1 2 3 4)
arr=(
1
2
3
4
)

或者单独定义:

1
2
arr[1]=10
arr[2]=20

使用 ${arr[]} 形式索引,下标从 0 开始:

1
2
$ echo ${arr[1]}
20

获取数组所有元素:${arr[@]}

1
2
$ echo ${arr[@]}
2 10 20 5 6

获取数组长度:

1
2
3
4
5
echo ${#arr[@]}
echo ${#arr[*]}

# 某个元素长度
echo ${#arr[n]}

Vim 常用操作

vim 的三种模式

  • 命令模式:进入 Vim 后的默认模式
  • 插入模式:aio 后进入
  • 编辑模式:命令模式下按 :

插入命令

A:行尾插入

I:行首插入

i:光标当前位置插入

o:向下插入一行

O:向上插入一行

定位命令

:set nu:设置行号

:set nonu:取消行号

gg:到第一行

G:到最后一行

nG:到第 n 行

:n:到第 n 行(用得更多)

$:移至行尾

0:移至行首

删除命令

x:删除光标后字符

nx:删除 n 个

dd:删除此行

ndd:删除 n 行

dG:删除光标后所有文字

D:删除光标后到行尾文字

:n1,n2d:删除指定行

复制剪切命令

yy:复制此行

nyy:复制此行以下 n 行

dd:剪切此行

ndd:剪切此行以下 n 行

p:粘贴至下一行

P:粘贴至上一行

替换取消命令

r:取代光标所在处字符

R:一直替换,ESC 退出

u:撤销上一步

搜索命令

/string:搜索关键词

set ic:忽略大小写

set noic

n:找下一个

%s/old/new/g:全文替换(c:询问确认)

:n1,n2s/old/new/g:指定行替换

保存退出命令

:w:保存修改

:w newfile:另存为

:wq:保存修改并退出

ZZ:保存修改并退出,快捷键

:q!:不保存修改退出

:wq!:保存修改并退出(root 及所有者可用)

Vim 使用技巧

:r !命令:导入命令的执行结果

:r 文件:导入文件内容

:!命令:暂时退出 vim 执行命令

:map 快捷键 操作:自定义快捷键,如 Cril+/ 注释一行::map ^_ I# <ESC>

:n1,n2s/^/# /g:连续注释行

:n1,n2s/# //g:取消注释行

:n1,n2s/^# //g:只取消行首的 #

:1,5s/^/\/\//g// 注释

:1,5s/^\/\///g:取消行首 // 注释

:ab mymail 10001@qq.com:替换字符,输入 mymail 即可替换

Vim 配置文件

配置文件:~/.vimrc

编写时前面不用加 :

1