基础
!!
可以执行上一条命令Ctrl+/
可以强行终止进程(在Ctrl+C
不管用时可以试试)一些快捷键
| 快捷键 | 含义 |
| :———: | :——————————————— |
|Ctrl + A
| 跳转到命令的开始位置 |
|Ctrl + E
| 跳转到命令的结尾位置 |
|Ctrl + K
| 清除从当前位置到命令末尾的所有字符 |
|Ctrl + R
| 向后搜索(输入关键字,重复输入可跳转到下一个) |
|Ctrl + T
| 颠倒当前位置与前一个位置的字符 |
|Ctrl + L
| 清除屏幕 |
|Ctrl + ->
| 以单词为单位跳转 |
单引号中的字符不会被转义,变量也不会被解释;双引号则不同。
echo
命令的参数最好带上双引号:1
2str="four spaces"
echo $str # output: four spaces原因是
$str
会被解释成两个字符串,并作为参数传给echo
,而加上双引号则指明这是一个字符串。一些重要的配置文件
~/.bash_profile
:用户专有的配置文件,只在用户登录时执行一次,调用~/.bashrc
~/.bashrc
:用户专有的配置文件,每次启动bash
的时候执行/etc/profile
:所有用户登录时执行的文件
Shell编程
source *.sh
也可以执行程序,其原理是读取每一行并在当前终端执行,而不是创建子进程来执行优先级从高到低:
- 别名
- 关键字
- 函数
- 内置命令
- 脚本和可执行函数
一些变量(只读)
$0
:脚本名$1
:第一个参数$*
:所有参数组成的单一字符串,以IFS
的第一个字符为分隔符$@
:参数列表,即多个字符串,等价于"$1""$2""$3"...
$#
:参数个数$?
:最后一个命令的退出状态
字符串替换操作符
命令 | 解释 |
---|---|
${varname:-word} |
返回默认值 |
${varname:=word} |
设置varname 默认值 |
${varname:?message} |
为空时输出报错信息,message 可以为空 |
${varname:+word} |
若为空,返回word ,否则返回空 |
${varname:offset:length} |
返回从offset 开始长度为length 的子串。:length 可省略,则子串一直到末尾。若offset 小于0,offset 为字符串末尾。若offset 为@ ,length 为从参数offset 开始的参数数目 |
- 字符串模式匹配操作符
命令 | 解释 |
---|---|
${variable#pattern} |
删除开头的最短匹配部分 |
${variable##pattern} |
删除开头的最长匹配部分 |
${variable%pattern} |
删除结尾的最短匹配部分 |
${variable%%pattern} |
删除结尾的最长匹配部分 |
${variable/pattern/string} |
使用string 替换匹配到的第一个字符串,pattern 中可以以# 或% 开头 |
${variable//pattern/string} |
替换所有的字符串 |
- 字符串长度操作符
命令 | 解释 |
---|---|
${<井号>variable} |
返回variable 的长度,如果variable 为'yfzm' ,则返回4 |
if
语句的condition
的实质是命令的退出状态,类似c
语义的条件语句实际上是内置语句test
的退出状态。[#condition#]
等价于test condition
,test
函数的特殊之处在于其退出状态被设置为condition
的值(真或假)判断字符串是否为空的陷阱:
1
2
3if [ -n $variable ]; then
...
fi这种写法是错误的,如果
$variable
中有分隔符,将被扩展为多个字符串,导致报出参数过多的错误$variable
不存在,语句变为if [ -n ]
,恒为真
因此应该使用双引号:
1
2
3if [ -n "$variable" ]; then
...
fi一些常见的操作符
操作符 | 含义 | ||
---|---|---|---|
-n str |
str 非空 |
||
-z str |
str 为空 |
||
-e file |
file 存在 |
||
-d file |
file 存在且为目录 |
||
-f file |
file 存在且为普通文件 |
||
-[rwx] file |
对file 有读/写/执行权限 |
||
-s file |
file 存在且非空 |
||
-[OG] file |
当前用户是file 的所有者/当前用户在file的用户组中 |
||
file 1 -nt file2 |
file1 比file2 新(指的是修改时间) |
||
file 1 -ot file2 |
file1 比file2 旧 |
||
-a |
&& (仅用于test 条件表达式,test 中不能用&& ) |
||
-o |
` | ` | |
-{lt, le, eq, ge, gt, ne} |
算数比较 |
*
表示当前目录下的所有文件名,这也是一种模式匹配 (*
可以匹配任意字符串)!?
也有类似的效果。IFS
指定分隔符。getopts
示例代码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# 创建文件名为 filename 的脚本
usage_prompt="Usage: ${0##*/} [-fh] filename"
# getopts的第一个参数开始的冒号表示忽略错误输出
while getopts ':fh' opt; do
case $opt in
f ) f_flag=y ;;
h ) echo "$usage_prompt"
echo " -f: force to overwrite file"
echo " -h: help"
exit 0 ;;
\? ) echo "$usage_prompt"
exit 3 ;;
esac
done
shift $(($OPTIND - 1)) # shift已经解析的部分
# 其它正常的代码
if [ -z "$1" ]; then
echo "$usage_prompt"
exit 1
fi
if [ -e "$1" ]; then
if [ -z "$f_flag" ]; then
echo "error: file $1 already exists."
echo "(hint: use -f to overwrite)"
exit 2
fi
rm $1
fi
touch $1
chmod u+x $1
code $1算术测试结果和常理相反,如果为真返回
0
,为假返回1
。使用算术测试可以直接使用(( ... ))
,这样可以避免-ge
等符号和对括号的转义。而如果使用$(( ... ))
,表达式为真时返回1
,为假时返回0
。使用数组:
1
2
3
4
5a=(apple [3]=banana pear)
# a[0]=apple a[3]=banana a[4]=pear
# a[*]="apple banana pear"
# a[@]="apple banana pear"
# #a[@]=3