Linux命令行与shell脚本学习笔记
一、常用命令
1.文件路径
#present working directory
wjtangdeMacBook-Pro:~ wjtang$ pwd
#present server
wjtangdeMacBook-Pro:~ wjtang$ ps
***************************************[注]:~ 代表当前位于主目录中
绝对文件路径(以 / 开头)
#可以用绝对文件路径切换到Linux虚拟目录结构的任何一级
wjtangdeMacBook-Pro:~ wjtang$ cd /var/log
wjtangdeMacBook-Pro:log wjtang$ pwd
/var/log
#也可以从任何目录直接跳回主目录
wjtangdeMacBook-Pro:log wjtang$ cd
wjtangdeMacBook-Pro:~ wjtang$ pwd
/Users/wjtang
相对文件路径(不以 / 开头)
wjtangdeMacBook-Pro:~ wjtang$ cd Movies
wjtangdeMacBook-Pro:Movies wjtang$ pwd
/Users/wjtang/Movies
#通过 ~ 直接跳到主目录
wjtangdeMacBook-Pro:Public wjtang$ cd ~/Public
wjtangdeMacBook-Pro:Public wjtang$ pwd
/Users/wjtang/Public
***************************************[注]:单点符(.)表示当前目录;双点符(..)表示当前目录的父目录
2.文件和目录列表
常用ls命令
#查询ls的可使用的后缀
man ls
#-F File在所有目录后加/, -R Recursive递归,显示子文件中的所有子目录, -a all显示普通文件+隐藏文件,-l 显示详细信息
ls -F -R -a -l
#这些后缀可以组合使用,如:
ls -alF
过滤ls输出列表
#ls -l [文件名],通过文件名来过滤,如:
ls -l my_script
#支持模糊匹配,?单个字符,*多个字符,[ai]a和i二取一,[a-i]a到i取一个,[!a]排除a
ls -l my_s*t
创建文件
#利用touch命令创建文件
192:~ wjtang$ touch test_one
#touch还可以更改文件的修改时间,这个操作不改变文件内容
192:~ wjtang$ ls -l test_one
-rw-r--r-- 1 wjtang staff 0 2 8 09:40 test_one
192:~ wjtang$ touch test_one
192:~ wjtang$ ls -l test_one
-rw-r--r-- 1 wjtang staff 0 2 8 10:00 test_one
复制文件
#cp source destination
#man cp #don't forget it!!!
#复制文件在同一个目录
192:~ wjtang$ cp test_one test_two
#cp命令默认自动覆盖原有文件,为防止误操作,可加上-i ,要求询问是否overwrite
192:~ wjtang$ cp -i test_one test_two
overwrite test_two? (y/n [n]) y
#复制到另一个目录,目录可使用绝对路径与相对路径
192:~ wjtang$ cp test_one /Users/Shared
#利用单点符 . 表示当前目录
192:~ wjtang$ cp /Users/shared/test_one .
链接文件
#Linux文件链接可分为两种
# 符号链接 相当于快捷方式 ,删除它不影响源文件
# 硬链接 相当于指针,指向文件存储的那个区块
#创建符号链接 ln 命令 ,-s 标签;ln-->link , s-->symbolic
192:~ wjtang$ ln -s test_one sl_test_one
192:~ wjtang$ ls -l *test_one
lrwxr-xr-x 1 wjtang staff 8 2 8 13:01 sl_test_one -> test_one
-rw-r--r--@ 1 wjtang staff 4 2 8 10:54 test_one
#创建硬链接,只需用到 ln 命令
192:~ wjtang$ ln test_one hl_test_one
192:~ wjtang$ ls -li *test_one
12310787 -rw-r--r--@ 2 wjtang staff 4 2 8 10:54 hl_test_one
12314072 lrwxr-xr-x 1 wjtang staff 8 2 8 13:01 sl_test_one -> test_one
12310787 -rw-r--r--@ 2 wjtang staff 4 2 8 10:54 test_one
# readlink 可以直接找出链接的最后一环
readlink -f /usr/bin/vi
【注】:只能对处于同一存储媒体的文件创建硬链接。不同存储媒体的文件之间只能使用符号链接
移动文件 & 重命名文件
#mv 命令
#重命名文件
192:~ wjtang$ mv test_one test_onz
#移动文件
192:~ wjtang$ mv test_two ../Pictures
#同时完成移动文件和重命名
192:~ wjtang$ mv test_onz ./test_one
#重命名或移动整个目录
192:~ wjtang$ mv code_logs Music/code_logs_new
【注】:/home/Pictures/fall 表示文件fall
/home/Pictures/fall/ 表示路径
区别就是结尾有没有 '/'
删除文件
#rm 命令 removing
#-i 意思是 Request confirmation before attempting to remove each file
192:~ wjtang$ rm -i test_one
remove test_one? y
创建目录
#mkdir
192:~ wjtang$ mkdir new_directory
#同时创建多级目录
#-p 意思是Create intermediate directories as required. 即递归创建目录
192:~ wjtang$ mkdir -p new_directory/sub_dir/under_dir
删除目录
#rmdir 默认只能删除空目录
#可以使用使用 rm -ri 递归删除文件,且其中的每个文件都询问,删完文件后目录也会被删除
192:~ wjtang$ rm -ri new_directory/
examine files in directory new_directory/? n
#也可使用使用 rm -fr 递归删除文件,且不询问每个文件,,删完文件后目录也会被删除
192:~ wjtang$ rm -fr new_directory/
查看文件内容
#file 查看文件类型
192:~ wjtang$ file test_three
test_three: ASCII text #字符编码为ASCII 文件类型为text
#一、查看全部文件内容
#1. cat命令
192:~ wjtang$ cat test_one
#也可以添加参数 如-n 显示行号 -b 只显示非空行的行号 -T 用^I代替Tab符
#2. more命令,more在显示一页内容后停下来
192:code_logs wjtang$ more spring-web-restful-crud.log
#3.less命令,less is more! more命令的升级版,可提供上下翻页
192:code_logs wjtang$ less spring-web-restful-crud.log
#二、查看部分文件内容
#1.tail命令 默认查看最后十行内容
192:code_logs wjtang$ tail spring-web-restful-crud.log
#2.head命令 默认查看前十行的内容
192:code_logs wjtang$ head spring-web-restful-crud.log
【注】:如果要自己指定行数,可以用-num,如:
192:code_logs wjtang$ tail -5 spring-web-restful-crud.log
192:code_logs wjtang$ head -5 spring-web-restful-crud.log
3.更多的bash shell命令
进程监测
# 1.查看系统当前进程 ps
# -e 代表显示所有进程 , -f 代表显示完整格式的输出
ps -ef
# 2. 实时监测进程 top,与ps类似,但是是实时显示进程信息的
top
# 3.结束进程
kill 406 # 通过PID结束进程
kill -s HUP 406 # 强制关闭进程
killall http* # 可以通过进程名而非PID来关闭进程,支持通配符
磁盘空间监测
mount # 输出当前系统上挂载的设备列表
umount # 卸载
df # 查看挂载的设备还有多少磁盘空间
du # 显示某个特定的目录的磁盘使用情况(默认为当前目录)
排序数据
cat file1 # 显示file1中的内容
sort file1 # 按照 字符 排序后,显示file1中的内容
sort -n file1 # 按照 数字 排序后,显示file1中的内容
sort -M file1 # 按照 月份 排序
# 更多的参数可以通过 man sort 查看
搜索数据
grep three file1 # 在 file1 中搜索 three
grep -n three file1 # 带行号搜索
# 同样的-v 代表反向搜索,-c 代表匹配的行数,更多的可以man grep
# [注]: grep搜索时支持使用 “正则表达式”
压缩数据
gzip test_one # 将test_one压缩成.gz文件
# tar命令 (书的p84)
tar -cvf test.tar test/ test2/ # 将test和test2压缩到test.tar
tar -tf test.tar # 列出test.tar的内容(不提取)
tar -xvf test.tar #提取test.tar中的内容
tar -zxvf filename.tgz # 提取.tgz文件,tgz即用gzip压缩过的tar文件
4.理解shell
父shell、子shell
- 大多数系统中默认的shell都是bash shell
# 默认打开的shell都是父shell
# 下面两行可以开子shell
/bin/bash
bash
# 可以通过多次使用bash,创建多级bash进程
exit # 退出bash
进程列表
- 一行指定多个命令,用; 隔开
# 不创建子shell的方式
pwd;ls;cd ..;pwd;ls
# 若想让多个命令以 “进程列表” 的方式执行,只需加(),所谓“进程列表”,即能创建出子shell
(pwd;ls;cd ..;pwd;ls)
# 判断是否创建出子shell,可以通过环境变量 $BASH_SUBSHELL(0代表不生成, 1代表生成),echo $BASH_SUBSHELL
pwd;ls;cd ..;pwd;ls ; echo $BASH_SUBSHELL
(pwd;ls;cd ..;pwd;ls ; echo $BASH_SUBSHELL)
后台模式
- “后台模式”:即在处理命令的同时让出CLI(Command-Line Interface)
sleep 10 # 休眠10秒
# 通过& 进入后台模式,即放入后台运行
sleep 20&
# 显示当前系统中后台中的作业,可以加参数,如jobs -l
jobs
# 可以将 “进程列表” 放入后台运行
(pwd;ls;cd ..;pwd;ls ; echo $BASH_SUBSHELL)&
协程
- 协程:即可以同时做两件事,与“后台模式”很类似
coproc sleep 10
外部命令
- 不属于“内建命令”,是存在于bash shell之外的命令
ps # 就是典型的外部命令,其他的还有如 python
# 通过 which 或 type 查看
which ps
type -a ps
which python3
内建命令
- 内建于bash shell
# 典型的内建命令
cd
exit
# 通过type查看
type cd
# 会输出:cd is a shell builtin
- history 可以用来查看最近使用过的命令列表
history
# !行号 使用第几行历史命令
!10
命令别名
alias li='ls -li'
5.环境变量
- 全局环境变量: 对于所有的shell都可见
- 局部环境变量:只对创建它们对shell可见
【注】:环境变量全大写
# 查看全局环境变量
printenv
# 查看某个环境变量
printenv LANG
echo $LANG
# $可以将环境变量作为参数,如下两行效果是一样的
ls $HOME
ls /Users/wjtang
# 查看所有环境变量(全局+局部)
set
# 自定义局部环境变量
my_variable="Hello World" # 赋值
echo $my_variable # 输出
# 自定义全局环境变量
# 1. 首先定义一个局部环境变量
# 2. 用export使其编程全局环境变量
export my_variable
# 删除环境变量
unset my_variable
PATH环境变量
- PATH 可以使系统找到“外部命令”的路径
- PATH 多个路径用 : 分隔
- 只能使用绝对路径
# 更新PATH
PATH=$PATH:/new/new_path
# 如果希望全局生效(包括子shell),记得导出
export PATH
# 但是这种修改,只能持续到重启系统,若要永久生效,则将修改过的全局环境变量放入/etc/profile.d目录中(/etc/profile也行,但是更新系统可能消失)
# 别名alias若想永久生效,放到$HOME/.bashrc
数组变量
mytest=(one two three four five) # 1. 赋值
echo ${mytest[2]} # 输出数组中某个元素
echo ${mytest[*]} # 输出数组中所有元素
unset mytest[2] # 删除某个元素
unset mytest # 删除整个数组
6.Linux安全性
用户操作
/etc/passwd # 存放用户名和UID
# 添加用户
useradd
# 删除用户
userdel
# 修改用户账户的字段
usermod
# 修改密码
passwd username
# 批量修改密码
chpasswd < users.txt # 将user.txt中的userid:passwd对,一次性修改到系统里
/etc/group # 存放用户的组信息
groupadd # 添加组
groupmod # 修改组
文件权限
- 文件权限符
drwxr-xr-x 3 wjtang staff 96 3 9 11:19 new_dir
-rw-r--r--@ 1 wjtang staff 55 3 9 09:37 test_one.gz
# 第一个字段描述文件和目录权限
- # 代表文件
d # 代表目录
l # 代表链接
c # 代表char
b # 代表块设备
n # 代表网络设备
# 之后的字符,以三个为一组,分别代表 “文件属主”、“组内其他成员”、“其他用户” 的权限
# 如drwxr-xr-x ,d代表目录,紧接着 rwx r-x r-x 分别代表三种不同角色的权限
r # 代表读
w # 代表写
x # 代表可执行
- 默认文件权限
# umask 可以显示和设置默认权限
wjtangdeMacBook-Pro:~ wjtang$ umask
0022
# 第一位 0 叫 “粘着位”,是一项安全特性
# 后三位022代表权限,用 666-022=644,644三个数字则分别代表 rw- r-- r--,即新建文件的默认权限
# 【注】:文件使用666减,而目录使用777减
- 改变安全性设置
# 改变文件权限
chmod 760 newfile # 将newfile的权限改为760
# 改变文件属主和属组
chown dan newfile # newfile的主人改为dan
chown dan.newgroup newfile # newfile的主人改为dan,属组改为newgroup
# 改变文件属组
chgrp newgroup newfile # newfile的属组改为newgroup
共享文件
- Linux共享文件的基本方法是创建组
mkdir testdir # 1.创建共享目录
chgrp shared testdir # 2. 将共享目录添加到想要其加入的组
chmod g+s testdir # 3.目录的SGID置位,保证共享目录中的所有新建文件都以shared为组
umask 002 # 4.修改默认文件权限,保证组内成员拥有至少rw-权限
7.管理文件系统
创建分区
sudo fdisk /dev/sdb # 用管理员身份创建分区
8.使用编辑器
vim编辑器
which vim # 查看vim路径,一般是/usr/bin/vim
ls -l /usr/bin/vim # 查看vim权限、
# vim编辑文件
vim filename # 若filename不存在,则会新建
# 【注】:有的版本的Linux中会给vim起别名(vi),判断方式是通过命令 alias vi;
- 普通模式
# h为向左;l为向右;j为向下;k为向上;
# PageDown 或 (Ctrl+F)下翻一页;
# PageUp 或 (Ctrl+B)上翻一页
# G:移到缓冲区最后一行
# num G:移到缓冲区第num行
# gg:移到缓冲区第一行
# 【注】:更过快捷键可以看书p185 or 上网查
- 插入模式
# 插入模式,按下 i 即可
# 退出插入模式,按 Esc 即可
- 命令行模式–其实也属于 普通模式
# vim的命令行模式,普通模式下按 : 进入
# q 未修改直接退出
# q! 取消所有修改,退出
# w filename 将文件另存为filename
# wq 保存修改并退出
# x 删除当前光标位置的一个字符
# dd 删除当前光标位置的一行
# dw 删除当前光标位置的一个单词
# d$ 删除当前光标位置到行尾的内容
# J 连接当前行和下一行
# u 撤销刚刚的编辑命令
# a 在当前光标位置后append数据
# A 在当前所在行的行尾append数据
# r char 用char替换当前光标位置的单个字符
# R text 用text文本替换当前光标位置的数据,直到按Esc键结束
- 复制、粘贴
# yw 复制光标处的一个单词;
# y$ 复制光标至行尾处
# p 表示粘贴
# 可以先dd删除一行,然后移动光标到目标位置后,切换成普通模式,用 p 命令实现粘贴
- 查找、替换
# 查找
# 普通模式下按 /serch_content,回车
# 若需要查下一个,回车 代表next
# 替换
:s/old/new/
:s/old/new/g # 替换一整行的内容
:n,ms/old/new/ # 替换行号n,m之间的所有内容
:%s/old/new/g # 替换整个文件
:%s/old/new/gc # 替换整个文件,且每次都询问
二、shell脚本
1.Shell脚本基础
创建脚本
# 1.编写shell脚本,可以用touch新建文件,再用vim编辑
# #!/bin/bash 脚本文件第一行一定指明要使用的shell,
touch shell_script_one
mv shell_script_one shell_script_one.sh # 改了个名字
vim shell_script_one.sh # 编辑脚本
chmod 774 shell_script_one.sh # 如果权限不足,可以改变权限之后再执行
执行脚本
# 以路径方式执行脚本
./shell_script_one.sh # 绝对路径
# 先cd到脚本所在路径
sh shell_script_one.sh
bash shell_script_one.sh
# 【注】:执行shell脚本有两种方式:1. 添加脚本到环境变量PATH 2.直接以路径方式引用执行
显示消息
echo This is a test # echo message 加入到脚本
echo "This is a test" # 有时需要加'' or ""
# 【注】:若要echo的文本中出现 '' ,则用 "" 来界定文本,反之亦然
# 文本和下面的一行命令在 同一行 输出;注意字符串尾部有个空格
echo -n "This is a test: "
使用变量
- 环境变量
# 在脚本中 $env_variable 可以使用环境变量,如
echo "The PATH of this environment is : $PATH"
echo $PATH
- 用户变量
# 在脚本中,先定义,再使用
person="Monica"
echo "$person is the most beautiful girl in our school!"
- 命令的输出赋值给变量 ,又叫命令替换
# 两种方式 `` or $()
testing1=`date`
testing2=$(date)
# 例如
testing=`date`
echo "The date is : $testing"
重定向输入和输出
- 输出重定向
# 输出重定向
command > outputfile # 将command的输出写入到outputfile,> 会覆盖文件中原有内容
command >> outputfile # append 到outputfile
# 例如
date > test_two
date >> test_two
- 输入重定向
# 将文件中的内容重定向到命令
command < inputfile
wc < test_two
command << inputfile #<< 用不到文件,是手动输入的,但是要指明开始和结尾的界定符
wc << EOF
> test 1
> test 2
> EOF
2 4 14
# 【注】:wc输出文件的文本行数,词数,字节数
管道
# 将一个命令的输出 重定向 到下一个命令
# 两个命令会同时执行,且command1的输出产生后会立即送往command2
command1 | command2 | command3
# 例如:
ls | sort
执行数学运算
- 整数运算
# expr命令,数字与运算符之间要有空格
expr 1 + 5
# expr 1 * 5 会报错,遇到*这种容易被Linux认错的操作符,需要加\,如下
expr 1 \* 5
# 数学运算的结果赋值给某个变量
# 可以使用$()和``进行命令替换
var3=$(expr $var1 / $var2)
echo $var3
# 也可以使用方括号进行数学运算
var3=$[ $var1 / $var2 ]
echo $var3
# [注]:bash shell只支持整数运算
- 浮点运算
bc # 可以使用内建的bash计算器,完成后用quit退出
# 在脚本中使用bc
varibale=$(echo "options; expression" | bc) # 注意空格
var1=$(echo "scale=4; 3.44*5" | bc) # scale=4代表保留四位小数
退出状态码
echo $? # 查看最后一条命令的退出状态码
# 在脚本中,默认使用脚本最后一条命令的退出状态码
# 若要自己指定退出状态码,可以通过在脚本最后一行加入 exit num
exit 88
# 【注】:退出状态码的范围是[0,255],默认0位执行成功的状态码
2.选择结构
if-then
if command # 此处的command若成功执行(退出状态码为0),则执行then下面的commands
then
command1
command2
...
fi
# 另一种写法
if command; then
command1
command2
...
fi
if-then-else
if command # command成功执行则 执行then下的语句,否则执行else下的语句
then
commands
else
commands
fi
嵌套if
# 在上面提及的代码块(then代码块、else代码块)中嵌入if结构化语句
# 另一种方法是使用 elif
if command1
then
commands
elif command2
then
commands
else
commands
fi
test命令
# 由于if语句只可以判断命令的退出状态码,为了拓展其判断的范围,加入了test命令
# test的条件成立,则test命令返回0,否则返回非0
test condition # 不写condition默认返回非0
# 使用方法如下:
if test condition
then
commands
fi
# 或者简单一点
if [ condition ] # 注意空格
then
commands
fi
#【注】:test只能用于 数值比较、字符串比较、文件比较
- 数值比较
n1 -eq n2 # equal
n1 -ge n2 # greater than or equal
n1 -gt n2 # greater than
n1 -le n2 # less than or equal
n1 -lt n2 # less than
n1 -ne n2 # not equal
#【注】:test命令中的比较,只能针对整数
- 字符串比较
str1 = str2
str1 != str2
str1 \< str2 # 使用转义字符,否则会将<看成重定向符
str1 \> str2
-n str1 # 检查str1的长度是否非0
-z str1 # 检查str1的长度是否为0
# 【注】:对于大小写字母,比较操作的顺序 与 sort排序的顺序正好相反
- 文件比较
-d file # 检查file是否是一个存在并是一个目录
-e file # 检查file是否exist
-f file # 检查file是否存在且是一个文件
-r file # 检查文件是否存在并且可读
-s file # 检查文件是否存在并且非空 ------
-w file # 检查文件是否存在并且可写
-x file # 检查文件是否存在并且可执行
-O file # 检查文件是否存在并且当前用户是其Owner
-G file # 检查文件是否存在并且默认的Group与当前用户相同
file1 -nt file2 # file1是否new than file2
file1 -ot file2 # file1是否older than file2
复合测试条件
[ conditon1 ] && [ condition2 ]
[ conditon1 ] || [ condition2 ]
if-then的高级特性
- 双括号–可以在比较过程中使用 高级数学表达式
(( expression ))
# 可以使用如下表达式
val++
val--
++val
--val
! # 逻辑取反
~ # 位求反
** # 求幂
<< # 左位移
>> # 右位移
&
|
&&
||
- 双方括号–可以在比较过程中使用高级字符串操作,如模式匹配
[[ expression ]]
# 例如:
if [[ $User == r* ]] # 判断时不需要加引号
then
echo "Hello $User"
else
echo "No such user here!"
fi
case命令
case variable in
pattern1 | patter2)
commands1;; #两个分号
pattern3)
commands2;;
*)
commands3;;
esac
# -----------------------------------------------------------------------
# 例如
case $User in
richal | abc)
echo "Welcome,$User"
echo "Please enjoy your visit!";; # 只有在一个代码块结束时才加 两个分号
cde)
echo "no such user";;
*)
echo "no such user";;
esac
3.循环结构
for命令
for var in list
do
commands
done
# 例如:
for test in one two three four five six
do
echo "The next number is $test"
done
#【注】:若list中与单引号等特殊字符,需要加转义字符 \,或者用双引号(遇到空格只能用双引号,如New York)
for test in I don\'t know if "this'll" work in "New York"
- 从变量中获取值
# 可以将整个列表都存入一个变量中,for再从这个变量里获取值
list="one two three four five six"
for test in $list
do
echo "The next number is $test"
done
- 从命令中读取值
# 也可以从命令读取值
list=$(ls)
for test in $list
do
echo "The next file is $test"
done
- 更改字段分隔符(默认为: 空格、制表符、换行符)
# 通过IFS环境变量配置分隔符
IFS=$'\n' # 只通过$'\n'分隔字段,这里都换行符只有加了才真的代表换行符 $'\n'
IFS=$'\n':; # 通过换行符、冒号、分号来分隔
# 如果只是临时修改,可以这样
IFS.old=$IFS
IFS=new
IFS=$IFS.old
- 使用通配符
for file in /Users/wjtang/*
do
echo "The next number is $test"
done
C语言风格的for循环
for(( a=1;a<10;a++))
do
commands
done
while命令
while test condition
do
commands
done
- 多个测试命令
# 以最后一个测试命令的退出状态码为准
while echo $var1
[ $var1 -ge 0 ]
do
commands
done
until命令
# until 与while正好相反,只有condition不成功执行才会进入循环,当condition成功执行则直接结束循环
until test condition
do
commands
done
嵌套循环
- 在for的do循环体里面再嵌入循环
控制循环
- break 跳出当前循环
# 特别的是break n 表示跳出第n级循环,当前循环为n=1的情况
break 2 # 跳出第二级循环
- continue 只跳过某次循环
# continue n 也表示跳过第n级循环的某次迭代
continue 2
处理循环的输出
- 重定向到某个文件
for test in one two three four five six
do
echo "The next number is $test"
done > outputfile
- 管道 将循环的输出重定向到某个命令
for test in one two three four five six
do
echo "The next number is $test"
done | sort
4.输入数据
命令行参数
./addem 10 30 # 向命令addem传递两个参数10和30
# 其中$0代表命令的名字,$1代表第一个参数,$2代表第二个参数,参数超过九个需要加{},如 ${10} ${11}
# 编写如下脚本
#!/bin/bash
echo "The first command attribute is $1" # 输出第一个命令行参数
# 每个参数用空格分开,参数中如果带空格,需要用引号扩起来(单引号和双引号都可以)
./test3.sh 'Rich Blum'
- 读取脚本名
# $0 相关
# 如果命令带路径那么 $0也会返回路径,如果不想要路径,执行要文件名,可以使用basename
name=$(basename $0)
- 测试参数
# 脚本中,使用参数之前最好先测试参数是否正确的被传入
if [ -n "$1" ] # -n str1 # 检查str1的长度是否非0 (test命令)
特殊参数变量
- 统计参数个数
$#
# 例如:
echo There were $# parameters supplied
${!#} # 可以获取最后一个参数
- 抓取所有数据
$* # 将所有参数看成一个整体(一个字符串)
$@ # 将所有参数看成一个个个体,类似于一个List
count=1
for param in $@
do
echo "Parameter $count: $param"
done
移动变量
# shift命令
# 默认左移动一个位置,,$3到$2,$2到$1,而$1的值会被删除,$0代表命令所以不受影响
# 可以通过shift遍历参数
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
count=$[ $count+1 ]
shift
done
# 一次性移动多个位置
shift 2 # 一次性左移两个位置
处理选项
# 可以自定义脚本如何处理选项
while [ -n "$1" ]
do
case '$1' in
-a) echo "Found the -a option" ;;
-b) echo "Found the -b option" ;;
-c) echo "Found the -c option" ;;
*) echo "$1 is not an option" ;;
esac
shift
done
# 如果运行命令则:
./test15.sh -a -b -c -d
# 结果为:
Found the -a option
Found the -b option
Found the -c option
-d is not an option
- 同时使用处理选项 和 参数
# 使用 特殊字符 分隔 选项和参数,例如使用--作为分隔符
while [ -n "$1" ]
do
case '$1' in
-a) echo "Found the -a option" ;;
-b) echo "Found the -b option" ;;
-c) echo "Found the -c option" ;;
--) shift
break;;
*) echo "$1 is not an option" ;;
esac
shift
done
echo "----------------------------------------"
count=1
for param in $@
do
echo "Parameter $count: $param"
done
【注】:getopt 和 getopts 可参考书P300,这两个命令的的主要作用为:选项可以连着用,如-ac 可以解析成-a -c
获取用户输入
- 从键盘输入
read parameter # 获取用户输入到parameter
# 例如
echo -n "Please input your name: " # -n 使得echo不换行
read name
echo "Hello,$name"
# 如果read后没有指明变量,可以用 $REPLY 来获取输入的值
read -p "Please enter your name: "
echo "your name is $REPLY"
# 输入超时 read -t seconds
if read -t 5 -p "Please enter your name: " name # -p "..."表示用...来提示
then
echo "your name is $name"
else
echo "sorry,too slow!"
fi
# 输入字数限制
read -n6 # read命令接受6个字符后退出
# 输入时不回显
read -s password # -s 表示不回显 read输入到password
- 从文件输入
# 可以通过 管道 将文件中的内容传递到输入
cat test | while read line # 每次读取一行内容复制给line
do
echo "$line"
done
5.输出数据
标准输入输出
# 0< 代表输入重定向,从某个文件获得输入
# 1> 代表将正确消息重定向到某个文件输出
# 2> 代表将错误消息重定向到某个文件输出
# &> 代表将正确和错误消息重定向输出到同一个文件
# ------------------------------------------------------------------------------------
# STDIN 标准输入,键盘
cat # 默认从键盘获取输入
cat < inputfile # 重定向,从文件获取
# STDOUT 标准输出,显示器
ls -l # 默认在显示器输出
ls -l > outputfile # 重定向,输出到文件,默认为 1>
# STDERR # 标准错误输出
ls -l noSuchFile 2> logfile # 2>代表只将错误消息输出到logfile,正确消息仍然输出到显示器
ls -l shell_script_one. shell_script_one.sh 1> testlog1 2> testlog2 # 正确消息输出到testlog1,错误消息输出到testlog2,1> 2>同时使用时,不能指向同一个文件
ls -l shell_script_one. shell_script_one.sh &> testlog3 # &>代表将正确和错误消息同时重定向输出到testlog3
在脚本中重定向输出
- 临时重定向
# 在脚本中,可以用 >&2 将某条消息定义为错误消息(重定向到STDERR)
echo "This is an error message" >&2
- 永久重定向
# 在脚本中
exec 1>testout # 自这一行以下的所有 正常消息 都重定向到 testout
exec 2>testerr # 自这一行以下的所有 错误消息 都重定向到 testerr
# 例子:参见课本P316
在脚本中重定向输入
exec 0< testfile # 自这一行之下的所有的输入都是从 testfile 获取的
# 比如可以在下面用read来获取输入的值
while read line
do
echo "$line"
...
done
# 例子:参见课本P317
创建自己的重定向
# 在脚本中 >&3 可以自己创建一种代号为3的输出
exec 3>test3out
echo "This is the first NO.3 outline: " >&3 # 将"..."消息代号定义为3
# 不出意外,这一行应该输出到test3out
关闭文件描述符
exec 3>&- # 关闭了自己创建的代号为3的文件描述符
列出文件描述符
lsof -a -p $$ -d 0,1,2
# 结果如下
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 8652 wjtang 0u CHR 16,0 0t1018 731 /dev/ttys000
bash 8652 wjtang 1u CHR 16,0 0t1018 731 /dev/ttys000
bash 8652 wjtang 2u CHR 16,0 0t1018 731 /dev/ttys000
# 0u u代表 读写;更多字段含义,参见书本P322
阻止命令输出
- 思路: 重定向输出到null
ls -al > /dev/null
记录消息
- 消息同时发送给文件和显示器,不一定要重定向两次,可以用tee命令
tee filename
# 例如:
date | tee testfile
date | tee -a testfile # append到testfile
实例
- 通过.csv文件逐行生成sql语句写入到.sql文件,参见书本P328
6.控制脚本
处理信号
# 常见Linux信号
1 SIGHUP # hang up
2 SIGNINT # interrupt
3 SIGNQUIT # quit
9 SIGKILL # kill
15 SIGTERM # terminate 终止进程,进程不会保留在内存中
17 SIGSTOP # stop 停止进程,进程继续保留在内存
18 SIGTSTP # terminate or stop but not kill
19 SIGCONT # continue
# CTRL+C 生成 SIGNINT中断进程
# CTRL+Z 生成 SIGTSTP 尝试
kill -9 PID # 通过PID杀死进程
- 捕获信号
trap commands signals # 当捕获到 signals 信号时,执行commands
trap "trapped a Ctrl-C" SIGINT # 捕获到SIGINT时 输出 "trapped a Ctrl-C"
# 当shell脚本退出时,捕获信号(通过EXIT)
trap "echo GoogBye..." EXIT # 当脚本结束时回产生EXIT的信号,这是捕获并执行"echo GoogBye..."
- 修改或移除捕获
# 修改捕获,重新使用一次trap命令即可
trap commands1 signals
trap commands2 signals
# 移除捕获,使用--
trap commands1 signals
trap -- signals
以后台模式运行脚本
- 命令后加上一个&
./test.sh &
sleep 100 &
非控制台下运行脚本
- 控制台下运行的脚本会随着控制台的关闭而终止,即使脚本仍在运行
- 非控制台下运行脚本可以在终端关闭时继续运行
- nohup命令
- 会自动将输出写入到nohup.out
nohup ./test.sh &
作业控制
- 查看作业
jobs
# 结果为:
[2]+ Running nohup sleep 100 &
jobs -l # 在jobs的基础上还可以查看PID
-l # 列出PID及作业好
-n # 只列出改变了状态的作业
-p # 只列出PID
-r # 只列出运行中的作业
-s # 只列出已停止的作业
- 重启作业
bg 作业号
# 【注】:作业号不是PID 而是上面的 [2]+ 中的2
bg 2
调整优先级
-20~19 # 优先级由高到低,默认优先级为0
# 指定优先级
nice -n 10 commands # 将commands的优先级调整为10
nice -10 commands# 或者这样
# 改变已运行进程的优先级
renice -n 10 -p 5055 # 将PID为5055的进程优先级改为10
定时运行作业
- at 命令
at [-f filename] time
# 例如
at -f test.sh 10:15
at -f test.sh now
# 【注】:batch也能实现,不过batch最终也是调用的at命令
# 查看等待的作业
atq
# 结果为
1 Fri Mar 12 19:38:00 2021
# 删除作业
atrm 1 # 这个 1 就是上面的atq命令查出来的
- corn命令–定期执行某个命令或脚本
# 格式如下
min hour dayofmonth month dayofweek command
# 例如
15 10 * * * command # 每天10:15运行command命令
15 16 * * 1 command # 每周1的15:15运行command (0代表周日,6代表周六),这里的1也可以换成mon\
00 12 1 * * command # 每月1号的12:00执行
# cron时间表
crontab -l # 查看时间表
三、高级shell脚本
1.创建函数
基本的函数脚本
function name {
commands
}
# 或者
name() {
commands
}
# 调用时只需
name
# 【注】:先定义、后使用
函数返回值
- 默认退出状态码
# 默认以函数的最后一条命令的状态码作为函数的状态码
# 可以使用 $? 来获取函数的退出状态码
- return状态码
return val # val必须是一个[0,255]的整数
- 获取函数的输出
result=$(function1) # 将function1函数的输出赋给result变量
使用变量
- 传参
function param1 param2
# 调用函数时一定要加上参数 ,尤其注意在脚本中调用函数时,函数无法直接从命令行获取参数,需要在调用函数时加上参数
function $1 $2 # 调用function,使用命令行的第一、第二个参数作为函数的参数
- 在函数中处理变量
# 全局变量
# 函数外定义的变量
# 局部变量
# 函数内部用local声明的变量
local variable
数组变量和函数
- 数组变量
# 若将数组变量作为函数参数,函数只能取到数组的第一个值
# 正确做法是,重新生成一个一个数组来存放参数的值
function test{
local newarray
newarry=($(echo "$@")) # $@ 将数组的值作为List
echo "The new array value is: ${newarray[*]}"
}
- 从函数返回数组
# 函数的输出为 数组的各个元素的值 即可:
echo ${newarray[*]}
递归
function factorial{
...
local result='factorial $temp'
...
}
# 自身调用自身
创建库
# 在脚本开头添加库文件的引用即可,source命令 可以实现库文件的引用(快捷符号为 .)
source filepath/filename
. filepath/filename # 推荐使用这种方式
#!bin/bash
. ./myfuncs # 引用同目录下的myfuncs库文件
命令行定义函数
# 单行方式定义
function divem { echo $[ $1 / $2 ]; } # 注意分号
# 多行方式
promote:~ wjtang$ function multem {
> echo $[ $1 * $2 ]
> }
# 调用
promote:~ wjtang$ multem 2 5
10
# 在命令行定义的shell函数会随着shell的退出而消失
# 如果想永久保留,可以将函数定义到 .bashrc 文件下