Bash 的使用技巧
约 2447 个字 362 行代码 26 张图片 预计阅读时间 13 分钟
变量
使用、设置、取消
# 使用
echo $变量名
# 或
echo ${ 变量名 }
# 设置(等号两边不能加空格)
变量名 = 值
# 取消
unset 变量名
设置规则
变量名开头不能是数字
通常系统变量为大写,自行设置变量一般用小写(建议)
变量值可以用双引号或单引号包裹,以在其中包含空格等字符
双引号内的特殊字符可以保存原有特性
单引号内的特殊字符仅为一般字符,不能再套单引号
特殊字符的转义用反斜杠
需要借别的命令提供信息时,可以使用 `命令`
或 $(命令)
(建议)
需要使用其他变量内容,或者是扩增自身内容,可以使用 ${变量名}
(建议)或 "$变量名"
设置变量 - 例
ding@ding-server:~$ echo "The $( uname) core version is: $( uname -r) "
The Linux core version is: 5 .4.0-109-generic
ding@ding-server:~$ echo 'The $(uname) core version is: $(uname -r)'
The $( uname) core version is: $( uname -r)
ding@ding-server:~$ name = Ding\ Junyao
ding@ding-server:~$ echo $name
Ding Junyao
ding@ding-server:~$ name = ${ name } \' s\ name
ding@ding-server:~$ echo $name
Ding Junyao's name
ding@ding-server:~$ name=' This is '${name}
ding@ding-server:~$ echo $name
This is Ding Junyao' s name
变量作用范围
环境变量的作用范围为当前环境。
通过前面的流程设置的变量的作用范围为当前进程,在子进程中无法使用。如果需要在子进程中使用,需要设置为环境变量:
直接定义环境变量:
例:
# 在父进程
ding@ding-server:~$ echo $name
This is Ding Junyao's name
ding@ding-server:~$ bash
# 至此离开父进程,进入子进程 1
ding@ding-server:~$ echo $name
ding@ding-server:~$ exit
exit
# 至此离开子进程 1,到父进程
ding@ding-server:~$ echo $name
This is Ding Junyao' s name
ding@ding-server:~$ export name
ding@ding-server:~$ bash
# 至此离开父进程,进入子进程 2
ding@ding-server:~$ echo $name
This is Ding Junyao' s name
经此前 export
配置,这时候的变量仅在当前登录终端有效。如果想要永久有效,需要更改配置文件,在其中添加设置变量的指令。
Bash 的 Login Shell 的配置文件结构如下图,需按需更改对应的配置文件。这些配置文件均为 Shell 脚本。
Login Shell 的配置文件读取过程(bash)
配置后,下次登录时即生效。如果想立即生效,执行:
source 配置文件路径
# 或
. 配置文件路径
查看目前的环境变量
ding@ding-server:~$ env
SHELL = /bin/bash
JAVA_HOME = /usr/lib/jvm/java-11-openjdk-amd64/
PWD = /home/foxconn
LOGNAME = foxconn
HOME = /home/foxconn
LANG = en_US.UTF-8
LESSOPEN = | /usr/bin/lesspipe %s
USER = foxconn
PATH = /usr/lib/jvm/java-11-openjdk-amd64//bin:/usr/lib/jvm/java-11-openjdk-amd64//bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
DBUS_SESSION_BUS_ADDRESS = unix:path= /run/user/1000/bus
SSH_TTY = /dev/pts/0
_ = /usr/bin/env
...
set
PS1 = '\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
PS2 = '> '
PS4 = '+ '
...
?
- 上一个命令的返回值
ding@ding-server:~$ ls
0507 a.txt
...
foxconn@hcm0153:~$ echo $?
0
foxconn@hcm0153:~$ ls 11 .txt
ls: cannot access '11.txt' : No such file or directory
foxconn@hcm0153:~$ echo $?
2
foxconn@hcm0153:~$ echo $?
0
一般返回值为 0
表示命令执行成功,非 0
表示执行失败
别名
# 设置别名(虽然可以用双引号包裹,但还是建议用单引号)
alias 别名 = '命令'
# 查看目前使用中的别名
alias
# 删除别名
unalias 别名
别名的作用范围和变量一样,如果想永久保存别名,同样需要更改配置文件。
例
每次删除时都要进行确认
查看别名
ding@ding-server:~$ alias
alias grep = 'grep --color=auto'
alias l = 'ls -CF'
alias la = 'ls -A'
alias ll = 'ls -alF'
alias ls = 'ls --color=auto'
一条危险的命令
连续执行多条命令
多个命令之间用 ;
连接,表示无条件分步执行。
&&
和 ||
有“且”和“或”的意义。
连续执行两条命令的连接符和流程图
例
# 如果目录不存在则新建
ls 目录路径 || mkdir -p 目录路径
# 软件包安装成功才运行程序
sudo apt install 软件包 && 程序
# 尝试新建文件(文件所在目录可能不存在)
ls 目录路径 || mkdir -p 目录路径 && touch 文件路径
假设判断式
判断文件是否存在,如存在打印 "exist
",否则打印 "not exist
"。
答案是:
ls 文件 && echo "exist" || echo "not exist"
如果写成了
ls 文件 || echo "not exist" && echo "exist"
则如果文件不存在:
第一条语句返回非 0 值
执行第二条语句,返回 0
执行第三条语句
结果如下:
ls: cannot access '文件' : No such file or directory
not exist
exist
格式
其中命令 2、3 会使用肯定成功的命令。
等价于:
假设判断式等价流程图
流程图如下:
假设判断式流程图
如果命令 1 返回值为 0
(成功):
命令 1 返回值为 0 时的流程图
如果命令 1 返回值非 0
(失败):
命令 1 返回值非 0 时的流程图
数据流重定向
数据流分类
数据流分类
各部分示例
数据流各部分示例
将标准输出重定向到文件
一般来说是将内容输出到文件。
>
和 >>
的差别:如果文件已存在,>
会清空原有内容,>>
会在原有内容之后累加。
例:
ding@ding-server:~$ ls -al /
total 4194436
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 .
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 ..
lrwxrwxrwx 1 root root 7 Feb 23 16 :49 bin -> usr/bin
...
ding@ding-server:~$ ls -al / > test.txt
ding@ding-server:~$ cat test.txt
total 4194436
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 .
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 ..
lrwxrwxrwx 1 root root 7 Feb 23 16 :49 bin -> usr/bin
...
ding@ding-server:~$ ls -al / >> test.txt
ding@ding-server:~$ cat test.txt
total 4194436
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 .
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 ..
lrwxrwxrwx 1 root root 7 Feb 23 16 :49 bin -> usr/bin
...
total 4194436
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 .
drwxr-xr-x 21 root root 4096 Apr 13 15 :25 ..
lrwxrwxrwx 1 root root 7 Feb 23 16 :49 bin -> usr/bin
...
...
重定向到设备
重定向到设备时,一般不要用累加符号。
传到另一个终端:
传到另一个终端
传到黑洞设备:
标准错误输出重定向
命令 2 > 路径
命令 2 >> 路径
# 将标准输出和标准错误输出放到不同的地方(> 可以按需换成 >>)
命令 > 标准输出路径 2 > 标准错误输出路径
# 将标准输出和标准错误输出放到相同的地方,不能使用上面的方法,会导致交叉写入,造成次序混乱;用下面的方法:
命令 > 路径 2 >& 1 # 较常用;注意顺序
# 或
命令 & > 路径
例:
标准错误输出重定向 - 例
标准输入重定向
将某内容传入命令的标准输入:
对命令的标准输入传入一段内容,自定义结束符号(默认为 Ctrl + D):
命令 << 结束符号
> 内容
> 内容...
> 结束符号
前面的符号是在终端输入时自动添加的,不是输入内容;如果写脚本,直接写内容和结束符号就行了。
例:
标准输入重定向 - 例
管道
形式
命令1 | 命令2
命令1 | 命令2 | 命令3
...
管道示意图
标准错误输出与管道
当需要传递标准错误输出时,可以利用重定向。
将标准输出和标准错误输出一并传入下一个命令
将标准输出和标准错误输出一并传入下一个命令 - 示意图
忽略标准输出,只将标准错误输出传入下一个命令
命令1 2 >& 1 > /dev/null | 命令2
# 注意顺序
忽略标准输出,只将标准错误输出传入下一个命令 - 示意图
例:
标准错误输出与管道 - 例
grep
- 输出包含要找的文字所在的行
grep [ 选项] 要找的文字 文件
... | grep [ 选项] 要找的文字
# 支持标准输入的命令都有类似的形式,后续如无特殊情况不再写出
选项:
-c
:输出符合条件的行的数量
-i
:忽略大小写
-n
:在符合条件的行前输出行号
-v
:输出不符合条件的行
--color=auto
:高亮显示要找的文本(一般会使用别名默认启用该功能)
例
grep - 例 1
grep - 例 2
cut
- 截取每一行的特定文本
# 在以某字符为分隔的数据中,取出某段文本
cut -d '分隔字符' -f 要取出的段的序号 文件
# 以字符为单位取出文本
cut -c 字符序号区间 文件
序号从 1 开始。
例
ding@ding-server:~$ echo $PATH
/home/foxconn/.local/bin:/home/foxconn/.local/bin:/usr/lib/jvm/java-11-openjdk-amd64//bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
ding@ding-server:~$ echo $PATH | cut -d ':' -f 2 ,4,5
/home/foxconn/.local/bin:/usr/local/sbin:/usr/local/bin
ding@ding-server:~$ echo $PATH | cut -d ':' -f 2
/home/foxconn/.local/bin
ding@ding-server:~$ echo $PATH | cut -d ':' -f 2 -4
/home/foxconn/.local/bin:/usr/lib/jvm/java-11-openjdk-amd64//bin:/usr/local/sbin
ding@ding-server:~$ echo $PATH | cut -d ':' -f 5 -
/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
ding@ding-server:~$ echo $PATH | cut -d ':' -f -3
/home/foxconn/.local/bin:/home/foxconn/.local/bin:/usr/lib/jvm/java-11-openjdk-amd64//bin
ding@ding-server:~$ cat /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
...
ding@ding-server:~$ cut -d ':' -f 1 ,6 /etc/passwd
root:/root
daemon:/usr/sbin
bin:/bin
...
ding@ding-server:~$ export
declare -x CLASSPATH = ".:/usr/lib/jvm/java-11-openjdk-amd64//lib/dt.jar:/usr/lib/jvm/java-11-openjdk-amd64//lib/tools.jar"
declare -x DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/user/1000/bus"
...
ding@ding-server:~$ export | cut -c 12 -
CLASSPATH = ".:/usr/lib/jvm/java-11-openjdk-amd64//lib/dt.jar:/usr/lib/jvm/java-11-openjdk-amd64//lib/tools.jar"
DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/user/1000/bus"
...
限制
如果列之间用了多个空格,则不能使用该命令去提取。
ding@ding-server:~$ last
foxconn pts/3 10 .94.5.157 Tue May 17 14 :11 still logged in
foxconn pts/2 10 .94.5.157 Tue May 17 10 :42 still logged in
foxconn pts/4 10 .94.0.112 Tue May 17 09 :07 - 10 :10 ( 01 :03)
...
ding@ding-server:~$ last | cut -d ' ' -f 1
foxconn
foxconn
foxconn
foxconn
ding@ding-server:~$ last | cut -d ' ' -f 2
...
sort
- 排序
选项:
-f
:忽略大小写
-b
:忽略最前面的空格
-M
:以月份名称(JAN
、DEC
...)排序
-n
:使用纯数字排序(默认是以文字形式排序的)
-r
:反向排序
-u
:去重
-t 分隔符号
:指定分隔符号(默认是 Tab )
-k 段序号或区间
:以哪个 / 哪些段排序(用法和 cut
一样)
-c
:检查文件是否有序
例:
ding@ding-server:~$ sort /etc/passwd
admin:x:1002:1002:admin,,,:/home/admin:/bin/bash
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
...
ding@ding-server:~$ sort -t ':' -k 3 /etc/passwd
root:x:0:0:root:/root:/bin/bash
foxconn:x:1000:1000:foxconn:/home/foxconn:/bin/bash
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
J6100354:x:1001:1001::/home/J6100354:/bin/sh
...
ding@ding-server:~$ sort -t ':' -k 3 -n /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
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
ding@ding-server:~$ sort -t ':' -k 3 -n -r /etc/passwd
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
admin:x:1002:1002:admin,,,:/home/admin:/bin/bash
...
uniq
- 去重
只会在连续的范围内去重。
选项:
-i
:忽略大小写
-c
:计数
-u
:仅输出无重复的行
例:
ding@ding-server:~$ last | cut -d ' ' -f 1 | uniq
foxconn
reboot
foxconn
reboot
foxconn
...
ding@ding-server:~$ last | cut -d ' ' -f 1 | sort | uniq
foxconn
reboot
wtmp
ding@ding-server:~$ last | cut -d ' ' -f 1 | sort | uniq -c
1
198 foxconn
12 reboot
1 wtmp
wc
- 字数统计
如果有多个文件,会在最下方列出总和。
选项:
无选项:列出行数、词数、字节数和文件名(如有)
-l
:多少行
-w
:多少词(以英文的方式判定)
-c
:多少字节
-m
:多少字符(换行符也包含在内)
-L
:列出最长一行的字符长度
例:
ding@ding-server:~$ cat wctest.txt
This is some text.
这是一段文本示例。
123456
ding@ding-server:~$ wc wctest.txt
3 6 54 wctest.txt
ding@ding-server:~$ wc -m wctest.txt
36 wctest.txt
ding@ding-server:~$ last | cut -d ' ' -f 1 | grep [ a-zA-Z] | grep -v 'wtmp' | grep -v 'reboot' | grep -v 'unknown' | sort | uniq | wc -l # 显示登录过的用户数量
2
tee
- 双向重定向
tee 示意图
选项:
- -a
:追加
常用在两个命令之间:
tee 示意图 - 管道
例:
ding@ding-server:~$ last | tee last.txt | cut -d ' ' -f 1 | sort | uniq
foxconn
reboot
wtmp
ding@ding-server:~$ cat last.txt
foxconn pts/3 10 .94.5.157 Tue May 17 14 :11 still logged in
foxconn pts/2 10 .94.5.157 Tue May 17 10 :42 still logged in
foxconn pts/4 10 .94.0.112 Tue May 17 09 :07 - 10 :10 ( 01 :03)
...
历史记录
查看和执行历史命令
执行 history
可以查看最近的命令。如果限定查看最近的若干条命令,可以加上数字作为参数。
Bash 记录历史命令时,会把命令里面的字符记录进去,所以一般不要在命令中写密码等敏感信息 。
ding@ding-server:~$ history
1216 sudo docker-compose -f docker-compose-mysql.yml down
...
2213 sudo whoami
2214 whoami
2215 history
执行历史命令时,使用 !
加上一些参数,例子见下。
执行时,首先会列出将要执行的命令。
ding@ding-server:~$ !2214 # 执行历史记录中的第 2214 条命令
whoami
ding
ding@ding-server:~$ !sudo # 执行开头为 sudo 的最近的命令
sudo whoami
root
ding@ding-server:~$ !2214
whoami
ding
ding@ding-server:~$ !! # 执行最近一条命令
whoami
ding
ding@ding-server:~$ sudo !!
sudo whoami
root
历史记录的操作
# 立即将目前的数据写入历史记录文件(默认为 ~/.bash_history)
history -w
# 查看历史记录保留的最大条数
echo ${ HISTSIZE }
# 清除当前用户的历史记录
history -c
Shell 相关
Shell 的概念与作用
Shell(壳层、壳程序) 是为用户提供用户界面的软件。能够操作应用程序。相对于内核(Kernel)而言。
有命令行和图形界面之分:
命令行(狭义的 Shell):bash、cmd、Powershell…
图形界面:Gnome、KDE、Windows Explorer…
Shell 在计算机体系中的位置
查看 Shell 相关信息
以下提供了两个系统中两种 Shell 的操作,操作分别为:
查看当前 Shell
查看可用的 Login Shell (CentOS 中也可以使用 chsh -l
查看)
查看 sh
被指定为哪个 Shell
# CentOS Linux release 7.9.2009, fish
# 查看当前 Shell
ding@ding-server ~> echo $SHELL
/usr/bin/fish
# 查看可用的 Login Shell
ding@ding-server ~> cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
/bin/zsh
/usr/bin/fish
# 查看 sh 被指定为哪个 Shell
ding@ding-server ~> ll /bin/sh
lrwxrwxrwx. 1 root root 4 Jul 15 2021 /bin/sh -> bash*
# Ubuntu 20.04 LTS, bash
# 查看当前 Shell
ding@ding-server:~$ echo $SHELL
/bin/bash
# 查看可用的 Login Shell
ding@ding-server:~$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/usr/bin/tmux
/usr/bin/screen
# 查看 sh 被指定为哪个 Shell
ding@ding-server:~$ ll /bin/sh
lrwxrwxrwx 1 root root 4 Feb 23 16 :50 /bin/sh -> dash*
sh、bash、dash
通常的 Shell 脚本,默认会以 sh
执行。
传统意义上的 sh 指 Bourne Sh ell,由 AT&T 公司的 Steve Bourne 开发,是 UNIX 系统上的标准 Shell。
Linux 上一般会指向 bash(B ourne A gain Sh ell),属于 GNU 计划的一部分。
但 Debian 发行版中指向的是 dash(D ebian A lmquist Sh ell),由 ash(A lmquist Sh ell ,BSD 等上的预装 Shell) 发展而来,更为小巧。
dash 对一些命令的支持与 bash 不同(如 echo
命令不支持 -e
选项),故写、执行脚本时要注意。
部分 Borne Shell 兼容 Shell 的发展历程(箭头只代表派生关系)
内部(built-in)命令与外部命令
内部命令指 Shell 程序自带的命令;外部命令指其他程序提供的命令。
内部命令的列表可以通过 enable
查看。
判断命令属于哪种,可以用 type [-a]
命令
内部命令与外部命令名称相同时,优先执行内部命令(如 pwd
)。
各 Shell“对命令的支持不同”,一般指的是内部命令。
fish、zsh
F riendly I nteractive Sh ell
Z Sh ell
有一些优化(如自动补全、高亮),适合用于个人工作。可以参考:
通过第三方插件,能够实现更好的效果。
但配置文件、脚本语法等和 bash 有不可忽视的差别,尤其是配置文件。
macOS 的终端默认使用 zsh。
fish 一例
zsh 安装 Oh My Zsh 插件后的效果一例
csh、tcsh
Linux 中如果有 csh( C sh ell ),一般指向 tcsh(T enex C sh ell),有补全等功能。
bash 的一些功能(如历史记录、别名)受其影响。
与 bash 的语法和配置文件差别很大。
现在很少用。
tcsh 一例
nologin
打印账号不可用的信息(可通过 /etc/nologin.txt
配置),返回非零值。
用于不可登录的用户:给其配置默认 Shell 为它,使其不可通过 Shell 登录。
ding@ding-server:~$ /sbin/nologin
This account is currently not available.
ding@ding-server:~$ echo $?
1
ding@ding-server:~$ cat /etc/passwd
...
games:x:5:60:games:/usr/games:/usr/sbin/nologin
...
ding@ding-server:~$ sudo su - games
This account is currently not available.
ding@ding-server:~$
rbash
是受限制的 bash,严格意义上来说是 bash 的特殊功能,相当于 bash -r
。
在 Debian 系发行版中,rbash
指向 bash
(但实际效果不一样);如果没有,可以创建符号链接。
可以用作中转服务器,或者仅使用 ssh 来访问网页等等。
受限的情况:https://www.howtoing.com/rbash-a-restricted-bash-shell-explained-with-practical-examples/
但不绝对安全,可以通过运行 bash
等方式脱离控制。
screen(一种终端复用工具)
创建会话:
离开会话:按 Ctrl + A ,再按 D
查看会话:
ding@ding-server:~$ screen -ls
There is a screen on:
1524444 .pts-1.hcm0153 ( 05 /19/2022 03 :02:27 PM) ( Detached)
1 Socket in /run/screen/S-foxconn.
重进上面的会话:
tmux(另一种终端复用工具)
默认前缀键:Ctrl + B
左右分屏:前缀 + %
上下分屏:前缀 + "
切换:前缀 + 方向键
调整:前缀 + + + 方向键
关闭:前缀 + x
...
tmux 一例
部分 Shell 和其他工具的关系
部分 Shell 和其他工具的关系
参考资料
鸟哥的Linux私房菜. 基础学习篇 / 鸟哥著 ; Linux 中国繁转简. -- 4版. -- 北京 : 人民邮电出版社, 2018.3; ISBN 978-7-115-47258-8
Linux就该这么学 / 刘遄著. -- 北京 : 人民邮电出版社, 2017.11; ISBN 978-7-115-47031-7
Linux 教程 | 菜鸟教程
Bourne shell - 维基百科,自由的百科全书
Almquist shell - Wikipedia
Bash - 维基百科,自由的百科全书
ash variants
Z shell - 维基百科,自由的百科全书
ohmyzsh/ohmyzsh: 🙃 A delightful community-driven (with 2,000+ contributors) framework for managing your zsh configuration. Includes 300+ optional plugins (rails, git, macOS, hub, docker, homebrew, node, php, python, etc), 140+ themes to spice up your morning, and an auto-update tool so that makes it easy to keep up with the latest updates from the community.
By Msnicki (talk) 03:56, 17 March 2015 (UTC) - This image is my own work, created by me on my personal MacBook and cropped to eliminate Apple's proprietary UI elements at the top and bottom of the screen in response to objections to the original full image discussed at en:File talk:TcshAndShScreenCapture.png., CC0, https://commons.wikimedia.org/w/index.php?curid=60793152
rbash - 一个受限的Bash Shell用实际示例说明
Linux screen命令 | 菜鸟教程
C Shell - 维基百科,自由的百科全书
Linux® 用户的 FreeBSD 快速入门向导 | FreeBSD Documentation Portal
KornShell - 维基百科,自由的百科全书