shell脚本详细学习
shell
passwd 文件:多行多列的表格
列的分隔符是冒号
root:x:0:0:root:/root:/bin/bash
第一列是用户名:root
第二列是密码,但是通过加密放到了shadow中
第三列是用户id
第四列是组id
第五列是用户描述
第六列是home目录地址
第七列是用户登录之后执行的第一条命令
确定用户当前用什么shell
1.echo $SHELL
2.看 passwd 文件用户的启动命令
shell内建命令
比如 cd、alias、umask 在which xxx中找不到,就是内建命令,这不是一个进程只是调用了shell进程的函数
shell脚本
shell脚本相当于把一行行命令集中一次性处理,在执行命令的过程中会fork一个子进程并调用exec执行./test.sh这个程序,exec系统调用把子进程的代码段替换成./test.sh程序的代码段,并从他的_start开始执行 但是./test.sh只是个文本文件,根本没用代码段和——start函数,怎么解决呢?实际上exec还有另外一种机制,如果要执行的是一个文本文件,并且第一行用Shebang指定了解释器,则用解释器程序的代码段替换当前进程,并且从解释器的_start开始执行,而这个文本文件被当作命令行参数传给解释器。
eg:
#!bin/bash # 指定用的解释器
echo Helloworld
cd ..
ls
执行脚本的几种方式:
可以用 ./test.sh执行;
也可以用 source 脚本地址,**这种方式执行脚本不会产生一个子进程,而是将脚本的命令都加载进来执行**
还可以用 . 脚本地址,跟 source 一样
shell 基本语法
变量
变量的定义
VARBNAME=value
变量的使用,用 $ 符号跟上变量名表示对某个变量进行取值,变量名可以加上花括号来表示变量名的范围
echo $VARBNAME
echo ${VARBNAME}
VARBNAME=value
注意:等号两边都不能有空格!!
变量的分类
* shell 内变量 (这个变量只存于shell内)
> 全局变量 (shell中不使用任何修饰符修饰的变量都是全局变量)
> 局部变量 (用 local 来修饰)
shell内变量只能在当前shell进程中使用,跨进程不允许使用多个子进程
* 环境变量
操作系统自带,每个进程都会有
当启动一个子进程时,环境变量是从父进程拷贝到子进程
全局变量以及局部变量 eg:
#!/bin/bash
global="hello"
function test()
{
global2="world"
local LocalValue="cast"
echo $LocalValue
}
test
echo $global $global2
echo $LocalValue #这里local是局部变量在函数结束的时候就已经全部释放了,自然看不到输出结果
环境变量 eg:
export varname=value
删除变量
用unset命令可以删除已定义的环境变量或本地变量 不管是环境变量还是普通的shell变量,都可以使用unset删除
unset VARNAME
文件名代换
用于匹配的字符统称为通配符
* 匹配0个或多个任意字符
? 匹配一个任意字符
[若干字符] 匹配方括号中的任意一个字符的一次出现
代换文件的前提:文件必须存在,如果不存在是代换不了的
命令代换
shell先执行该命令,然后将输出结果立刻代换到当前命令行中
eg:
#!/bin/bash
DataTime=`date` # 由"`"这个符号也是一条命令,相当于先把这个命令保存再输出,与 $(date)的功能一样
echo "DateTime is " $DataTime
算数代换
使用$(()),用于算术计算,(())中的Shell变量取值将转换成整数,跟$[]等价
注意:$(())中只能用加减乘除和()运算,并且只能做整数运算
eg:
#!/bin/bash
VAR=45
echo $((VAR+3))
使用 $[base#n] 做进制计算,其中base表示进制,n按照base进制解释,后面再有运算数,按十进制解释
eg:
#!/bin/bash
echo $[8#10+11]
shell引号
Shell脚本中单引号和双引号都是字符串的界定符,而不是字符的界定符。单引号用于保持引号内所有字符的字面值,即使引号内的\和回车也不例外,但是字符串中不能出现单引号
单引号eg:
echo 'aaa
> bbbb'
aaa
bbbb
双引号eg:
被双引号括住的内容,将被视为单一字串,它防止通配符扩展,但允许变量扩展
eg:
#!/bin/bash
var="a b"
echo $var
输出的结果就是 a b 中间有空格,但是我想 var 是一个整体我并不想把他拆开变量,于是就可以用 echo "$var"
条件测试
条件测试是用来表示真假,直接使用某条命令的返回状态(main函数返回值)
若 main()函数返回0,即为真,若main函数返回非0,即为假
如何获取返回状态?
通过 $? 来获取上一条命令的返回状态
使用命令 test 或 [ 可以测试一个条件是否成立
! EXPRESSION(取反)
EXPRESSION is false
EXPRESSION1 -a EXPRESSION2 (与)
both EXPRESSION1 and EXPRESSION2 are true
EXPRESSION1 -o EXPRESSION2 (或)
either EXPRESSION1 or EXPRESSION2 is true
-n STRING (判断字符串不是空串)
the length of STRING is nonzero
STRING equivalent to -n STRING
-z STRING (判断字符串长度为0)
the length of STRING is zero
STRING1 = STRING2 (判断字符串相等)
the strings are equal
STRING1 != STRING2(判断字符串不相等)
the strings are not equal
INTEGER1 -eq INTEGER2 (判断整数相等)
INTEGER1 is equal to INTEGER2
分支结构
if结构
结构:if 命令|条件测试
then xxxxx
elif 命令2|条件测试
else
xxxx
fi
如果then跟if写在同一行,加分号,else后不需要跟then
eg:
#!/bin/bash
if [ -f /bin/bash ]
then echo "Bash is installed."
else
echo "Bash is not installed."
fi
case结构
case命令可类比C语言的 switch/case语句,esac表示case语句块的结束,Shell的 case 可以匹配字符串和 Wildcard,每个匹配分支可以有若干条命令,末尾必须以;;结束
结构:
case 表达式 in
val1|pattern1)
xxxxx
;;
val2|pattern2)
;;
esac
eg:
#!/bin/bash
echo "Is it morning or evening? (morning/evening)"
read time_of_day
case "$time_of_day" in
morning)
echo "Good morning!"
;;
evening)
echo "Good evening!"
;;
*)
echo "Invalid input. Please enter 'morning' or 'evening'."
;;
esac
循环
for循环
结构:
for varname in 列表
do
done
for varname in 列表; do
done
eg:
#!/bin/bash
for F in Banana apple pear;do
echo "I like $F"
done
#!/bin/bash
sum=0
for F in {1..100}; do
sum=$((sum + F))
done
echo "The sum is: $sum"
while 循环
结构:
与C类似
while 命令|条件测试
do
xxxx
done
eg:
#!/bin/bash
sum=0
cnt=1
while [ $cnt -le 100 ] ; do
sum=$((sum + cnt))
cnt=$((cnt + 1))
done
echo "The sum of numbers from 1 to 100 is: $sum"
#!/bin/bash
echo "Enter Password:"
read password
while [ "$password" != "manager" ] ; do
echo "Please try again"
read password
done
echo "Access granted"
位置参数和特殊变量
$0 相当于C语言 main 函数的 argv[0]
$1、$2... 这些相当于位置参数,类似C语言中的 argv[1]、argv[2]...
$@ 表示参数列表"$1""$2"...可以用在for循环中in后面
$? 上一条命令的返回状态
$$ 当前进程号
eg:
#!/bin/bash
sum=0
for i in $@;do
sum=$[$sum+$i]
done
echo "The sum is $sum"
注意: 位置参数默认就支持10个,$@还是支持很多个
管道 (命令与命令的交互)
可以通过 | 把一个命令的输出传递给另一个命令做输入,实际上是将前面进程的标准输出重定向到后边进程的标准输入,标准错误输出是不会重定向的
eg:
#include <stdio.h>
#include <ctype.h>
int main()
{
int ch = getchar();
while(ch != EOF)
{
putchar(toupper(ch));
ch = getchar();
}
return 0;
}
echo abc | ./main
衍生:
* more命令 ,将标准输入的内容(超级多)进行缓慢向下查看,只支持向下走,不支持往回走
* less 命令,比 more 更完善,支持往回滚,支持类似 vim 的操作
* tee 命令 ,把结果输出到标准输出,另一个副本输出到相应文件
重定向 (文件与命令的交互)
* cmd > file 把标准输出重定向到新文件中
* cmd >> file 追加
* cmd > file 2>&1 标准出错也要重定向到1所指向的file里
* cmd >> file 2>&1 文件描述符2重定向到文件描述符1中
* cmd < file1 > file2 输入输出分别定向到两边文件中
* cmd < &fd 把文件描述符fd作为标准输入
* cmd > $fd 把文件描述符fd作为标准输出
* cmd < &- 关闭标准输入
函数
在Shell中函数定义中没有返回值也没有参数列表
结构:
func 函数名()
{
return 1
}
注意:func可以省,小括号也可以省,但是这两个必须保留一个 小括号里面也不需要填列表参数,return 只能返回参数,不能返回其他类型
eg:
#!/bin/bash
foo()
{
echo "This is a lion."
return 1
}
foo
echo $?
testfunc()
{
echo "aaaaaaaa"
return 0
}
var=`testfunc` # 将反引号内的命令执行,并用执行结果替换掉整个反引号部分
#var=$(testfunc)
echo ${var}
shell调试方法
* -n 读一遍脚本中的命令但不执行,用于检查脚本中的语法错位
* -v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
* -x 提供跟踪执行信息,将执行每一条命令和结果依次打印出来
可以跟在 命令行后 bash -x xx.sh,也可以在代码前显示 #!/bin/bash -x
正则表达式
结构:
1.以S开头的字符串 ^S匹配
2.以数字结尾的字符串
[0123456789] [0-9] \d [0-9]$
3.匹配空字符串(没有任何字符)
^$匹配
4.字符串只包含三个数字
^\d\d\d$ 控制次数 我们有更好的办法写 ^\d{n}$ 表示前面的单元重复n次
5.字符串只有 3-5 个
{m,n} m表示前面单元最小重复次数,n表示最大重复 次数
字母表示法:[a-zA-Z] PS:**如果中括号中有多个区间,区间之间不要留空格或其他分隔符**
所以 ^[a-zA-Z]{3,5}$
6.匹配不是a-z的任意字符
[^a-z] 若 ```^```这个符号出现在中括号内第一个表示**区间取反**
7.字符串有0到1个数字或者字母或者下划线
{0,1} 表示重复0-1次, ? 也可以表示重复0-1次
^[0-9A-Z_]?$
8.字符串有1个或多个空白符号
\s 表示有空格出来的字符 包括 \t\n\r...
{1,n}表示重复 1-n次,与 ```+```这个符号一样
^\s+$
9.字符串有0个或若干个任意字符
. 代表任意字符,除 \n
{} 花括号中两个参数置空表示重复次数任意 0-n
* 表示前面的单元重复 n 次 (0-n)
^.*$
总结:
? 0-1
+ 1-n
* 0-n
10.匹配0或任意多组ABC,比如ABC,ABCABCABC
在描述多组ABC时,可以用 ()来表示多个单元重新组成一个单元
^(ABC)*$
11.字符串内要么是ABC,要么是123
需要做选择的时候,需要写 ```|``` 选择两边的一个
^(ABC)$|^(123)$
12.字符串只有一个点号
因为 ```.```具有特殊含义,表示任意字符,所以我们需要对其进行转义
^\.$
13.匹配十进制3位整数
十进制三位数有:100-999
^[1-9][0-9]{2}$
14.匹配十进制 0-999 数字
这种情况需要 一位数、二位数、三位数分类
一位数:^[1-9][0-9]$
二位数:^[1-9][0-9]$
三位数:^[0-9][0-9]{2}$
^[0-9]|[1-9][0-9]{1,2}$
shell中的其他命令
sort 命令
命令从标准输入中读取数据然后按照字符串内容进行排序
-f 忽略字符大小写
-n 比较数值大小
-t 指定分隔符,默认是空格或者tab
-k 指定分割后进行比较字段
-u 重复行只显示一次
-r 反向排序
-R 打乱顺序
cat test.txt | sort 与 sort < test.txt一样
uniq
去除重复的行,前提是重复的行连续
-c 显示每行重复的次数
-d 仅显示重复的行
-u 仅显示不曾重复的行
eg:
sort < test.txt | uniq -c
1 xiao 89 man 16
1 xiaow 10 man 11
1 xiaowen 78 man 90
2 xio 30 man 50
sort < test.txt | uniq -d
xio 30 man 50
sort < test.txt | uniq -u
xiao 89 man 16
xiaow 10 man 11
xiaowen 78 man 90
wc命令 (word count)
* -l 统计行数
* -c 统计字节数
* -w 统计单词数
wc -l < test.txt
5
cat test.txt
xiaowen 78 man 90
xiao 89 man 16
xiaow 10 man 11
xio 30 man 50
xio 30 man 50
wc -c < test.txt
77
wc -w < test.txt
20
grep (Global Regular Expression Print)
grep 命令是一种强大的文本搜索工具,能使用正则表达式搜索文本,并把匹配的行打印出来,使用权限是所有用户
格式以及主要参数
Usage: grep [OPTION]... PATTERNS [FILE]...
Search for PATTERNS in each FILE.
Example: grep -i 'hello world' menu.h main.c
PATTERNS can contain multiple patterns separated by newlines.
Pattern selection and interpretation:
-E, --extended-regexp PATTERNS are extended regular expressions
-F, --fixed-strings PATTERNS are strings
-G, --basic-regexp PATTERNS are basic regular expressions
-P, --perl-regexp PATTERNS are Perl regular expressions
-e, --regexp=PATTERNS use PATTERNS for matching
-f, --file=FILE take PATTERNS from FILE
-i, --ignore-case ignore case distinctions in patterns and data
--no-ignore-case do not ignore case distinctions (default)
-w, --word-regexp match only whole words
-x, --line-regexp match only whole lines
-z, --null-data a data line ends in 0 byte, not newline
Miscellaneous:
-s, --no-messages suppress error messages
-v, --invert-match select non-matching lines
-V, --version display version information and exit
--help display this help text and exit
Output control:
-m, --max-count=NUM stop after NUM selected lines
-b, --byte-offset print the byte offset with output lines
-n, --line-number print line number with output lines
--line-buffered flush output on every line
-H, --with-filename print file name with output lines
-h, --no-filename suppress the file name prefix on output
--label=LABEL use LABEL as the standard input file name prefix
-o, --only-matching show only nonempty parts of lines that match
-q, --quiet, --silent suppress all normal output
--binary-files=TYPE assume that binary files are TYPE;
TYPE is 'binary', 'text', or 'without-match'
-a, --text equivalent to --binary-files=text
-I equivalent to --binary-files=without-match
-d, --directories=ACTION how to handle directories;
ACTION is 'read', 'recurse', or 'skip'
-D, --devices=ACTION how to handle devices, FIFOs and sockets;
ACTION is 'read' or 'skip'
-r, --recursive like --directories=recurse
-R, --dereference-recursive likewise, but follow all symlinks
--include=GLOB search only files that match GLOB (a file pattern)
--exclude=GLOB skip files that match GLOB
--exclude-from=FILE skip files that match any file pattern from FILE
--exclude-dir=GLOB skip directories that match GLOB
-L, --files-without-match print only names of FILEs with no selected lines
-l, --files-with-matches print only names of FILEs with selected lines
-c, --count print only a count of selected lines per FILE
-T, --initial-tab make tabs line up (if needed)
-Z, --null print 0 byte after FILE name
Context control:
-B, --before-context=NUM print NUM lines of leading context
-A, --after-context=NUM print NUM lines of trailing context
-C, --context=NUM print NUM lines of output context
-NUM same as --context=NUM
--group-separator=SEP print SEP on line between matches with context
--no-group-separator do not print separator for matches with context
--color[=WHEN],
--colour[=WHEN] use markers to highlight the matching strings;
WHEN is 'always', 'never', or 'auto'
-U, --binary do not strip CR characters at EOL (MSDOS/Windows)
When FILE is '-', read standard input. With no FILE, read '.' if
recursive, '-' otherwise. With fewer than two FILEs, assume -h.
Exit status is 0 if any line is selected, 1 otherwise;
if any error occurs and -q is not given, the exit status is 2.
pattern 正则表达式主要参数:(pattern - 表示要查找的字符串或正则表达式。)
* \ 忽略正则表达式中特殊字符的原有含义
* ^ 匹配正则表达式的开始行
* $ 匹配正则表达式的结束行
* \< 从匹配正则表达式的行开始
* \> 到匹配正则表达式的行结束
* [ ] 单个字符,eg:[A] 即A符合要求
* [ - ] 表示字符范围,eg:[A-Z],即A~Z都符合要求
eg:
在文件 file.txt 中查找字符串 "hello",并打印匹配的行:
grep hello file.txt
在文件夹 dir 中递归查找所有文件中匹配正则表达式 "pattern" 的行,并打印匹配行所在的文件名和行号:
grep -r pattern ~/shell/create
find 命令
find 用于在指定目录下查找文件和目录,可以使用不同的选项来过滤和限制查找的结果
结构以及常见的匹配条件:
find [路径] [匹配条件] [动作]
* -name 按照文件名查找文件
* -perm 按照文件权限来查找文件
* -prune 使用这一选项可以使 find 命令不在当前指定的目录中查找,如果同时使用 -depth选项,那么-prune 将被 find 命令忽略
* -user 按照文件属主来找文件
* -group 按照文件所属的组来找文件
* -mtime days:按修改时间查找,支持使用 + 或 - 表示在指定天数前或后,days 是一个整数表示天数。
* -type 查找某一类型的文件 eg:b(块设备文件)、d(目录)、c(字符设备文件)、p(管道文件)、l(符号链接文件)、f(普通文件)、s(socket文件)
* -exec 可以作为一个指令执行
eg:
查找当前目录下名为 file.txt 的文 件:
find . -name file.txt
查找当前目录下的txt文件并且打包成为 gzip
find . -name "*.txt" -exec gzip {} \;
xargs
将标准输入的参数整齐拼凑在一行里面 一般是和管道一起使用
sed(Stream Editor)
sed 在 shell 脚本 和 Makefile 中作为过滤器使用非常普遍,也就是把前一个程序的输出引入 sed 的输出,经过一系列编辑命令转换位另一种格式输出 还有一种说法就是它利用脚本来处理文本文件
结构:
sed option '/pattern/action' file1 file2 ...
解释: sed 选项参数 '/pattern/action'(其中pattern是正则表达式,action是编辑操作) 待处理文件
sed option -f scriptfile file1 file2
解释: sed 选项参数 -f '脚本文件' 待处理文件
* -e 用来指定一段脚本(编辑命令)
* a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
* c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
* d :删除,因为是删除啊,所以 d 后面通常不接任何东东;
* i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
* p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
eg:
sed '' test10.txt
ashfjklsadfsndhlkj
ashfjiksdahfkl
asdjfio
lllll999999999
oiopi90
123
-------------------------------
sed -e '4a helloworld' test10.txt
# sed -e '4a\helloworld' test10.txt
ashfjklsadfsndhlkj
ashfjiksdahfkl
asdjfio
lllll999999999
helloworld
oiopi90
123
awk
sed 以行单位处理文件,awk 比 sed 强的地方在于不仅能以行为单位还能以列为单位处理文件。**awk缺省的行分隔符是换行,缺省的列分隔符是 连续的空格 和 Tab**,当然,行分隔符和列分隔符都可以自定义
结构:
awk option 'script' file1 file2
awk option -f scriptfile file1 file2
**和 sed 类似,pattern 是正则表达式,actions是匹配成功后一系列操作**
命令行参数传递方式:
* /pattern/{actions}
* condition/{actions}
关于 awk 脚本,我们需要注意两个关键词 BEGIN 和 END。
**BEGIN{ 这里面放的是执行前的语句 }**
** END {这里面放的是处理完所有的行后要执行的语句 }**
eg:
awk '{print $1}' test10.txt
ProduceA
ProduceB
ProduceC
以C语言的方式理解 awk 命令
$2 < 75
{
printf("%s\n%s\t","reorder");
}
$2 > 75
{
printf("%s\n",$0);
}
本文来自博客园,作者:Alaso_shuang,转载请注明原文链接:https://www.cnblogs.com/Alaso687/p/22871596

浙公网安备 33010602011771号