Bash 脚本在 linux 上还是非常有用的. 以前没有仔细用过, 这些结合教程, 好好学习了一遍, 顺便做些笔记, 也当作自己的快速参考指南.
当前所看的教程是 Bash 脚本教程.
Shell 可以理解为与内核交互的环境, 同时也是一个命令解释器.
查看当前时使用的 Shell
echo $SHELL
查看当前系统里已安装的所有 Shell
cat /etc/shells
进入和退出 Bash
bash
exit
查看 Bash 版本
bash --version
echo $BASH_VERSION
echo 是一个非常基础的输出命令, 用于在屏幕上输出一行文本.
一些常见的用法如下:
# 常见用法
echo hello world
# -n 取消行尾的换行符
echo -n hello world
# -e 解释引号中的特殊字符
echo -e "hello\nworld"
;
作为命令的结束符. 借助分号可以在一行中使用多个命令.&&
和 ||
可以控制多个命令的发生关系.
Command1 && Command2
在第一个命令成功后, 继续运行第二个命令Command1 || Command2
在第一个命令失败后, 继续运行第二个命令type
获取命令来源.Bash 中的一些快捷键, 非常基础且实用, 建议速记:
模式扩展是指 Bash 会将用户命令中的特殊字符扩展为完整的定义, 然后才继续执行对应的命令. 类似于简化版的 正则表达式
.
Bash 共有 8 种扩展:
~
字符扩展?
字符扩展*
字符扩展在 Bash 中打开或关闭扩展
# 打开模式扩展
set +o noglob
set +f
# 关闭扩展
set -o noglob
set -f
~
会自动扩展为当前用户的主目录?
表示文件路径中的任意单个字符, 不包括空格*
表示文件路径中的任意数量的任意字符, 包括零个字符[...]
方括号扩展匹配括号中的任意一个字符[start-end]
方括号扩展的简写形式, 表示一个连续的范围{.,.,.}
大括号扩展表示大括号中的所有值, 使用逗号分隔值. 逗号前后不能有空格. 可以用于多字符的匹配.{start..end}
大括号的简写模式, 表示一个连续的范围. 支持逆序.$
开头的词元视为变量. 变量也可以用 %{}
形式.$(...)
可以扩展为另一个命令的运行结果.$((...))
可以扩展为整数运行的结果.用于文件路径匹配时, 需要文件确实存在时才会扩展. 否则会原样输出
文件名扩展时只适用于单层路径, 不能跨目录匹配. Bash4.0 之后可以用 **
匹配零个或多个子目录.
一些示例:
echo ~
ls ?.txt
ls *.txt
ls [ab].txt
ls [a-c].txt
echo {1,2,3}
echo {a..c}
echo $SHELL
echo $(date)
echo $((2 + 2))
[[:class:]]
表示一个字符类. 常用的如下:
[[:alnum:]]
匹配任意英文字母与数字[[:alpha:]]
匹配任意英文字母[[:blank:]]
空格和 Tab 键。[[:cntrl:]]
ASCII 码 0-31 的不可打印字符。[[:digit:]]
匹配任意数字 0-9。[[:graph:]]
A-Z、a-z、0-9 和标点符号。[[:lower:]]
匹配任意小写字母 a-z。[[:print:]]
ASCII 码 32-127 的可打印字符。[[:punct:]]
标点符号(除了 A-Z、a-z、0-9 的可打印字符)。[[:space:]]
空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)[[:upper:]]
匹配任意大写字母 A-Z。[[:xdigit:]]
16 进制字符(A-F、a-f、0-9)。量词语法用于控制模式匹配的次数, 只有在 Bash 的 extglob
参数打开的情况下使用.
?(pattern-list)
匹配零个或一个模式。*(pattern-list)
匹配零个或多个模式。+(pattern-list)
匹配一个或多个模式。@(pattern-list)
只匹配一个模式。!(pattern-list)
匹配零个或一个以上的模式,但不匹配单独一个的模式。shopt
可以调整 Bash 的行为.
# 打开某个参数
$ shopt -s [optionname]
# 关闭某个参数
$ shopt -u [optionname]
# 查询某个参数关闭还是打开
$ shopt [optionname]
**
可以匹配零个或多个子目录\
进行转义.$
, 反引号 `
, 和反斜杆 \
会被自动扩展.使用 Here 文档
可以输入多行字符串.
<< token
text
text
token
其中 << token
是开始标记, token
是结束标记. Here 文档中会发生变量替换, 同时支持反斜杆转义, 但不支持通配符扩展. << token
中的空格可有可无.
Here 文档的一个变体是 <<<
, 将字符串通过标准输入传递给命令.
cat <<< 'hi there'
使用 env
或 printenv
显示环境变量
声明变量
variable=value
myvar="hello world"
读取变量: 在变量名前加上 %
字符
foo=bar
echo $foo
删除变量, 使用 unset
.
unset NAME
输出变量, 使用 export
. export
用于向子 Shell 输出环境变量.
NAME=foo
export NAME
特殊变量:
$?
上一个命令的退出码$$
当前 Shell 的进程 ID$_
上一个命令的最后一个参数$!
最近一个后台执行的异步命令的进程 ID$0
当前 Shell 的名称或脚本名$-
当前 Shell 的启动参数$@
和 $#
脚本的参数数量$1 - $9
表示脚本的第 1 - 9 个参数设置变量的默认值:
${varname:-word}
varname 存在且不为空返回 varname, 否则返回 word. (保证有默认值兜底)${varname:=word}
varname 存在且不为空返回 varname, 否则将它设置为 word 并返回 word. (设置变量的默认值)${varname:+word}
varname 存在且不为空返回 word, 否则返回空值. (测试变量是否存在)${varname:?message}
varname 存在且不为空返回 varname, 否则打印出 varname:message
并中断脚本执行 (防止变量未定义)一些其他用于声明变量的命令:
declare
声明特殊类型的变量. declare OPTION VARIABLE=value
-a
声明数组变量。-f
输出所有函数定义。-F
输出所有函数名。-i
声明整数变量。-l
声明变量为小写字母。-p
查看变量信息。-r
声明只读变量。-u
声明变量为大写字母。-x
该变量输出为环境变量。readonly
声明一个只读的变量.let
声明变量时, 可以直接执行算术表达式${#varname}
获取字符串长度${varname:offset:length}
提取子字符串. offset 从 0 开始计算.${varname^^}
转换为大写形式${varname,,}
转换为小写形式检查字符串的开头是否匹配给定的模式. 匹配成功后会删除匹配成功的部分, 返回剩余的部分. 不改变原始变量.
# 如果 pattern 匹配变量 variable 的开头,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable#pattern}
# 如果 pattern 匹配变量 variable 的开头,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable##pattern}
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
${variable%pattern}
# 如果 pattern 匹配变量 variable 的结尾,
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
${variable%%pattern}
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,但仅替换第一个匹配
${variable/pattern/string}
# 如果 pattern 匹配变量 variable 的一部分,
# 最长匹配(贪婪匹配)的那部分被 string 替换,所有匹配都替换
${variable//pattern/string}
((...))
整数的算术运算, 使用 $((...))
读取算术运算的结果number
十进制0number
八进制0xnumber
十六进制base#number
base 进制$((...))
支持二进制的位运算
<<
位左移运算,把一个数字的所有位向左移动指定的位。>>
位右移运算,把一个数字的所有位向右移动指定的位。&
位的“与”运算,对两个数字的所有位执行一个AND操作。|
位的“或”运算,对两个数字的所有位执行一个OR操作。~
位的“否”运算,对一个数字的所有位取反。^
位的异或运算(exclusive or),对两个数字的所有位执行一个异或操作。$((...))
支持的逻辑运算. 如果表达式为真, 返回 1, 否则返回 0.
<
小于>
大于<=
小于或相等>=
大于或相等==
相等!=
不相等&&
逻辑与||
逻辑或!
逻辑否expr1?expr2:expr3
三元条件运算符。若表达式expr1的计算结果为非零值(算术真),则执行表达式expr2,否则执行表达式expr3。$((...))
可以执行赋值运算.
parameter = value
简单赋值。parameter += value
等价于parameter = parameter + value。parameter -= value
等价于parameter = parameter – value。parameter *= value
等价于parameter = parameter * value。parameter /= value
等价于parameter = parameter / value。parameter %= value
等价于parameter = parameter % value。parameter <<= value
等价于parameter = parameter << value。parameter >>= value
等价于parameter = parameter >> value。parameter &= value
等价于parameter = parameter & value。parameter |= value
等价于parameter = parameter | value。parameter ^= value
等价于parameter = parameter ^ value。$((...))
内逗号,
是求值运算符, 执行前后两个表达式, 并返回后一个表达式的值.expr
命令支持算术运算, 可以不使用 ((...))
.这一节可以极大加速命令行使用中的操作速度, 建议速记.
Bash 内置了 Readline 库, 提供了很多行操作.
光标移动
Ctrl + a
移到行首。Ctrl + b
向行首移动一个字符,与左箭头作用相同。Ctrl + e
移到行尾。Ctrl + f
向行尾移动一个字符,与右箭头作用相同。Alt + f
移动到当前单词的词尾。Alt + b
移动到当前单词的词首。清除屏幕使用 Ctrl + l
, 和 clear
命令作用相同.
编辑操作
Ctrl + d
删除光标位置的字符(delete)。Ctrl + w
删除光标前面的单词。Ctrl + t
光标位置的字符与它前面一位的字符交换位置(transpose)。Alt + t
光标位置的词与它前面一位的词交换位置(transpose)。Alt + l
将光标位置至词尾转为小写(lowercase)。Alt + u
将光标位置至词尾转为大写(uppercase)。
Ctrl + k
剪切光标位置到行尾的文本。
Ctrl + u
剪切光标位置到行首的文本。
Alt + d
剪切光标位置到词尾的文本。
Alt + Backspace
剪切光标位置到词首的文本。
Ctrl + y
在光标位置粘贴文本。
Tab
完成自动补全。
Alt + ?
列出可能的补全,与连按两次 Tab 键作用相同。
Alt + /
尝试文件路径补全。
Ctrl + x /
先按Ctrl + x,再按/,等同于Alt + ?,列出可能的文件路径- 补全。
Alt + !
命令补全。
Ctrl + x !
先按Ctrl + x,再按!,等同于Alt + !,命令补全。
Alt + ~
用户名补全。
Ctrl + x ~
先按Ctrl + x,再按~,等同于Alt + ~,用户名补全。
Alt + $
变量名补全。
Ctrl + x $
先按Ctrl + x,再按$,等同于Alt + $,变量名补全。
Alt + @
主机名补全。
Ctrl + x @
先按Ctrl + x,再按@,等同于Alt + @,主机名补全。
Alt + *
在命令行一次性插入所有可能的补全。
Alt + Tab
尝试用.bash_history里面以前执行命令,进行补全。
Bash 会保留用户的操作历史, 退出 Shell 式, 会将操作历史写入都 ~/.bash_history
中.
echo $HISTFILE
!e
表示找出操作历史中, 最近的一条以 e
开头的命令并执行.
Bash 会先输出那条命令, 然后直接执行.
history
会显示操作历史. 可以通过定制 HISTTIMEFORMAT
来显示每个操作的时间.
Ctrl + p
显示上一个命令,与向上箭头效果相同(previous)。Ctrl + n
显示下一个命令,与向下箭头效果相同(next)。Alt + <
显示第一个命令。Alt + >
显示最后一个命令,即当前的命令。Ctrl + o
执行历史文件里面的当前条目,并自动显示下一条命令。这对重复执行某个序列的命令很有帮助。
!!
执行上一个命令。
!n
执行历史文件里面行号为n的命令。
!-n
执行当前命令之前n条的命令。
!string
执行最近一个以指定字符串string开头的命令。
!?string
执行最近一条包含字符串string的命令。
^string1^string2
执行最近一条包含string1的命令,将其替换成string2。
Ctrl + j
等同于回车键(LINEFEED)。
Ctrl + m
等同于回车键(CARRIAGE RETURN)。
Ctrl + o
等同于回车键,并展示操作历史的下一个命令。
Ctrl + v
将下一个输入的特殊字符变成字面量,比如回车变成^M。
Ctrl + [
等同于 ESC。
Alt + .
插入上一个命令的最后一个词。
Alt + _
等同于Alt + .。
cd -
进度前一次的目录pushd
进入目录, 并将该目录放入堆栈popd
移除堆栈顶部的记录, 并进入新的堆栈顶部目录dirs
显示目录堆栈的内容pushd
和 popd
支持的参数:
-n
仅操作堆栈, 不改变目录整数参数
该整数表示堆栈中的指定位置的记录. 从 0 开始.目录参数
pushd 可以接受一个目录作为参数, 表示将目录放到堆栈顶部, 并进入该目录脚本的第一行通常是指定解释器. 这一行以 #!
开头, 这个字符被称为 Shebang
.
#!/bin/bash
脚本通常需要执行权限, 使用 chmod 添加权限
chmod +x script.sh
如果需要脚本在任何地方都能执行,必须加到环境变量 $PATH
中.
比如将脚本统一放在 ~/bin
目录, 然后将其加入到 $PATH
中.
export PATH=$PATH:~/bin
# 可以将上面这行加入到 ~/.bashrc 中, 然后运行下面这句使得生效
source ~/.bashrc
#!/usr/bin/env NAME
让 Shell 查找环境变量中第一个匹配的 NAME.
使用 #
表示注释.
调用脚本时, 脚本后面可以带有参数.
$0
脚本文件名,即script.sh。$1~$9
对应脚本的第一个参数到第九个参数。$#
参数的总数。$@
全部的参数,参数之间使用空格分隔。$*
全部的参数,参数之间使用变量$IFS值的第一个字符分隔,默认为空格,但是可以自定义。${10}
脚本参数多于 10 以后, 可以用 ${}
形式shift
可以改变脚本参数, 每次执行都会移除脚本当前的第一个参数, 使得后面的参数前进一位.
getopts
可以解析复杂的脚本命令行参数, 获取所有带前置连词线 -
的参数.
getopts optstring name
optstring
给出脚本的所有连词线参数. 后面有冒号的表示有参数值.
name
是一个变量名, 用来保存当前提取到的配置项参数.
while getopts 'lha:' OPTION; do
case "$OPTION" in
l)
echo "linuxconfig"
;;
h)
echo "h stands for h"
;;
a)
avalue="$OPTARG"
echo "The value provided is $OPTARG"
;;
?)
echo "script usage: $(basename $0) [-l] [-h] [-a somevalue]" >&2
exit 1
;;
esac
done
shift "$(($OPTIND - 1))"
$OPTIND
在开始执行前是 1, 每次执行都会加 1.
$OPTIND - 1
是已经处理的连词线参数的个数, 使用 shift 将这些参数移除.
配置项参数终止符 --
, 可以让执行变量只能作为实体参数, 而不能作为配置项参数.
myPath="~/docs"
ls -- $myPath
exit
用于终止当前脚本的执行, 并向 Shell 返回一个退出值.
命令执行结束后, 有一个返回值. 0 表示成功, 非 0 表示失败.
用 $?
可以读取前一个命令的返回值.
source
用于执行一个脚本, 通常用于重载一个配置文件.
alias
可以为一个命令执行别名.
# 指定别名
alias NAME=DEFINITION
# 列出所有别名
alias
# 解除别名
unalias lt