Linux Shell及vi/vim使用
vi/vim 简单使用
概念
vi是unix下的编辑程序,vim由vi改编增强版。
现在一般用的是vim
vim有多种模式
- 普通模式(Normal mode)
在普通模式中,用的编辑器命令,比如移动光标,删除文本等等。这也是 Vim 启动后的默认模式。这正好和许多新用户期待的操作方式相反(大多数编辑器默认模式为插入模式)。
这种设置与其他文本编辑器不太一样,但这样的话可以更快移动光标
在普通模式中,有很多方法可以进入插入模式。比较普通的方式是按 a
(append/追加)键或者 i
(insert/插入)键。
- 插入模式(Insert mode)
在这个模式中,大多数按键都会向文本缓冲中插入文本。大多数新用户希望文本编辑器编辑过程中一直保持这个模式。
在插入模式中,可以按
ESC
键回到普通模式。
- 可视模式(Visual mode)
这个模式与普通模式比较相似。但是移动命令会扩大高亮的文本区域。高亮区域可以是字符、行或者是一块文本。当执行一个非移动命令时,命令会被执行到这块高亮的区域上。Vim 的”文本对象”也能和移动命令一样用在这个模式中。
- 选择模式(Select mode)
这个模式和无模式编辑器的行为比较相似(Windows 标准文本控件的方式)。这个模式中,可以用鼠标或者光标键高亮选择文本,不过输入任何字符的话,Vim 会用这个字符替换选择的高亮文本块,并且自动进入插入模式。
- 命令行模式(Command line mode)
在命令行模式中可以输入会被解释成并执行的文本。例如执行命令(
:
键),搜索(/
和?
键)或者过滤命令(!
键)。在命令执行之后,Vim 返回到命令行模式之前的模式,通常是普通模式。
- Ex 模式(Ex mode)
这和命令行模式比较相似,在使用 :visual
命令离开 Ex 模式前,可以一次执行多条命令。
模式切换
普通模式中按 i
(插入)或 a
(附加)键都可以进入插入模式,普通模式中按 :
进入命令行模式。命令行模式中输入 wq
回车后保存并退出 vim。
进入vim界面1
vim practice_1.txt
进入命令行模式后还可以使用 e:file 打开其他文件
游标移动
按键 | 说明 |
---|---|
h | 左 |
l | 右(小写 L) |
j | 下 |
k | 上 |
w | 移动到下一个单词 |
b | 移动到上一个单词 |
0 | 光标到行首 |
$ | 光标到行末 |
numG | 移动到num行 |
G | 移动到最后一行 |
numj | 向下移动num行 |
numgg | 跳转至num行 |
进入插入模式
按键 | 说明 |
---|---|
h | 左 |
l | 右(小写 L) |
j | 下 |
k | 上 |
w | 移动到下一个单词 |
b | 移动到上一个单词 |
命令行模式下保存文档
从普通模式输入 :
进入命令行模式,输入 w
回车,保存文档。输入 :w <filename>
可以将文档另存为其他文件名或存到其它路径下。
命令行模式下退出 vim
从普通模式输入 :
进入命令行模式,输入 wq
回车,保存并退出编辑。
以下为其它几种退出方式:
命令 | 说明 |
---|---|
:q! | 强制退出,不保存 |
:q | 退出 |
:wq! | 强制保存并退出 |
:w <文件路径> | 另存为 |
:saveas 文件路径 | 另存为 |
:x | 保存并退出 |
:wq | 保存并退出 |
普通模式下退出 vim
普通模式下按下 Shift+zz
即可保存退出 vim。
普通模式下删除 vim 文本信息
进入普通模式,使用下列命令可以进行文本快速删除:
命令 | 说明 |
---|---|
x | 删除游标所在的字符 |
X | 删除游标所在前一个字符 |
Delete | 同 x |
dd | 删除整行 |
dw | 删除一个单词(不适用中文) |
d$ 或D | 删除至行尾 |
d^ | 删除至行首 |
dG | 删除到文档结尾处 |
d1G | 删至文档首部 |
2dd | 一次删除两行 |
行间跳转
命令 | 说明 |
---|---|
nG (n Shift+g) / ngg | 游标移动到第 n 行 (如果默认没有显示行号,请先进入命令模式,输入 :set nu 以显示行号) |
gg | 游标移动到到第一行 |
G (Shift+g) | 到最后一行 |
Ctrl+o
快速回到上一次(跳转前)光标所在位置
行内跳转
普通模式下使用下列命令在行内按照单词为单位进行跳转:
命令 | 说明 |
---|---|
w | 到下一个单词的开头 |
e | 到当前单词的结尾 |
b | 到前一个单词的开头 |
ge | 到前一个单词的结尾 |
0 或^ | 到行头 |
$ | 到行尾 |
f<字母> | 向后搜索<字母>并跳转到第一个匹配的位置(非常实用) |
F<字母> | 向前搜索<字母>并跳转到第一个匹配的位置 |
t<字母> | 向后搜索<字母>并跳转到第一个匹配位置之前的一个字母(不常用) |
T<字母> | 向前搜索<字母>并跳转到第一个匹配位置之后的一个字母(不常用) |
复制及粘贴文本
普通模式中使用
y
复制普通模式中,
yy
复制游标所在的整行(3yy
表示复制 3 行)普通模式中,
y^
复制至行首,或y0
。不含光标所在处字符。普通模式中,
y$
复制至行尾。含光标所在处字符。普通模式中,
yw
复制一个单词。普通模式中,
y2w
复制两个单词。普通模式中,
yG
复制至文本末。普通模式中,
y1G
复制至文本开头。普通模式中使用
p
粘贴普通模式中,
p
(小写)代表粘贴至光标后(下)普通模式中,
P
(大写)代表粘贴至光标前(上)粘贴
普通模式中使用
p
粘贴普通模式中,
p
(小写)代表粘贴至光标后(下)普通模式中,
P
(大写)代表粘贴至光标前(上)
剪切
dd命令就是剪切
ddp可以实现交换行
字符串替换
命令 | 说明 |
---|---|
r +<待替换字母> | 将游标所在字母替换为指定字母 |
R | 连续替换,直到按下 Esc |
cc | 替换整行,即删除游标所在行,并进入插入模式 |
cw | 替换一个单词,即删除一个单词,并进入插入模式 |
C (大写) | 替换游标以后至行末 |
~ | 反转游标所在字母大小写 |
{n}u | 撤销一次或 n 次操作 |
U (大写) | 撤销当前行的所有修改 |
Ctrl+r | redo,即撤销 undo 的操作 |
快速缩进
- 普通模式下输入
>>
整行将向右缩进 - 普通模式下输入
<<
整行向左回退 - 普通模式下输入
:
进入命令行模式下对shiftwidth
值进行设置可以控制缩进和回退的字符数
:set shiftwidth=10
调整文本位置
:ce
:ri
:le
分别将本行文本靠某个位置
快速查找
普通模式下输入 /
然后键入需要查找的字符串,按回车后就会进行查找。?
与/
功能相同,只不过 ?
是向上而 /
是向下查找。
进入查找之后,输入 n
和 N
可以继续查找。n
是查找下一个内容,N
查找上一个内容。
- 命令行模式下输入
noh
然后回车即可取消搜索
使用 vim 编辑多个文件
编辑多个文件有两种形式,一种是在进入 vim 前使用的参数就是多个文件。另一种就是进入 vim 后再编辑其他的文件。 同时创建两个新文件并编辑1
2
3vim 1.txt 2.txt
默认进入1.txt文件的编辑界面命令行模式下输入 :n 编辑 2.txt 文件,可以加 ! 即 :n! 强制切换,之前一个文件的输入没有保存,仅仅切换到另一个文件命令行模式下输入 :N 编辑 1.txt 文件,可以加 ! 即 :N! 强制切换,之前文件内的输入没有保存,仅仅是切换到另一个文件
- 命令行模式下输入:e 3.txt 打开新文件 3.txt
- 命令行模式下输入
:e#
回到前一个文件 - 命令行模式下输入
:ls
可以列出以前编辑过的文档 - 命令行模式下输入
:b 2.txt
(或者编号)可以直接进入文件 2.txt 编辑 - 命令行模式下输入
:bd 2.txt
(或者编号)可以删除以前编辑过的列表中的文件项目 - 命令行模式下输入
:e! 4.txt
,新打开文件 4.txt,放弃正在编辑的文件 - 命令行模式下输入
:f
显示正在编辑的文件名 - 命令行模式下输入
:f new.txt
,改变正在编辑的文件名字为 new.txt
恢复文件
如果因为断电,终端意外关闭等原因造成文档没有保存,可以采用恢复方式。
vim 在编辑的时候会自动在当前文件目录下生成一个交换文件,一般以 <filename>.swp
的格式保存,如果有多个版本的交换文件,还可能是 .swn
,.swm
等字母。注意这个交换文件的更新不是实时的,因为实时的更新会占用磁盘,影响系统其他正常进程的速度。
在命令行模式中输入 !
可以执行外部的 shell 命令。
:!ls
用于显示当前目录的内容:!rm FILENAME
用于删除名为 FILENAME 的文件:w FILENAME
可将当前 VIM 中正在编辑的文件另存为 FILENAME 文件
Shell变量 运算符 分支 循环 传递参数 函数使用
行首以 #
开头(除#!之外)的是注释。#!
是用于指定当前脚本的解释器,我们这里为 bash,且应该指明完整路径,所以为 /bin/bash
命令分隔符
使用分号 ;
可以在同一行上写两个或两个以上的命令。1
vim test2.sh
输入如下代码,并保存:1
2
3
4
5
6
7
8
echo hello; echo there
filename=ttt.sh
if [ -e "$filename" ]; then # 注意: "if"和"then"需要分隔,-e用于判断文件是否存在
echo "File $filename exists."; cp $filename $filename.bak
else
echo "File $filename not found."; touch $filename
fi; echo "File test complete."
终止 case 选项(双分号)
使用双分号 ;;
可以终止 case 选项。1
vim test3.sh
输入如下代码,并保存。1
2
3
4
5
6
7
8
varname=b
case "$varname" in
[a-z]) echo "abc";;
[0-9]) echo "123";;
esac
双引号(”)
“STRING” 将会阻止(解释)STRING 中大部分特殊的字符。
单引号(’)
‘STRING’ 将会阻止 STRING 中所有特殊字符的解释,这是一种比使用”更强烈的形式。
斜线(/
)
文件名路径分隔符。分隔文件名不同的部分(如 /home/bozo/projects/Makefile
)。也可以用来作为除法算术操作符。注意在 linux 中表示路径的时候,许多个 /
跟一个 /
是一样的。/home/shiyanlou
等同于 ////home///shiyanlou
。
反斜线(\
)
一种对单字符的引用机制。\X
将会“转义”字符 X。这等价于”X”,也等价于’X’。\
通常用来转义双引号("
)和单引号('
),这样双引号和单引号就不会被解释成特殊含义了。
- 符号 说明
\n
表示新的一行\r
表示回车\t
表示水平制表符\v
表示垂直制表符\b
表示后退符\a
表示”alert”(蜂鸣或者闪烁)\0xx
转换为八进制的 ASCII 码, 等价于 0xx\"
表示引号字面的意思
转义符也提供续行功能,也就是编写多行命令的功能
命令替换
反引号中的命令会优先执行,如:1
2cp `mkdir back` test.sh back
ls
先创建了 back 目录,然后复制 test.sh 到 back 目录。
空命令
1 | : |
等价于“NOP”(no op,一个什么也不干的命令)。也可以被认为与 shell 的内建命令 true 作用相同。“:”命令是一个 bash 的内建命令,它的退出码(exit status)是(0)
命令组
在括号中的命令列表,将会作为一个子 shell 来运行。
在括号中的变量,由于是在子 shell 中,所以对于脚本剩下的部分是不可用的。父进程,也就是脚本本身,将不能够读取在子进程中创建的变量,也就是在子 shell 中创建的变量。如:1
vim test20.sh
输入代码:1
2
3
4
5
6
a=123
( a=321; )
echo "$a" #a的值为123而不是321,因为括号将判断为局部变量
运行代码:1
2bash test20.sh
a = 123
在圆括号中 a 变量,更像是一个局部变量。
2.初始化数组
创建数组1
vim test21.sh
输入代码:1
2
3
4
arr=(1 4 5 7 9 21)
echo ${arr[3]} # get a value of arr
运行代码:1
2bash test21.sh
7
条件测试
条件测试表达式放在 [ ]
中。下列练习中的 -lt
(less than)表示小于号。1
vim test24.sh
输入代码:1
2
3
4
5
6
7
8
9
a=5
if [ $a -lt 10 ]
then
echo "a: $a"
else
echo 'a>=10'
fi
运行代码:1
2bash test24.sh
a: 5
双中括号([[ ]]
)也用作条件测试(判断)
变量
概念
变量的名字就是变量保存值的地方。引用变量的值就叫做变量替换。
如果 variable 是一个变量的名字,那么 $variable
就是引用这个变量的值,即这变量所包含的数据。
$variable
事实上只是 ${variable}
的简写形式。在某些上下文中 $variable
可能会引起错误,这时候你就需要用 ${variable}
了
局部变量
这种变量只有在代码块或者函数中才可见。
环境变量
这种变量将影响用户接口和 shell 的行为。
在通常情况下,每个进程都有自己的“环境”,这个环境是由一组变量组成的,这些变量中存有进程可能需要引用的信息。在这种情况下,shell 与一个一般的进程没什么区别。
位置参数
从命令行传递到脚本的参数:$0
,$1
,$2
,$3
…
$0
就是脚本文件自身的名字,$1
是第一个参数,$2
是第二个参数,$3
是第三个参数,然后是第四个。$9
之后的位置参数就必须用大括号括起来了,比如,${10}
,${11}
,${12}
。
$#
: 传递到脚本的参数个数$*
: 以一个单字符串显示所有向脚本传递的参数。与位置变量不同,此选项参数可超过 9 个- $$$$ : 脚本运行的当前进程 ID 号
$!
: 后台运行的最后一个进程的进程 ID 号$@
: 与$*
相同,但是使用时加引号,并在引号中返回每个参数$
: 显示 shell 使用的当前选项,与 set 命令功能相同$?
: 显示最后命令的退出状态。 0 表示没有错误,其他任何值表明有错误。
运算符
运算符 | 说明 |
---|---|
-eq | 检测两个数是否相等 |
-ne | 两个数是否不等 |
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
逻辑运算
运算符 | 说明 | ||
---|---|---|---|
&& | 逻辑的AND | ||
\ | \ | 逻辑的OR |
字符串运算符
运算符 | 说明 |
---|---|
= | 检测两个字符串是否相等 |
!= | 检测两个字符串是否不相等 |
-z | 检测字符串长度是否为0 |
-n | 检测字符串长度是否不为0 |
str | 检测字符串是否为空 |
文件测试运算符
1 | -b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。 |
流程控制
if
if 语句语法格式:1
2
3
4
5
6
7if condition
then
command1
command2
...
commandN
fi
if else
if else 语法格式:1
2
3
4
5
6
7
8
9if condition
then
command1
command2
...
commandN
else
command
fi
if-elif-else 语法格式:1
2
3
4
5
6
7
8
9if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
for循环
for 循环一般格式为:1
2
3
4
5
6
7for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
创建文件
while循环
while 循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:1
2
3
4while condition
do
command
done
(()) 双括号用于增加算术值,双括号里数值不用带$ 扩展,且可以进行很多算术运算
Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $
来表示变量
case
Shell case 语句为多选择语句。可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case 语句格式如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
- 取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至
;;
。 - 取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号
*
捕获该值,再执行后面的命令。
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break
和 continue
。
break
命令
break 命令允许跳出所有循环(终止执行后面的所有循环)。
continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环
esac
case 的语法和 C family 语言差别很大,它需要一个 esac(就是 case 反过来)作为结束标记,每个 case 分支用右圆括号,用两个分号表示 break。
函数
shell 中函数的定义格式如下:1
2
3
4
5
6
7
8
9[ function ] funname [()]
{
action;
[return int;]
}
说明:
- 可以带
function fun()
定义,也可以直接fun()
定义,不带任何参数。 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return 后跟数值 n(0-255)
函数返回值在调用该函数后通过
$?
来获得- 所有函数在使用前必须定义。
函数参数
在 Shell 中,调用函数时可以向其传递参数。在函数体内部,通过 $n
的形式来获取参数的值,例如,$1
表示第一个参数,$2
表示第二个参数…
带参数的函数示例:1
2
3
4
5
6
7
8
9
10
11
funWithParam(){
echo "The first parameter is $1 !"
echo "The second parameter is $2 !"
echo "The tenth parameter is $10 !"
echo "The tenth parameter is ${10} !"
echo "The eleventh parameter is ${11} !"
echo "The total number of parameters is $# !"
echo "Outputs all parameters as a string $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73