《Linux命令行與shell脚本編程大全》學習筆記

第 1 章 初识 Linux shell

win+s 输入cmd检查python是否安装成功, 或者win+s ,输入IDLE也可以使用python(IDLE是Python自带的)。

python文本编辑器:Geany,subline text,vs code ,pycharm,jupyter notebook。但是Geany,subline text在配置上很容易出现问题,不建议使用。

在vs code中运行.py文件

打开vs code——新建文件——文件另存为——某文件夹——保存类型选成:Python——在文件中输入print("Hello world! My name is Shuntai Yu.")——调试——启动调试——python file

在cmd中运行.py文件

cd 到.py文件所在位置——输入.py文件名——enter

给变量取名字是一件痛苦的事情,习惯于一些变量名字更是如此,以下是我常用的变量名:
id、tmp、foo、goo、bar、arg、fib、funct

第 1 章 初识 Linux shell

Centos基于Red Hat

第 3 章 基本的 bash shell 命令

默认bash shell提示符是美元符号($),这个符号表明shell在等待用户输入,shell提示符并非一成不变。你可根据自己的需要改变它。
绝对文件路径总是以正斜线(/)作为起始,指明虚拟文件系统的根目录。

ls -F  #会在可执行文件(比如上面的my_script文件)的后面加个星号,以便用户找出可在系统上运行的文件。
ls -a  #把隐藏文件和普通文件及目录一起显示出来
ls -R  #列出了当前目录下包含的子目录中的文件
ls -l

问号(?)代表一个字符; 星号(*)代表零个或多个字符。

ls -l my_scr?pt 
ls -l my* 
ls -l my_s*t 
ls -l my_scr[ai]pt 
ls -l f[a-i]ll  #指 定字符范围
ls -l f[!a]ll  #感叹号(!)将不需要的内容排除在外。 

cp -i test_one  test_two   #好是加上-i选项,强制shell询问是否需要覆盖已有文件。 
cp -i test_one  Documents/ 
cp -R Scripts/  Mod_Scripts  #复制整个目录的内容,在执行cp –R命令之前,目录Mod_Scripts并不存在。它是随着cp –R命令被创建的

软硬连接

同一个文件可以有多个链接,不要建立链接的链接。

ln -s data_file  sl_data_file  #建立软连接时源文件使用绝对路径
ln code_file  hl_code_file #硬链接本质上是同一文件,享有同一个inode号

重命名文件

mv fall  fzll #重命名文件
mv fzll  Pictures/  #移动文件

删除文件

rm -i fall 
rm -i f?ll #可使用通配符
rm -rf !(a|b)  #除a和b之外其它都删除

目录新建和删除

mkdir New_Dir  #创建目录
mkdir -p New_Dir/Sub_Dir/Under_Dir   #创建多个目录和子目录
rmdir New_Dir  #删除目录,rmdir只删除空目录
rm -i New_Dir/my_file 
tree 文件/目录名  #了解文件或目录结构

文件查看

查看文件:head、tail、 less、more、cat、

cat -n test1  #所有行都加行号
cat -b test1  #只有文本行加行号,空行不加。
cat、 less、 more是一回事儿
cat  #显示全部,不易控制。
more test1   #是分页显示
less test1   #less是more的升级版本
less -S #使文本整齐的显示
less -N

head test1   #查看前十行
tail test1   #查看后十行
tail -n 2 test1  #查看后两行
tail -2 test1  #查看后两行

第 4 章 更多的 bash shell 命令

ps   #ps命令只会显示运行在当前控制台下的属于当前用户的进程
ps -f  #显示完整格式的输出
ps -ef  #查看系统上运行的所有进程
ps -ef|grep lixianlong  #只查看自己的进程记录
ps -l  #S那一列,进程的状态(O代表正在运行; S代表在休眠; R代表可运行,正等待运行; Z代表僵化,进程已结束但父进程已不存在; T代表停止)
top命令跟ps命令相似,能够显示进程信息,但top是实时显示的
jobs -l  #可以查看当前终端生效的进程
kill -9 29485   #结束PID号为29485的进程,要检查kill命令是否有效,可再运行ps或top命令,看看问题进程是否已停止
df -h   #查看所有已挂载磁盘的使用情况。
du -sh  #查看当前位置每个文件大小
du -sh *   #查看当前位置所有文件大小总和

sort命令

一个例子掌握:将/etc/passwd文件按照用户ID(以冒号分割第三列)进行降序的数值排序,并将排序结果输出到/data1/lixianlong下面的test文件里面。

cat /etc/passwd |sort -t':' -k3 -nr -o /data1/lixianlong/test  
#即使test文件不存在,也会被创建出来;若test已存在,则原内容会被覆盖。
-t 指定字段分隔符
-k 指定按分隔符分隔的第几字段
-n 表示把数字识别成数字而不是字符;
-r 降序排列
-o 将结果输出到指定文件

sort -M file  #将含有时间戳日期的文件按三字符的月份来排序
sort -u file   #u是unique的意思,就是每个只出现一次
cat file
111
222
222
333
333
333

cat a.txt|sort -u   #u是unique的意思,就是每个只出现一次
111
222
333


grep命令

grep a test1  #在test1文件中搜索含有a的行
grep -n a test1   #在test1文件中搜索含有a的行,并显示行号
grep -w a test1   #在test1文件中搜索含有严格匹配a的行
grep -v a test1  #在test1文件中搜索不含有a的行
grep [ab] test1  #搜索既含有a又含有b的行
grep -e a -e b test1  #同上,搜索既含有a又含有b的行。

压缩

gzip SRR*  #将SRR开头的文件分别进行压缩
tar -cvf test.tar SRR*  #将SRR开头的文件全部压缩进同一个文件,并命名为test.tar

解压

gunzip SRR*  #将SRR开头的压缩文件分别解压缩,这个可以解压缩.gz开头的
tar -xvf test.tar  #将test.tar解压到当前路径

第 5 章 理解 shell

cat /etc/passwd |grep lixianlong  #在/etc/passwd文件中,在用户ID记录的第7个字段中列出了默认的shell程序。只要用户登录到某个虚拟控制台终端或是在GUI中启动终端仿真器,默认的shell程序就会开始运行

ls -lF /bin/bash  #bash shell程序位于/bin目录内。从长列表中可以看出/bin/bash( bash shell)是一个可执行程序:

shell的父子关系

生成子shell的三种方法:bash,进程列表,coproc

ps -f  #显示进程
bash  #生成子shell
bash  #生成子shell的子shell
ps -f  #显示进程,发现多了两个bash shell
echo $BASH_SUBSHELL  #该命令返回0,就表明没有子shell。如果返回1或者其他更大的数字,就表明存在子shell。
ps --forest  #展示这些子shell间的嵌套结构
exit  #退出子shell的子shell
exit  #退出子shell

命令列表和命令分组

命令列表

pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls   #依次运行一系列命令使用分号(;),这是命令列表

命令分组

进程列表

(pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls)  #括号括起来之后是进程列表,会生成子shell来执行该命令。
(pwd ; ls ; cd /etc ; pwd ; cd ; (pwd ; ls))  #会生成子shell的子shell。

放花括号里面的

{ command; }  #使用花括号进行命令分组不会创建子shell

置入后台和显示后台作业

sleep 3000&  #sleep命令会在后台( &)睡眠3000秒( 50分钟)
ps -f  #显示各种进程
jobs  #显示出当前运行在后台模式中的所有作业、作业状态和对应命令。
coproc sleep 10  #coproc命令是协程处理,自动置入后台,不需要加&
ps -f  
coproc My_Job { sleep 10; }   # My_Job是给协程起的名字。

外部命令和内建命令

内建命令和外部命令的内外是相对于shell而言的。
外部命令存在于bash shell之外,并不是shell程序的一部分,可用which和type命令找到它,但which只显示外部命令。

which ps
type -a ps

内建命令和shell编译成了一体,是shell的组成部分;可以用type命令来了解某个命令是否是内建的。

type cd
type exit

内建命令执行速度更快,因为内建命不需要子进程,外部命令需要子进程;有些命令既有内建命令也有外部命令,比如echo和pwd;

命令别名

一个别名仅在它所被定义的shell进程中才有效,想要在在子shell中也有效需要export该别名

alias -p  #查看当前可用的别名
alias li='ls -li'  #创建别名
li
bash
li
exit
li

第6章 使用Linux环境变量

全局环境变量对所有子shell都可见,局部变量只对创建它的shell可见。

printenv  #显示全局变量,且不排序。
env #显示全局变量,且不排序。
set  #显示全局变量、局部变量和用户定义变量,并按字母顺序排序。
printenv HOME  #查看个别环境变量
echo $HOME  #查看个别环境变量
ls $HOME   #让变量作为命令行参数

全局环境变量可用于进程的所有子shell

echo $my_variable
my_variable=Hello  #设置用户定义变量,变量名、等号和值之间没有空格;
echo $my_variable

my_variable="Hello World"
echo $my_variable

系统环境变量都是大写字母,所以自己创建的局部变量或是shell脚本,要使用小写字母,避免重新定义系统环境变量可能带来的灾难。

创建全局环境变量

将局部环境变量导出到全局环境使用export,但是全局环境指的是该shell以及该shell的子孙shell,无法使用export导出到该shell的父shell,想要在该shell的父shell中也能使用该变量需要写入bashrc文件

bash
my_variable="I am Global now"
export my_variable  #将局部环境变量导出到全局环境
echo $my_variable
bash
echo $my_variable  #在子shelll中该变量依然可见
exit
exit
echo $my_variable  #在父shelll中该变量不可见
#总之一句话:老子可以改变儿子,儿子改变不了老子,除非写入bashrc文件

刪除环境变量

unset my_variable  #用unset命令删除变量,在子shell中删除了一个全局环境变量,这只对子shell有效。该全局环境变量在父shell中依然可用;但是在父shell中删除一个全局变量,则子shell中该全局变量也消失;

PATH 是一个默认的shell环境变量,是查找命令的目录列表,由冒号分隔;个人认为添加进PATH就是加进bashrc;因为是PATH是目录列表,所以如果将软件添加进bashrc,则只写到该软件的可执行文件所在的文件夹即可,而不是将可执行文件的名也写上!

PATH=$PATH:/home/christine/Scripts
export PATH="$PATH:/home/christine/Scripts"    #export一下之后子shell也能找到/home/christine/Scripts的程序的位置;如果是将下载的软件写入bashrc则此处只写到文件夹位置,不要将最后的可执行文件名也写上,因为写入bashrc的是可执行文件所在的文件夹;

$HOME表示的是某个用户的主目录。它和波浪号( ~)的作用一样。
.bashrc文件有两个作用:一是查看/etc目录下通用的bashrc文件,二是为用户提供一个定制自己的命令别名和私有脚本函数的地方。

数组变量

mytest=(one two three four five)  #索引值分别是是0、1、2、3、4
echo ${mytest[2]} #环境变量数组的索引值都是从零开始,所以这表示显示数组中的第3个数;
echo ${mytest[*]}  #显示整个数组变量
mytest[2]=seven  #改变第三个数的值
unset mytest[2]   #unset命令删除数组中的第3个值
echo {mytest[2] }  #该值虽然被删,但是索引由空格占据
unset mytest  #删除整个数组

第7章 理解Linux文件权限

显示和设置默认八进制权限值

文件的全权限值是666(所有用户都有r和w的权限),目录的全权限值是777(所有用户都有rwx权限)。rwx对应八进制值是4、2、1分别表示可读、可写、可执行;
全权限值减去umask值就是新建文件(夹)的八进制权限值

-rw-r--r-- 1 lixianlong tanglab 0 Jun 25 13:11 tmp   #第一个字母表示文件夹后3个表示文件拥有者权限,再后3个是所属群全权限,再后3个是其他人权限;
umask  #显示umask值
umask 026   # 设置默认八进制权限值,0、2、6分别是拥有者、所属群、其他人的八进制权限值
touch newfile2  # umask值设成026后,默认的文件权限值变成了640,640就是该文件的八进制权限,即rw-r-----

更改权限

u代表拥有者; g代表所属群; o代表其他人; a代表上述所有;

touch newfile
chmod 760 newfile  # 赋予newfile权限为rwxrw----
chmod o+r newfile  # 其他人添加可读权限
chmod u-x newfile  # 拥有者去除可执行权限

第 9 章 安装软件程序

Centos是基于Red Hat企业版Linux源代码构建的免费发行版;

yum list installed  #找出系统上已安装的包

从源码安装

直接到程序的官方站点下载
tar -zxvf sysstat-11.1.1.tar.gz  #解压文件
cd sysstat-11.1.1
# README或AAAREADME文件。读这个文件非常重要。该文件中包含了软件安装所需要的操作。
./configure  #源代码安装的第一步,目的是对即将安装的软件进行配置,检查当前环境是否满足需要安装的软件的依赖关系,生成makefile文件
make  #一个软件要想在linux上执行必须是二进制文件,make命令可以将软件源码编译成二进制文件,make步骤结束时,可运行的sysstat软件程序就会出现在目录下!

第 10 章 使用编辑器

vim基础

vim编辑器有两种操作模式:普通模式(刚进入vim)和插入模式(按下i之后)
PageDown(或Ctrl+F):下翻一屏。
PageUp(或Ctrl+B):上翻一屏
q!:取消所有对缓冲区数据的修改并退出。
w filename:将文件保存到另一个文件中。
wq:将缓冲区数据保存到文件中并退出
uu:普通模式下撤回上一步
gg:定位到第一行行首
shift+g:移动到最后一行

编辑数据

dd :普通模式下删除当前光标所在行
dw :普通模式下删除当前光标所在位置的单词
d cat /dev/null > file.txt #删除file.txt的所有内容

查找替换(普通模式)

按下斜线( /)键,光标会跑到消息行,然后vim会显示出斜线,输入你要查找的文本,按下回车键,点击n表示下一个
s/old/new/g:一行命令替换所有old。
n,ms/old/new/g:替换行号n和m之间所有old。
%s/old/new/g:替换整个文件中的所有old。
%s/old/new/gc:替换整个文件中的所有old,但在每次出现时提示。

一次性注释多行与删除多行注释

第一步:打开vim
第二步:ctrl + v (此时左下角变为 VISUAL BLOCK)
第三步:上下移动光标,选上要注释的行
第四步:点一下CAPS LOCK键进入大写模式,再点一下i(此时左下角变为 INSERT)
第五步:shift + 3 ,然后再按ESC按钮(等两秒左右时间发现第三步选中的行都带上了#)
第六步:wq保存即可

删除多行注释
同样 Ctrl+v 进入列选择模式,移到光标把注释符选中,直接按下d,注释就被删除了。

第 11 章 构建基本脚本

date ; who  #两个命令一起运行,date先运行,显示当前日期和时间,who后运行,显示当前是谁登录到了系统上;
date && who  #与上等价

创建 shell 脚本文件

#!/bin/bash    #这行必不可少,指定要使用的shell,shell不会解释以#开头的行(除了以#!开头的第一行);

cat > test1
#!/bin/bash
date
who

chmod u+x test1  #拥有者有x权限才能执行,否则无法执行;

使用变量

cat test1
#!/bin/bash
# This script displays the date and who's logged on
echo The time and date are:
date

echo -n "The time and date are: "  #echo后面加上-n参数则文本字符串和命令输出显示在同一行
date
echo "Let's see who's logged into the system:"
who
echo "User info for userid: $USER"  #引用变量要加$,会在脚本运行时替换成被引用内容
echo "User info for userid: ${USER}"  #与上等价,{}帮助识别美元符后的变量名,但是有时必须要用{}
echo  $11 #这表示输出第一个位置参数后面加1
echo  ${11} # #这表示输出第11个位置参数

echo "The cost of the item is \$15"  #$会被认为是在引用变量,加上转移符(\)就会显示符号本身,通过${variable}形式引用的变量。变量名两侧额外的花括号通常用来。

var1=1
var2=2
echo $var2 is larger than $var1

test1=`who`  #反引号实现命令替换,test1获得了who命令的输出;反引号不是单引号,反引号在Esc键下面;
test2=$(date)  #$()也可以实现命令替换,test2获得了date命令的输出;
echo $test1
echo $test2

today=$(date +%y%m%d)  #
ls /usr/bin -al > log.$today  #生成日志文件名常用的一种技术

输入输出重定向

> 实现输出重定向,若文件已存在则覆盖
>> 实现追加数据,原文件不会覆盖,只会追加内容

date > test6  #  >  实现输出重定向
who >> test6  # >> 追加

wc < test6  #对test6文件进行wc操作

sort -n << end  # << 是内联输入重定向,对以下输入内容进行<<左边的处理,并且在输入<< 右边的内容后结束
> 12
> 32
> 44
> 9
> end
9
12
32
44

 wc << ooo  #对以下输入内容进行<<左边的处理,并且在输入<< 右边的内容后结束
> i am young
> i am strong
> i am hansom
> ooo
 3  9 35

管道

管道可以将一个命令的输出作为另一个命令的输入,但管道串起的两个命令是同时运行

;和&&等价的,命令依次执行;|是前一个命令的输出作为后一个命令的输入,但实际上同时执行

cat test1|wc  #将cat test1的结果作为wc命令的输入,但是二者是同时运行的,第一个命令产生输出的同时,输出会立即送给第二个命令

#执行以下三个知差别
cd .. | ls -F  #同时执行cd .. 和ls -F
 cd .. ; ls -F  #先执行cd .. 后执行ls -F
cd .. && ls -F  #先执行cd .. 后执行ls -F

数学运算

expr

只支持整数运算

expr 10+2  # +的左右要有空格才可以成功运算
10+2

expr 1.5 \* 1.5  #expr只支持整数运算
expr: non-integer argument

expr 10 + 2
12

expr 10 - 2
8

expr 10 \* 2  #乘以号要使用转义符进行转义
20

expr 10 / 2
5
$ cat test6
#!/bin/bash
# An example of using the expr command
var1=10
var2=20
var3=$(expr $var2 / $var1)
echo The result is $var3

$ chmod u+x test6

$ ./test6

$[]

使用$[ ] 将数学表达式包起来,只支持整数运算

$ cat test1
#!/bin/bash
var1=$[1 + 5]
var2=$[$var1 * 2]
var3=2
var4=$[$var1 * ($var2 - $var3)]  # $[ ]里面的*不需要转义,不会被看做通配符。
var5=$[1.5 * 1.5]  #这步代码将失败,因为$[]和expr一样也只支持整数运算
echo $var1
echo $var2
echo The final result is $var4
echo $var5
$ chmod u+x test1
$ ./test1
The final result is 500

bc

bc基本用法

$ bc -q   # -q 选项可以不显示冗长的欢迎信息。
scale=6   #在计算中保留的小数位数
var1=10
var1 * 4
40
var2 = var1 / 3
print var2
3.333333
quit   # 退出bc

在脚本中使用bc

例子1

$ cat test1
#!/bin/bash
var1=$(echo "scale=4; 3.44 / 5" | bc)
echo The answer is $var1

$ chmod u+x test1
$ ./test1
The answer is .6880

例子2

$ cat test1
#!/bin/bash
var1=20
var2=3.14159
var3=$(echo "scale=4; $var1 * $var1" | bc)
echo $var3
var4=$(echo "scale=4; $var3 * $var2" | bc)
echo $var4
var5=$(echo "scale=4; ($var1 + $var2)*$var3/$var4" | bc)
echo The final result is $var5

例子3
例子2可以改为如下

$ cat test2
#!/bin/bash
var1=20
var2=3.14159
var3=$(bc << eof
scale=4
$var1 * $var1
eof
)
echo $var3

var4=$(bc << ooo
scale=4
$var3 * $var2
ooo
)
echo $var4

var5=$(bc << tmp
scale=4
($var1 + $var2)*$var3/$var4
tmp
)

echo The final result is $var5

可以继续改为如下

重点

表达式中用到的变量若是在表达式之外被赋值的则加上$,若是在表达式内被赋值的则不加$

$ cat test3
#!/bin/bash
var1=20
var2=3.14159

var5=$(bc << eof
scale=4
var3=($var1 * $var1)
var4=(var3 * $var2)    # var3是在表达式内被赋值的,不加$
($var1 + $var2)*var3/var4   # var3 和 var4都是表达式内被赋值的,不加$
eof
)
echo The final result is $var5

$( )、$[ ]、${ }辨析

$( )、$[ ]、${ }作用分别是命令替换、数学运算、界定边限

第 12 章 使用结构化命令

if-then-fi

若if后面的命令可成功运行则执行then后面的命令

#!/bin/bash
# testing the if statement
if pwd  # pwd可以换成date或who试下
then  #这一句和上一句可以合并为 if pwd  ; then
echo "It worked"
fi

if-then-else-fi

若if后面的命令可成功运行则执行then后面的命令,否则执行else后面的命令;

#!/bin/bash
if pwd
then echo pwd is available
else
echo pwd is not available
fi

if-then-elif-then-fi

if command1
then
command set 1
elif command2
then
command set 2
elif command3
then
command set 3
elif command4
then
command set 4
fi
若command1成功执行则执行command set 1,否则执行command2;
若command2成功执行则执行command set 2,否则执行command3;
若command3成功执行则执行command set 3,否则执行command4;
若command4成功执行则执行command set 4;

#!/bin/bash
if PWD
then echo PWD is available
elif PWd
then echo PWD is not avaliable, but PWd is available
elif pwd
then echo PWD and PWd is not avaliable, but pwd is available
fi

test 命令

test命令可以在if-then语句中测试不同条件;test后的测试条件可以使用[ ]来替代,测试条件在[]中前后要空一格;test命令中不能使用带小数点的值(因为bash shell只能识别整数)。

test后沒有内容和内容为空都将执行else

$ cat test1  
#!/bin/bash
if test  #可以改为if test [ ]   #test后沒有内容将执行else  
then
echo "True"
else
echo "False"
fi

$ ./test1
False
$ cat test1  
#!/bin/bash
my_variable=""
if test $my_variable  #可以改为if test [ $my_variable ]   # test后内容为空将执行else,不为空则执行then  
then
echo "True"
else
echo "False"
fi

$ ./test1
False

test命令可以进行:数值比较、字符串比较、文件比较

数值比较

e是equal; g是great; l是less; n是not; t是than;

n1 -eq n2 检查n1是否与n2相等
n1 -ge n2 检查n1是否大于或等于n2
n1 -gt n2 检查n1是否大于n2
n1 -le n2 检查n1是否小于或等于n2
n1 -lt n2 检查n1是否小于n2
n1 -ne n2 检查n1是否不等于n2

$ cat test1
#!/bin/bash
value1=10  #如果是10.1就会无法判断,bash shell只能处理整数
if [ $value1 -gt 5 ]  #条件与中括号前后要有一个空格
then
echo "$value1 is greater than 5"
fi

字符串比较

str1 = str2 检查str1是否和str2相同
str1 != str2 检查str1是否和str2不同
str1 < str2 检查str1是否比str2小,
str1 > str2 检查str1是否比str2大
-n str1 检查str1的长度是否非0
-z str1 检查str1的长度是否为0,若该字符未被定义,会被认为是0

#!/bin/bash
# testing string equality
testuser=lixianlong
#
if [ $USER = $testuser ]
then
echo "Welcome $testuser"
fi

字符串比较时,26个英文字母从a到z是依次变大,并且大写小于小写;而sort命令排序时,小写字母大于大写;注意:字符串比较不是比较字符串的长度!

$ cat test1
#!/bin/bash
val1=aa
val2=b
if [ $val1 \> $val2 ]  #大于号和小于号必须转义,否则会认为是重定向
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi

$ ./test1
aa is less than b

文件比较

test命令的文件比较功能
比 较 描 述
-d file 检查file是否存在并是一个目录
-e file 检查file是否存在
-f file 检查file是否存在并是一个文件
-r file 检查file是否存在并可读
-s file 检查file是否存在并非空
-w file 检查file是否存在并可写
-x file 检查file是否存在并可执行
-O file 检查file是否存在并属当前用户所有
-G file 检查file是否存在并且默认组与当前用户相同
file1 -nt file2 检查file1是否比file2新
file1 -ot file2 检查file1是否比file2旧

$ cat test1
#!/bin/bash
if [ -d $HOME ] && [ -w $HOME/testing ]  # &&是逻辑和,两个条件都满足才能执行then, ||是逻辑或,两个条件有一个满足就能执行then
then
then
echo "The file exists and you can write to it"
else
echo "I cannot write to the file"
fi

(( ))和[[ ]]

(( ))里面可以使用高级数学表达式,并且>不需要转义,但是在[ ]中>进行判断需要转义

$ cat test1
#!/bin/bash
val1=10
if (( $val1 ** 2 > 90 ))  # **是双括号命令符号的一种,表示幂运算
then
(( val2 = $val1 ** 2 ))
echo "The square of $val1 is $val2"
fi

$ ./test1
The square of 10 is 100

双方括号可以通过正则表达式进行模式匹配,不是所有的shell都支持双方括号

$ cat test1
#!/bin/bash
if [[ $USER == l* ]]  #
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi

$ ./test1
Hello lixianlong

case命令

在一组可能的值中寻找特定值,需要很长的if-then-elif-then-else-fi语句,使用case更方便;case命令提供了一个更清晰的方法来为变量每个可能的值指定不同的选项。

case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac

$ cat test1
#!/bin/bash
case $USER in
lixianlong | Jack | Summer) echo "Welcome, $USER"
echo "Shuntai Yu is the best!";;
Bitch)
echo "Fuck you ! son of bitch !";;
jessica)
echo "Do not forget to log off when you're done";;
*)
echo "Sorry, get out of here!";;
esac

输入金陵十二钗人名可到对应的判词脚本

#!/bin/bash
case $1 in
daiyu | baochai|lindaiyu|xuebaochai)
echo "可叹停机德,堪怜咏絮才。"
echo "玉带林中挂,金簪雪里埋。";;

yuanchun|jiayuanchun)
echo "二十年来辨是非,榴花开处照宫闱。"
echo "三春争及初春景,虎兔相逢大梦归。";;

tanchun|jiatanchun)
echo 才自清明志自高,生于末世运偏消。
echo "清明涕泣江边望,千里东风一梦遥。";;

xiangyun|shixiangyun)
echo 富贵又何为?襁褓之间父母违。
echo "展眼吊斜辉,湘江水逝楚云飞。";;

miaoyu)
echo 欲洁何曾洁,云空未必空。
echo "可怜金玉质,终陷淖泥中。";;


yingchun|jiayingchun)
echo 子系中山狼,得志便猖狂。
echo "金闺花柳质,一载梦黄梁。";;

xichun|jiaxichun)
echo "堪破三春景不常,缁衣顿改昔年妆。"
echo "可怜秀户侯门女,独卧青灯古佛旁。";;

fengjie|wangxifeng)
echo "凡鸟偏从末世来,都知爱慕此生才。"
echo "一从二令三人木,哭向金陵事可哀。";;

liwan)
echo "桃李春风结子完,到头谁似一盆兰?"
echo "如冰水好空相妒,枉与他人作笑谈。";;

qinkeqing)
echo "情天情海幻情身,情既相逢必主淫。"
echo "慢言不肖皆荣出,造衅开端实在宁。";;

qinshishang|shishang|laoqin)
echo "FUCK YOU! 老秦";;
*)
echo "此人非金陵十二钗";;
esac

$ ./test1 xuebaochai
可叹停机德,堪怜咏絮才。
玉带林中挂,金簪雪里埋。

第 13 章 更多的结构化命令

for-in-do-done

知识点1

复杂值用引号括住

$ cat test1
#!/bin/bash
for id in wuhan "New York"  don\'t "don't "  '“”' "$¥¥"   #复杂值可以用引号或者转义符(\)
do
echo $id
done

$ ./test1
wuhan
New York
don't
don't
“”
$¥¥

知识点2

遍历列表和给列表添加内容

$ cat test1
#!/bin/bash
list="Guangzhou Shenzhen Shangqiu"  #给定一个列表
list=$list" Beijing"  #可以在列表后再加值,注意:$list"后面有一个空格,不加空格会和最后一个词融合成为一个。
for id in $list  #使用for去遍历列表
do
echo "Have you ever visited $id?"
done

知识点3

遍历外部文件列表

$ cat list
Guangzhou
Shenzhen
Shangqiu
Beijing

$ cat test1
#!/bin/bash
list="/home/lixianlong/list"  #给定一个列表,这里使用绝对路径,也可以使用相对于test1所在位置的相对路径
for id in $(cat $list)  #使用for去遍历列表
do
echo "Have you ever visited $id?"
done

知识点4

bash shell默认分隔符为空格、制表符、换行符,若想更改分隔符需要指定IFS

$ cat name.txt
Jack,Bruce,Summer
Nick,Lily,Marry

$ cat test1
#!/bin/bash
IFS=$'\n',  #这里若写成IFS=$'\n'则只识别换行符
name=name.txt
for id in $(cat $name)
do
echo My name is $id
done

知识点5

C语言的for

$ cat > test1
#!/bin/bash
for (( id=1; id <= 5; id++ ))
do
echo "The next number is $id"
done

$ ./test1
The next number is 1
The next number is 2
The next number is 3
The next number is 4
The next number is 5
$ cat > test1
#!/bin/bash
for (( id=1, foo=6; id <= 6, foo>=1; id++, foo-- ))
do
echo "$foo - $id"
done

$ ./test1
6 - 1
5 - 2
4 - 3
3 - 4
2 - 5
1 - 6

while-do-done

命令行形式

$ cat name.txt
Jack
Bruce
Summer

$ cat name.txt |while read id; do (echo my name is $id); done
my name is Jack
my name is Bruce
my name is Summer

脚本形式

$ cat test1
#!/bin/bash
var1=6
while [ $var1 -gt 0 ]
do
echo $var1
var1=$[ $var1 - 1 ]
done

$ ./test1
6
5
4
3
2
1

until-do-done

$ cat test1
#!/bin/bash
var1=100
until [ $var1 -eq 0 ]
do
echo $var1
var1=$[ $var1 - 25 ]
done

$ ./test1
100
75
50
25

通过嵌套循环实现

Q1
Outer loop: 5
Inner loop: 5 * 1 = 5
Inner loop: 5 * 2 = 10
Outer loop: 4
Inner loop: 4 * 1 = 4
Inner loop: 4 * 2 = 8
Outer loop: 3
Inner loop: 3 * 1 = 3
Inner loop: 3 * 2 = 6
Outer loop: 2
Inner loop: 2 * 1 = 2
Inner loop: 2 * 2 = 4
Outer loop: 1
Inner loop: 1 * 1 = 1
Inner loop: 1 * 2 = 2
Outer loop: 0
Inner loop: 0 * 1 = 0
Inner loop: 0 * 2 = 0

Q2
Outer loop: 3
Inner loop: 3 / 1 = 3.0000
Inner loop: 3 / 2 = 1.5000
Inner loop: 3 / 3 = 1.0000
Inner loop: 3 / 4 = .7500
Outer loop: 2
Inner loop: 2 / 1 = 2.0000
Inner loop: 2 / 2 = 1.0000
Inner loop: 2 / 3 = .6666
Inner loop: 2 / 4 = .5000
Outer loop: 1
Inner loop: 1 / 1 = 1.0000
Inner loop: 1 / 2 = .5000
Inner loop: 1 / 3 = .3333
Inner loop: 1 / 4 = .2500

控制循环

break和continue控制循环

break命令

break n n是从里层往外层数的,最里面的n为1。n若不写则默认为1,跳出当前循环。将n设为2,则跳出下一级外部循环,以此类推。

continue命令

continue n n也是从里层往外层数的。

第 14 章 处理用户输入

位置参数

$ cat test1
#!/bin/bash
echo $1  #输出第一个位置参数
echo $11  #输出第一个位置参数,然后加1
echo ${11}  #输出第11个位置参数

$ ./test1 a b c d e f g h i j k l 
a
a1
k

basename和dirname

$ cat test1
#!/bin/bash
echo $(basename $0)  #basename实现掐头
echo $(dirname $0)  #dirname实现去尾
echo $0

$ bash /data1/lixianlong/wangxiao/2020-4-16/small/tmp/test1  #运行此脚本
test1  #掐头后的结果
/data1/lixianlong/wangxiao/2020-4-16/small/tmp  #去尾后的结果
/data1/lixianlong/wangxiao/2020-4-16/small/tmp/test1

#也可以以下面的方式运行
$ chmod u+x test1  #赋予执行权限
$ bash /data1/lixianlong/wangxiao/2020-4-16/small/tmp/test1  #直接运行脚本

$#、$*、$@

$#为位置参数的个数; $*、$@为所有位置参数

$ cat  test1
echo $#
echo ${!#}   #输出最后一个位置参数,不能使用${$#},因为花括号里面不能有$

$ ./test1 a b c
3  #位置参数的个数
c  #最后一个位置参数

$ ./test1 a b c d e f g
7
g
$ cat test1
echo $*
echo $@

$ ./test1 a b c d e f g
a b c d e f g
a b c d e f g

shift

shift n 将每个位置参数向左移动n个位置,这样可以跳过不需要的位置参数。

$ cat test1
#!/bin/bash
echo "The original parameters: $*"
shift 2  #将每个位置参数向左移动n个位置
echo "Here's the new first parameter: $1"

$ ./test1 1 2 3 4 5
The original parameters: 1 2 3 4 5
Here's the new first parameter: 3

分离参数和选项

$ cat test1
#!/bin/bash
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  #当出现--首先左移一位,然后跳出while循环
break ;;
*) echo "$1 is not an option";;
esac
shift  #这个shift与下面的$@呼应,每进来一个option,这个shift都进行了移除,不会再次出现
done
#
count=1
echo $@  #因为上面的shift将所有的option都移除了,所以这里只输出--后的位置参数
for param in $@
do
echo "Parameter #$count: $param"
count=$[ $count + 1 ]
done


$ ./test1 -c -a -b test1 test2 test3
Found the -c option
Found the -a option
Found the -b option
test1 is not an option
test2 is not an option
test3 is not an option

$ ./test1 -c -a -b test1 -- test2 test3
Found the -c option
Found the -a option
Found the -b option
test1 is not an option
test2 test3
Parameter #1: test2
Parameter #2: test3

getopt :略

read

read的命令有一些选项
-p:直接在read命令行指定变量
-s:隐藏输入的数据
-t:超时选项
-n:要求输入特定数目字符

$ cat test1
#!/bin/bash
echo -n "Enter your name: "   # -n选项使用户可以脚本输入数据。
read id
echo "Hello $id, welcome to my program. "

$ ./test1
Enter your name: Summer
Hello Summer, welcome to my program.
$ cat test1
#!/bin/bash
read -p "Enter your name: " id foo   #-p直接在read命令行输入变量,并且指定了两个变量。
echo "Your first name is $id, your last name is $foo"

$ ./test1
Enter your name: Shuntai Yu
Your first name is Shuntai, your last name is Yu
$ cat test1
#!/bin/bash
if read -t5 -n9 -s -p "Enter your name: "
then
echo
echo "Your name is $REPLY"
else
echo
echo "Too late!"
fi

第 15 章 呈现数据

输入重定向 <

cat可以显示STDIN的输入,而标准STDIN是指键盘输入

$ cat
this is the first line  #这一行是键盘输入的,即STDIN
this is the first line   #这一行是cat显示的
this is the second line   #这一行是键盘输入的,即STDIN
this is the second line   #这一行是cat显示的

<可以将STDIN强制变成文件

$ cat test1
this is the first line
this is the second line

$ cat < test1  # 将STDIN改成了test1文件,<称为输入重定向符号
this is the first line
this is the second line

输出重定向 >

标准STDOUT是屏幕显示,但是>可以将STDOUT导入文件

$ ls -l > test1  #本来要显示在屏幕上的内容被导进了文件,这会覆盖原内容
$ ls -l >> test1   #这样只追加,不覆盖

$ cat test1
total 4
-rw-r--r-- 1 lixianlong tanglab 34 Jun 28 18:42 name.txt
-rwxr--r-- 1 lixianlong tanglab  0 Jun 30 12:02 test1

错误重定向2>

$ ./error.txt 2> test1  #将错误输出导进文件

$ cat test1
-bash: ./error.txt: No such file or directory

1>和2> 正确和错误输出分别导进不同文件

$ cat test
this is a test line

$ cat error.txt  #因为没有 error.txt文件,所以会报错
cat: error.txt: No such file or directory

$ cat test error.txt 1> T.txt 2> F.txt   # 1>将正确输出导入T.txt,将错误输出导入F.txt

$ cat T.txt
this is a test line

$ cat F.txt
cat: error.txt: No such file or directory

&>和1> S.txt 2>&1正确和错误输出导进同一个文件

1> S.txt 2>&1 S.txt左边表示正确的输出导进S.txt文件,S.txt右边错误的输出当做正确的也导进该文件
2> S.txt 1>&2 S.txt左边表示错误的输出导进S.txt文件,S.txt右边正确的输出当做错误的也导进该文件

巧记:>&前面的东西变成>&后面的东西,比如1>&2是1变成2,1是正确的,2是错误,意思就是将本来正确的输出变成错误的输出。

$ cat test
this is a test line

$ cat test error.txt &> S.txt   # error.txt文件并不存在,所以cat它会报错
$ cat test error.txt 1> S.txt 2>&1  #这句意思是将正确的输出导进S.txt,2>&1表示将2变成1,也就是将本来错误的输出变成正确的输出,也导进文件S.txt。

$ cat test error.txt > S.txt 2>&1    #1可以省略,这句与上两句同
$ cat test error.txt 2> S.txt 1>&2    #这句意思是将错误的输出导进S.txt,1>&2表示将1变成2,也就是将本来正确的输出变成错误的输出,也导进文件S.txt。

$ cat S.txt
this is a test line
cat: error.txt: No such file or directory
$ cat test
echo "This is an error" >&2  # >&2等同于1>&2,表示将1变成2,本来该句是正确输出,现在被变成了错误输出。
echo "This is normal output"

$ ./test  #标准STDOUT会将两句都显示
This is an error
This is normal output

$ ./test > T.txt   # > 只将正确输出导入T.txt
This is an error  #这句被当成了错误输出没有导进去,在屏幕上显示了出来

$ cat T.txt  #T.txt里面只有正确输出
This is normal output

$ ./test 2> F.txt  # 2> 只将错误输出导入F.txt
This is normal output   #这句是正确输出没有导进去,在屏幕上显示了出来

$ cat F.txt  #F.txt里面只有错误输出
This is an error

阻止命令输出

在Linux系统上null文件的标准位置是/dev/null。你重定向到该位置的任何数据都会被丢掉,不会显示。将错误输出导向它可避免出现错误消息,也不保存错误消息。

$ cat test
echo "this is a test"
echo "this is a test'  #这一句将会报错

$ ./test 2> /dev/null #避免出现错误消息,也不保存错误消息,只出现正确消息。
this is a test

$ ./test 1> /dev/null  #避免出现正确消息,也不保存正确消息,只出现错误消息。
./test: line 2: unexpected EOF while looking for matching `"'
./test: line 3: syntax error: unexpected end of file

$ ./test 1> /dev/null 2>&1  #正确和错误的消息都不出现
$ ./test 2> /dev/null 1>&2  #正确和错误的消息都不出现

$ cat /dev/null

$ cat /dev/null > test  #将/dev/null内容导入文件,会清除文件内容
$ cat test

tee

tee命令可以分流,可将命令结果一部分显示到屏幕上,另一部分导进文件,tee -a是追加到文件

$ cat test
111111

$ date |tee test  #tee命令分流,并覆盖test文件原内容
Tue Jun 30 15:08:58 CST 2020

$ cat test
Tue Jun 30 15:08:58 CST 2020

$ date|tee -a test  #tee命令分流,并追加到test文件里
Tue Jun 30 15:08:58 CST 2020

$ cat test
111111
Tue Jun 30 15:14:32 CST 2020

第 16 章 控制脚本

后台运行和nohup

命令后加&就是后台运行,当后台运行时,仍用终端显示器来显示STDOUT和STDERR消息。终端会话退出,后台进程也会随之退出。加上nohup即使退出终端,命令也会一直运行到结束。

$ cat test
#!/bin/bash
count=1
while [ $count -le 10 ]
do
sleep 1
echo $count
count=$[ $count + 1 ]
done

$ ./test &  #命令后加&就是后台运行
$ nohup ./test &  #退出putty也没关系。但对相同目录中的多个文件nohup时要小心,因为所有的输出都会被发送到同一个nohup.out文件中,这会造成混乱。

Ctrl+C 和 Ctrl+Z 和jobs -l

Ctrl+C  #中断进程
Ctrl+Z   #暂停进程,
ps -l   #显示进程
kill -9 PID号   #杀掉该PID号的进程

jobs   #查看shell正在处理的作业
jobs -l    #还可看到作业的PID

$ cat test-1
#!/bin/bash
count=1
while [ $count -le 10 ]
do
sleep 1
echo $count甲
count=$[ $count + 1 ]
done

重启停止的作业

$ jobs -l
[1]- 32358 Stopped                 ./test  #[ ]里面的1才是作业号,莫搞错
[2]+ 32428 Stopped                 ./test-2

$ bg    #若只有一个被暂停的作业,可以直接bg或者fg
$ bg 1  #多个作业bg加作业号可以放到后台重启该作业号对应的作业
$ bg 2
$ fg 1   #fg加作业号可以放到前台重启该作业
$ fg 2

定时运行作业

使用at命令。设置开始运行的时间有很多种格式,具体参考书本

$ at -f test-2 now  #-f指定要运行的文件,文件后为开始运行的时间
job 27 at Tue Jun 30 20:42:00 2020
You have mail in /var/spool/mail/lixianlong  #去 /var/spool/mail/lixianlong就可以找到上面命令的输出结果,有时候这句可能没有,可以使用jobs命令试一下。知道地方在哪里就可以了。

$ at -f test-2 now+1min  #从现在起1min之后运行test-2脚本

$ atq   #atq命令可以查看系统中有哪些作业在等待
$ atrm 18  #atrm加作业号就可删除,作业号是第一列

第 17 章 创建函数

定义函数的两种方法

方法1
function 函数名 {
命令行
}

方法2
函数名() {
命令行
}

$ cat test
#!/bin/bash
function funct {          #这一行可改为funct () { 
echo "This is an example of a function"
}
sleep 5
funct  #这里直接使用上面定义好的函数名,注意,不要加$

$ ./test-2   #睡眠5秒之后执行funct
This is an example of a function

向函数传递参数

bash shell会将函数当作小型脚本来对待。这意味着你可以像普通脚本那样向函数传递参数;

$ bar=666  #在命令行赋值一个变量

$ cat test
#!/bin/bash
tmp=888   #在脚本中赋值一个变量
#
function funct {
echo $1  #这个$1是函数的第一个位置参数
echo $2
echo $tmp  #函数可以使用脚本中赋值的变量,这句可以执行
echo $bar  #函数不可以使用命令行赋值的变量,这句将无法执行
}
#
echo $bar  #脚本无法输出$bar,因为脚本运行新开了一个shell,无法使用命令行赋值的变量,想运行该句,需要提前执行export bar
echo $tmp  #脚本可以使用脚本中赋值的变量
arg=6
foo=$(funct 10 20 $3)  #向函数funct传递三个位置参数,$3意思是将脚本的第四个位置参数传递了进来,这样就将脚本的位置参数传给了函数。
echo $foo  #函数中的echo $bar将无法执行,因为bar是命令行赋值的变量,但是提前执行export bar,函数中的echo $bar也可以执行。

$ ./test 111 222 333
                  #$bar 无法输出,所以这行空白
888
10 20 888

$ export bar  #命令行中通过export将bar变成全局变量

$ ./test 111 222 333
666
888
10 20 888 666

local

local可以将变量局限于函数内部。
下面的例子函数变量和脚本变量重名,加上local之后可以分开

$ cat test
#!/bin/bash
#
function funct {
tmp=$[ $value + 5 ]  #这一句改成local tmp=$[ $value + 5 ] 则这个tmp变量只局限于函数内
result=$[ $tmp * 2 ]
}
#
tmp=4
value=6
funct
echo "The result is $result"
if [ $tmp -gt $value ]
then
echo "tmp is larger"
else
echo "tmp is smaller"
fi

$ ./test
The result is 22
tmp is larger

#若在函数中的tmp前加上local则结果与上相反
$ ./test
The result is 22
tmp is smaller

数组变量和函数

将该数组变量作为函数参数,函数只会取数组变量的第一个值。

$ cat test
#!/bin/bash
function foo {
echo "The parameters are: $@"
thisarray=$1
echo "The received array is ${thisarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
foo $myarray  #将$myarray作为函数的位置参数,但是函数只取了数组变量的第一个值。
foo ${myarray[*]}

$ ./test
The original array is: 1 2 3 4 5
The parameters are: 1
The received array is 1
The parameters are: 1 2 3 4 5
The received array is 1

函数递归

局部函数变量的一个特性是自成体系。除了从脚本命令行处获得的变量,自成体系的函数不
需要使用任何外部资源。这个特性使得函数可以递归地调用,也就是说,函数可以调用自己来得到结果。

算法实现:5! = 1 * 2 * 3 * 4 * 5 = 120

$ cat > test
#!/bin/bash
function factorial {
if [ $1 -eq 1 ]
then
echo 1
else
local temp=$[ $1 - 1 ]
local result=$(factorial $temp)
echo $[ $result * $1 ]
fi
}
read -p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"

函数调用

函数可以放在脚本中随时调用(创建函数库);也可以使用一次性的函数(命令行创建函数);也可以放在bashrc中永久化;

创建函数库

函数库可以在多个脚本中使用同一段代码,不需要在每个脚本中都定义一次函数。

$ cat myfuncts
function addem {
echo $[ $1 + $2 ]
}
function multem {
echo $[ $1 * $2 ]
}
function divem {
if [ $2 -ne 0 ]
then
echo $[ $1 / $2 ]
else
echo -1
fi
}

$ cat test
#!/bin/bash
. ./myfuncts   #这句可以改写成source ./myfuncts,这句执行之后才可以调用库中的函数,这里的./使用的相对路径,也可以使用具对路径,这句改写成 . /data1/lixianlong/wangxiao/2020-4-16/small/tmp/myfuncts  
tmp=$(addem 10 15)
foo=$(multem 10 15)
arg=$(divem 10 15)
echo $tmp
echo $foo
echo $arg

$ ./test
25
150
0

命令行上创建函数

命令行上定义函数可以单行定义,也可以多行定义

#单行定义
$ function divem { echo $[ $1 / $2 ]; }   #注意加分号和空格

$ divem 100 5
20

#多行定义
$ function tmp {
> echo $[ $1/$2 ]
> }

$ tmp 100 5
20

函数放在.bashrc中

把函数放在.bashrc中有两种方法,一种是直接在.bashrc文件中定义函数,还有一种是将写有很多函数的函数库脚本写进.bashrc

$  cat >> ~/.bashrc
foo() { echo $[ $1+$2 ]; }

$ source ~/.bashrc
$ foo 1 1
2

将myfuncts文件写进.bashrc使其永久化。

cat >> ~/.bashrc
source /data1/lixianlong/wangxiao/2020-4-16/small/tmp/myfuncts

$ source ~/.bashrc

$ addem 10 2
12

$ multem 10 2
20

$ divem 10 2
5

第 19 章 初识sed和gawk

sed 的三个选项
-e 多个命令; -f 命令放在文件中; -n 不产生命令输出。

$ cat test
The: quick brown fox jumps over the lazy: dog.
The: quick brown fox jumps over the lazy: dog.
The: quick brown fox jumps over the lazy: dog.
The: quick brown fox jumps over the lazy: dog.

$ cat script.sed
s/brown/green/
s/dog/cat/' 

sed执行多个命令要用-e,命令之间用分号,命令末尾和分号紧挨着。
如果不想用分号,也可以用bash shell中的次提示符来分隔命令。只要输入第一个单引号标示
出sed程序脚本的起始( sed编辑器命令列表), bash会继续提示你输入更多命令,直到输入了标示
结束的单引号。

将test内容变成下面的

The: quick green fox jumps over the lazy: cat.
The: quick green fox jumps over the lazy: cat.
The: quick green fox jumps over the lazy: cat.
The: quick green fox jumps over the lazy: cat.

**方法1:**单行写命令加命令之间加分号
$ sed -e 's/brown/green/; s/dog/cat/' test

**方法2:**多行写命令不加分号,直接换行
$ sed -e '
> s/brown/green/
> s/dog/cat/' test

**方法3:**将命令放入脚本
sed -f script.sed test

gawk

-F指定分隔符; -f指定文件
让test文件中:分隔的第一部分变成aaa,然后再让改换之后的第一步分is very love最后一部分,然后在处理数据前运行一句话This is begin:,在处理数据后运行一句话This is end。

gawk的模式

极简模式:gawk '{}' 文件

简单模式:gawk 选项 寻址 '{print }' 文件

选项 -F指定分隔符 --re-interval识别正则表达式间隔

**方法1:**单行写命令加命令之间加分号
$ gawk -F: 'BEGIN {print "This is begin:"}
{$1="aaa"; print $1 " is very love " $3}
END {print "This is end"}' test

**方法2:**多行写命令不加分号,直接换行
$ gawk -F: 'BEGIN {print "This is begin:"}
{$1="aaa"
print $1 " is very love " $3}
END {print "This is end"}' test

**方法3:**将命令放入脚本
$ cat script   
BEGIN {print "This is begin:"}
{$1="aaa"; print $1 " is very love " $3}
END {print "This is end"}

$ gawk -F: -f script test

**方法3`-1:**将命令放入脚本
$ cat script
BEGIN {print "This is begin:"}
{$1="aaa"
foo=" is very love "
print $1 foo $3}   #gawk引用变量时不加$,并且注意该变量要定义到该{ }里面,不要定义错位置。
END {print "This is end"}

$ gawk -F: -f script test

sed基础

sed的模式

极简模式:sed '{}' 文件

简单模式:sed 选项 '{[地址][命令] 新内容}' 文件

当命令只有一条的时候可去掉花括号,有多条命令则命令之间用分号,或者每条命令另起一行。
选项 -n禁止输出
地址和命令紧挨着
地址:可以数字行寻址,也可以字符匹配行寻址
命令:s替换,d删除,a后追加,ir插入,c修改,y转换字符

w只保存匹配模式的行

$ cat test
This test is the first test  #/bin/bash
This test is the second test  #/bin/bash
This test is the third test
this line is so stupid
This test is the fourth test
this line is another stupid line


$ sed 's/test/trial/2' test   #对每一行的第2处匹配进行替换
$ sed 's/test/trial/g' test   #对文本所有匹配处进行替换
$ sed 's/test/trial/gw tmp.txt' test   #对文本所有匹配处进行替换,然后结果保存进tmp.txt文件
$ sed -n 's/test/trial/gpw tmp.tx' test   #对文本所有匹配处进行替换,然后结果保存进tmp.txt文件,并将被替换命令修改过的行输出到显示屏(-n和p结合即是此意。)

#将/bin/bash换成/bin/csh有以下两种方法:
$ sed -n 's/\/bin\/bash/\/bin\/csh/gpw tmp.tx' test    #方法1:对 / 进行转义
$ sed -n 's!/bin/bash!/bin/csh!gpw tmp.tx' test    #方法1:使用 ! 来替代 / 作为字符串的分隔符号

$ sed -n '2,3s/test/trial/2pw tmp.txt' test   #对文本的第2,3两行进行替换,并将替换结果保存进tmp.txt文件
$ sed -n '2,$s/test/trial/2pw tmp.txt' test   #对文本的第2到最后一行进行替换,并将替换结果保存进tmp.txt文件

$ sed -n '{
2,3s/test/trial/2pw tmp.txt
3,$s/test/trial/1w foo.txt
}' test    #对文件2,3两行第二处匹配的test替换为trial,并将替换好的结果存进tmp.txt(注意只存匹配的行);同时将文件3到尾行第一处匹配的test替换为trial,并将替换好的结果存进foo.txt(注意只存匹配的行)


#d是删除;i是前面插入;a是后面追加;c是替换;
$ sed '3,$d' test   #将第3行至最后一行删除
$ sed '/stupid/d' test   #将含有stupid的行删除
$ sed '/second/,/stupid/d' test   #second触发删除,stupid终止删除。将匹配second的行(注意second第一次出现)至匹配stupid 的行(注意stupid第一次出现)都删除。


$ sed '3,5i oooooo' test   #在test文本第3到第5行每行前面插入一行oooooo;将i换成a就是行后面追加;将i换成c就是改变内容
$ sed '3,5i\
oooooo' test   #与上同只不过改成了两行

$ sed '/stupid/i oooooo' test    #与上同,只不过改变了寻址方式



$ cat foo.txt
ooooooooo
ooooooooo

$ sed '3r foo.txt' test   #将foo.txt的文本内容完整的插入test文件的第3行
This test is the first test  #/bin/bash
This test is the second test  #/bin/bash
This test is the third test
ooooooooo
ooooooooo
this line is so stupid
This test is the fourth test

sed的单字符转换

使用y

$ sed '1y/bin/-!&/' test   #将第一行的字符b变成-,所有的i变成!,所有的n变成&。注意并不是将bin变成-!&,将bin变成-!&应该使用sed的s命令而不是y命令
Th!s test !s the f!rst test  #/-!&/-ash
This test is the second test  #/bin/bash
This test is the third test
this line is so stupid
This test is the fourth test

第 20 章 正则表达式

从大量数据中过滤出特定数据需要正则表达式,通过正则表达式来定义模式模板。
sed和gawk使用的正则表达式不同
sed只可使用基础正则表达式( basic regular expression, BRE);
gawk使用扩展正则表达式( extended regular expression, ERE)。

特殊字符

有些字符在正则表达式模式中有特殊意义,想将这些特殊字符当做文本字符来看就必须使用反斜线( \)转义。正则表达式识别的特殊字符包括:
{} [] () \ ^ + | * . $

转义之后就被当做文本字符
巧记
大中小括号 {} [] ()
拄棍戴着帽 \ ^
加一星点钱? + | * . $ ?

sed的特殊字符

^ 锁定在行首或者排除
$ 锁定行尾
. 必须有一个字符,且不能是换行符

  • 字符后面放置星号表明该字符必须在匹配模式的文本中出现0次或多次。

gawk的特殊字符

下面是gawk可识别而sed不可识别的
? 置于字符后,表示出现0次或1次

  • 至少出现1次
    { }
    | 是逻辑OR,定义多个匹配模式
    ( ) 表达式使用()进行分组

sed使用特殊字符例子

$ cat > test
^aaa$ooo^aaa$the cost is $4
\ is a special character
the book store
aaa
aaaa

^aaa$
^aaa$ooo^aaa$
cat
ccat
 at
hat
eat
fat
jat
*at
no
NO
No
188

$ sed -n '{/\$/p
> /\\/p        #将含有$的打印
> /^the/p        #将行首为the的打印
> /store$/wfoo.txt        #将行尾为store的存进文件foo.txt
>/^aaa$/p        #将只含有aaa的行打印
>/\^aaa\$/p        #将含有^aaa$的行打印
>/[^ch]at/p       #将不含cat和hat的行打印
>/^\^aaa\$$/p        #将只含有^aaa$的行打印
>/.at/p        #将at前至少有一个字符的行打印
>/[ch]at/p        #将含有cat或者hat的行打印
>/[Nn][Oo]/p        #将含有NO或者no或者No或者nO的行打印
>/^[0-9][0-9][0-9]$/p    #将只含有三位数的行打印
>/[c-eh-j]at/p    #将含cat或者dat或者eat或者hat或者iat或者jat的行打印
>/c*at/p       #表示字符c出现0次或多次
>/c.*at/p       # .表示此处必须有一个字符,*表示字符出现0次或多次,c at属于空格也算。
}' test

$ sed '{/^$/d}' test        #将空白行去掉 

BRE还包含了一些特殊的字符组,很好记,模式就是 [[:单词:]],所以只需要记几个单词就可以

[[:alpha:]] 匹配任意字母字符,不管是大写还是小写
[[:alnum:]] 匹配任意字母数字字符0~9、 AZ或az
[[:blank:]] 匹配空格或制表符
[[:digit:]] 匹配0~9之间的数字
[[:lower:]] 匹配小写字母字符a~z
[[:print:]] 匹配任意可打印字符
[[:punct:]] 匹配标点符号
[[:space:]] 匹配任意空白字符:空格、制表符、 NL、 FF、 VT和CR
[[:upper:]] 匹配任意大写字母字符A~Z
可以在正则表达式模式中将特殊字符组像普通字符组一样使用

$ cat test
adfdhgfdghf
SFGDFGDFG

1234
12SD
,,..:

$ sed -n '/[[:lower:]]/p' test   #输出小写字母,将lower换成其他单词就可换成其他的

gawk使用特殊字符例子

gawk可以识别而sed不能识别的ERE(扩展正则表达式)
? {} | ()

$ echo "bt"|sed -n '/be?t/p'    #没有结果,因为sed不能识别?
$ echo "bt"|gawk '/be?t/{print $0}'    #gawk可以识别?
$ echo "bet" | gawk --re-interval '/be{1,2}t/{print $0}'    #be{1,2}t表示e至少出现1次,最多出现2次
$ echo "He has a hat." | gawk '/[ch]at|dog/{print $0}'    #  | 是逻辑OR,定义多个匹配模式。意思就是有cat,hat或者dog的都算匹配上
$ echo "Sat" | gawk '/Sat(urday)?/{print $0}'    #urday这个字符串出现0次或者1次。使用()进行分组
$ echo "cat" | gawk '/(c|b)a(b|t)/{print $0}'   #cab,cat,bab,bat都算匹配上

你可能感兴趣的:(《Linux命令行與shell脚本編程大全》學習筆記)