Shell脚本学习
杂项小知识:
- 命令ls之类的其实就是可执行程序,shell搜索这些程序的目录在$PATH
- $0输出程序名,可以用来知道当前是什么shell
- 环境变量UID保持用户的UID,如果为超级用户则UID=0
- 在命令结束后立刻运行$?可以打印命令的退出码;为0表示正常退出;
- 默认情况下stdout和stderr都会输出到屏幕;
- unix并不是通过扩展名(.txt)来区分文件类型的,而是通过检查文件内容;(file命令可以检查文件类型)
脚本(xxx.sh)执行的两种方式:
- bash xxx.sh
- 将脚本作为bash解释器程序的命令行参数运行;这样在xxx.sh的第一行就不需要再指定:#!/bin/bash
- ./xxx.sh
- 内核会读取脚本的第一行#!/bin/bash并最终调用/bin/bash xxx.sh来执行脚本
单引号和双引号的区别:
双引号:可以对双引号内的特殊字符进行解释;
单引号:单纯输出,shell不对脚本内的任何符号进行解释;
Shell的数学运算:
shell通过let、(())、[ ]来执行算术操作:
- let result=a+b let a++ let a+=2
- result=$[ $a+$b ]
- result=$(($a + $b))
文件描述符和重定向:
将标准输出stdout和错误输出stderr都重定向到文件中:
- cmd &> AllOutput.txt
Shell普通数组和关联数组:
普通数组:
- 定义普通数组和添加元素:
- arr=(str1 str2 str3 str4)
- arr[4]="str6"
- arr[${#arr[*]}]="str7"
打印数组的长度:echo ${#arr[*]}
关联数组:
定义关联数组:declare -A arr
关联数组添加元素:
- arr=([索引1]=值1 [索引2]=值2 [索引3]=值3)
- arr[索引4]=值4
打印数组的索引列表:echo ${!arr[*]}
时间命令:
date命令:在参数最前面带(一个)+指定输出格式;
date +%s:打印时间戳;
date +%Y:答应2019或者+%y答应19
date "+%Y%d%d":输出20191119
Shell函数:
在Shell函数内:
- $0:代表脚本文件名
- $1:第一个参数
- $2:第二个参数
- $@:在利用for循环并且带双引号("")时$@会把里面的参数当作当个单词遍历:for chr in "$@"
- $*:而 for chr in "$*"会将参数当成一个整体输出一次;基本不用$*!!!
子Shell进程:
利用()来启动一个shell子进程;
取得子shell进程的返回值:result=$(cat txt)
Shell循环的几种方式:
#第一种 do var in list do command; done #第二种:(())里面代表进行算术运算 for ((i=0; i < 10; i++)) { command; } #第三种 while condition do commond; done #第四种:直达到达条件才停止 x=0 until [ $x -eq 10 ] do commands; done
Shell比较判断
if、if else
#if if [ condition ] then commands; fi #if else if [ condition ] then commands; else if [ condition ] then commands; fi fi #if可以用逻辑与和逻辑或代替 [ condition ] && commands; //如果condition为true则执行commands [ condition ] || commands; //如果condition为false则执行commands
算术比较
- [ $i -eq 0 ]:等于
- [ $i -ne 0 ]:不等于
- [ $i -gt 0 ]:大于
- [ $i -lt 0 ]:小于
- [ $i -ge 0 ]:大于等于
- [ $i -le 0 ]:小于等于
- [ $i -ge 0 -a $i -le 10 ]:大于等于0并且小于等于10
- [ $i -ge 0 -o $i ge 10 ]:大于等于0或者大于等于10
文件相关比较:
- [ -e $path ]:包含的文件/目录是否存在,对于软链接,会判断软链接所指的文件是否存在。
- [ ! -e $path ]
- [ -L $paht ]:判断软链接是否存在
- [ -w $path ]:路径文件是否可写
- [ -r $path ]:路径文件是否可读
字符串比较:最好用两个[[]],如果用一个有可能出错
- [[ $a == $b ]]
- [[ -z $a ]]:如果$a为空则为真
- [[ -n $a ]]:如果$a不为空则为真
单方括号[]和双方括号[[]]的区别:
1.单方括号[ ]如果用于字符串比较,字符串比较只能用[ $a != $b ]或者[ $a == $b ]
2.单方括号[ ]如果用于整数比较,整数比较只能使用-ne -eq的形式
3.不论是[ ]还是[[ ]]都不支持:>= 和 <= ,这两个只存在于(())
4.[[ ]]支持&&、||、>、<等操作符。对于整数比较如果不想用-gt -lt这种形式,可以使用[[ $a > $b ]]这种形式。
Find命令:
小知识:
find命令或者不进入符号链接(ln -s的那种)寻找,有可能会陷入循环
选项:
- -iname:忽略大小写
- -o:或者(find . -name '*.txt' -o -name '*.sh')
- -a:并且
- !:排除(find . ! -name '*.txt')找到所有不是.txt的文件
- -maxdepth:最好在所有参数最前面
- -mindepth:最好在所有参数最前面
- -L:允许进入符号链接种
- -atime:默认单位时天;
- -mtime:find . -name "xx" -mtime -7:7天内修改的:+7大于7天的
- -ctime:单位是分钟;
- -amin:
- -mmin:
- -cmin:
- -size: 按文件大小:+2K、-2K、2K(大于、小于、等于)
- -perm:按权限查找:-perm 777
- -user:按用户
- -----找到文件以后的操作-----
- -delete:删除找到的文件
- -exec:做一些操作:
find . -name '*.txt' 和 find . -name "*.txt"的区别(单引号和双引号):双引号会对内容进行解释而单引号则原封不动将字符串当作参数传给find命令;
find . -name "xxx" -exec touch {} --------------对于每一个find找到的文件都会用{}替换;故假如find找到了3个文件,则会执行三次touch
如果-exec后面的命令可以接受多个参数;则就不需要对每个文件都执行造成多的花销:
find . -name "xxx" -exec touch {} + --------------touch只会执行一次:touch xx1 xx2 xx3
xargs和find命令:
xargs命令就是将标准输入的参数多行变单行并进行分割(默认是按空格分割)再将分割后的参数提供给指定的命令;默认是提供给echo输出;
选项:
- -d:为xargs指定分隔符
- -n:指定多少个为一组作为整体传给指定命令
- -0:以NULL作为分割符,搭配find的-print0作为找到不同文件的分割符;
- -I {}:cat args.txt | xargs -I {} ./echo.sh -p {} Op xargs分割的每个参数都会放到{}里面并替换在下一个{}内;
以下命令很危险:
find . -type f "*.txt" | xargs rm -f
xargs将多行转换为多行以后默认以空格分割参数传递给rm;
假如文件名叫Import file.txt那么rm 会删除Import 和file.txt文件;
文件行排序sort和uniq:
sort选项:
- -n:以数字顺序排序;
- -k:指定第几列为依据进行排序;列和列之间以空格分割开;
- -r:以逆序的方式排序;
uniq命令:只能用在以排序过的文件找出唯一的行;
选项:
- -c:统计每行重复的次数,重复的只显示一次;
- -d:打印出文件中重复的行;
字符串截取:
%和#将匹配到字符的删除;
%:从右向左;
#:从左向右;
var="hello.world.txt"
- {var%.*}:只匹配第一个
- {var%%.*}:贪婪模式,匹配所有的;
- {var#*.}
- {var##*.}
文件特殊权限:
- chmod +s:只能用于二进制程序,当运行这个程序的时候是以root的身份运行;
- chmod +t:设置目录的粘滞位,用户在这个目录下只能创建和修改属于自己的文件;
设置文件不可修改状态:
路径栈:
当要在多个目录之间切换的时候,可以将路径压入栈中;
- pushd
- popd
- pushd +3:进入到指定路径,且会将该路径放到栈顶;
- popd +3:将指定路径出栈;
正则表达式:
匹配规则:(默认会选择最长匹配)
- ^abc:开头是abc的都匹配成功
- dec&:结尾是dec的都匹配成功
- .:任意一个字符都匹配
- [ ]:匹配括号内任意一个字符或一组字符范围;[a-z]
- [^]:匹配不在括号内的字符
- ?:?符号前面那个字符出现1次或者0次的都匹配
- +:+符号前面的那个字符出现1次或者多次都匹配
- *:*符号前面的那个字符出现0次或者多次都匹配
- {n}:{n}符号前面的那个字符出现n次才匹配
- {n,}:{n,}符号前面那个字符至少出现n次才匹配
- {n,m}:{n,m}符号前面那个字符至少出现n次至多出现m次
- ^$:空行
grep\egrep命令:
- grep word:从标准输入拿到输入;
- grep word fileName1 fileName2 fileName3:从文件拿到输入;
- 使用正则表达式的grep:egrep命令
- -v:过滤匹配到的;
- -l:打印匹配到的所属的文件;
- -L:打印没匹配到的所属的文件;
- -o:只打印匹配到的字符串;
- -n:打印匹配字符串所在的行数;
- -r:会进入目录
- -i:忽略大小写
- -e:grep -e "xx" -e "aa":符合其中一个的都会找到
- --include:grep "xxx" . -r --include *.{c,cpp}:递归目录下的.c和.cpp文件搜索"xxx"
- --exclude:
- -exclude-dir:排除目录
sed编辑器(stream editor):
格式:
sed 's/模式/替换字符/' fileName:默认只替换首次匹配到的内容
sed 's/模式/替换字符/g' fileName:全局替换
也可以从标准输入拿到参数;
选项:
- -i:会修改源文件;
- 移除空行: sed /^$/d fileName
- -i.backup:会将源文件备份到xx.backup
- &:已匹配到的字符串可以用&来代表:
- \w:匹配一个单词. sed s/\w\+/[&]/g:将匹配到的单词加一个[]号
- \1:用来匹配到字符的第一个括号内的字符;
awk脚本:
awk其实相当于一个脚本了,awk按行处理;也可从标准输入读入参数
格式: awk 'BEGIN { } 匹配规则{ } END { }' FileName
一开始会执行BEGIN括号内的语句;
然后读取每一行如果匹配规则则执行 {}
当处理完所有行的时候执行END括号内的语句;
awk脚本内的特殊变量:
- NR:代表当前行号
- NF:代表当前行的字段数量,默认以空格分割
- $0:代表整行
- $1:代表第一个字段
- $2:代表第二个字段
归档和压缩:
归档是将所有文件打包成一个,压缩是压缩;两个不是一个东西;
tar:
tar可以将归档和压缩一起进行;
tar选项:
- -f:归档后的文件名:一般这个选项不可缺少且需要在最后,后面跟着文件名;
- -c:表示创建新的归档文件
- -t:显示归档文件内的文件
- -v或者-vv:输出细节信息
- -r:往归档文件中追加文件,如果归档内已有同文件名的文件则会有两份;
- -u:往归档文件中追加文件,如果归档内已有同文件名则覆盖时间戳最新的那个;
- -x:从归档文件中提取文件,-C指定提取到哪个文件夹中:tar -xf xx.tar -C Path
- -A:合并两个归档文件;
- --delete:从归档文件中删除文件
- -a:自动选择压缩算法,-z:使用gzip格式压缩
- --exclude:归档的时候排除某些文件:--exclude *.txt
- --exclude-svn:可以排除版本控制文件
rsync:
rsync可以同步不同位置(机器)上的文件,并且支持压缩传输和只更新最新的文件;
格式:rsync -av 源路径 目的路径
默认递归进入目录进行复制文件;
-a:表示归档传输?
-v:表示显示详细消息
-z:传输时压缩数据;
--delete:删除目的路径上在源路径下没有的文件;
远程执行命令:
ssh -p 23 user@host "commands1; commands2; commands3"
在远程执行命令,在本地显示输出;
本地输出作为远程命令的输入:echo "text" | ssh user@host "echo "
ps命令:
选项:
- -e、-A:ps显示所有进程的选项只有这两个。-e比较简单好记;
- -l:显示比较多列的信息;
- -f:显示格式更全了;