Linux命令行与shell脚本学习笔记


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 文件下
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值