Appearance
前言
开发中总是遇到各种脚本,目前只能对普调的脚本进行阅读和编写,系统性的记录一篇开发中所需要的脚本技能,同时将平常遇到的脚本难点作为讲解。脚本分为shell脚本和bat脚本
shell
脚本处理程序
如果对脚本程序需要直接执行,在脚本前面添加#!/usr/bin/env sh使用默认执行程序,/usr/bin/env 会自动查找环境的sh的程序来执行
对于需要全局使用的脚本,有两种方式,第一个是软连接或者放到/usr/local/bin/ 中,第二个是使用别名 alias some="/path/xxx"
输入输出
read 能从当前stdin获取输入并存储到某个变量中 read -p "请输入值" -n 10 -s -t 20 someValue # -n 限制文本长度 -s 密码输入,不会显示, -e 启用命令补全 -t 输入时长 echo "$someValue"
echo ""转义输出echo ''不转义输出 echo ``执行转义后的结果并输出
printf 是高级的输出工具,具有序列化的功能。printf "string" ...args printf "%s-5 %c $-4.2f" 郭靖 男 66.1234
shell执行时总是默认开启了三个文件描述符为0 1 2 用于输入,输出和stderr。在shell中执行的命令默认也是使用这三个文件。通过 command > file; command >> file 将 stdout指向另一个文件。 使用 2 &> 1(&> file) 将stderr 指向 stdout ,使用 command < file 将stdin 指向file
命令执行
command1;command2; 命令都是按照;结尾(换行符前面的可以省),命令都是从前往后执行,即使前面的命令错误 command1 && command2 || command3 如果命令command1 成功则执行command2,失败则执行command3。成功错误失败根据进程返回0 1 2来判断
变量
只有字符串,没有数字布尔等类型,变量默认只在当前shell或者脚本(新开了一个子shell来执行脚本)中有效。变量通过$name 或者 ${name}来获取
截取字符串
${day_id:0:7} 0 起始位置 7 截取多少个
sh
text="wo shi yi da shuai bi"
# 截取
echo "who are you: ${text:0:2}" # wo
# 替换
echo "who are you: ${text/shuai bi/sha bi}" # wo shi yi ge da sha bi
# 删除留剩下的
echo "who are you": ${text%%shuai*}if控制
sh
if command; then
command;
elif command; then
command
else
command
fi控制语句如上,command表示退出码,除了0是成功其余都是失败,如果要测试表达式的退出码,使用[expr] 来判断
case控制
sh
case word in
glob)
command;; break,不在执行后面的语句
command;& 执行当前语句并在接着进入下一个语句(不匹配直接执行)
command;;& 继续下一个语句的匹配sh
cat << TIP
你最喜欢哪个国家
1. 中国
2. 日本
3. 美国
TIP
read choose
case $choose in
1)
echo "选择了中国,真棒";
lang=ch
2)
echo "选择了日本,给你一棒子";
lang=jap;;
3)
echo "选择了美国,小垃圾";
lang=a
esac
printf "%s %s" choose langwhile 循环
sh
while commmand; do
command;
done;for 循环
sh
for i in "1 2 3 4 5 6" do # 循环带空格的字符串,根据IFS变量定义的内容来分割
done
for file in ./* # 遍历文件名
done
for item in (1 2 3 4 5); do # 遍历数组
echo $item;
done
for i in {1..10} do # 遍历1到10
done
for i in seq 1 10 do # 循环 10次
done
name="123
123
123"数组
some=(v1, v2, v3)
算数表达式
shell没有内置表达式,只有单纯的变量声明,一切基于指令,部分指令提供了简写的形式。表示式都是被指令支持的。不能直接sum=1+1 比如 let sum=1+1, 理解成let指令,传入了一个sum=1+1 参数。let指令可以简写成((sum=1+1))
函数
sh
add() {
let sum=1+1
echo $sum # 将sum的值作为函数返回值
return
}shell的函数没有返回值,只是单纯的调用。函数内部有一些特殊的变量$0(脚本名),$N(参数1~N),$#(参数个数), $@ 全部参数
sh
add() {
echo "当前参数个数: $#"
echo "当前函数名称: $FUNCNAME"
echo "分别是\n"
local i=1
for ((i=;i <= $#;i++))
do
echo "${i}"
done
}
add 1 3 2特殊符号
- $取变量值
- ""转义字符串
- ''不转义
- ()在子shell中执行()中的代码
- $() 将()中结果stdout,和反引号包裹相同,如果作为命令执行,是将结果执行,如果作为赋值,将结果赋值
- {} 在当前shell自行命令
- [] 变量的测试,或者用于计算表单
- (())计算算数表达式, 或者在for中((i=0;i<3;i+++))
关于()和{}区别
sh
name=test
(name=test1)
echo $name # test
{name=test1} #
echo $name # test1算数运算
expr 指令用于算数的计算,计算符号之前需要有空格,大致逻辑是expr 通过空格来区分不同的参数。['expr', $a, "+", $b]这种逻辑,expr运算后会返回结果
((expr 1 + 1)) (expr 1 + 1)
条件表达式需要放在[]中, [] 也被视为一个命令 [ ! $? -eq 0 ] && xxxx 表示上一个命令的成功执行后续的,为了在兼容性推荐使用[[ ]] 语法,可以实现 [[ command1 && command2 ]] 操作
还有关系运算符 ! -eq -ne -gt -lt -ge -le 都只能在[],注意中括号内两边都有空格
提供了文件系统检查功能 [ -e file ] 文件是否存在 [ -d file ] 是否是目录
从上面可以看出[ ] 也是一个命令,返回 0 1
正则表达式
shell中部分命令支持正则表达式,主要是grep,awk, sed和cut。shell中正则表达式分为基本正则和扩展正则,不同的命令实现不一致。
基本正则表达式
. * $ \ ^ \{n\} 还有单词编辑等常用于grep命令中 echo "1 2 33 4 aaa" | grep -n ".{1}3"
扩展正则表达式
不需要讲{}转义括起来,支持+?等量词,同时支持分组和逻辑or,用于sed -r grep -E awk中
IFS
常见命名都通过该项进行分割,比如for in, cut, awk等命令
shell三剑客之grep
命令格式如下 grep [option] pattern [...files] 选项有如下
- -i 忽略大小写
- -v 反向匹配
- -o 只显示匹配命中的字符串(默认是显示整行,高亮命中的字符串)
- -c 统计命中的行数,常用于判断是否匹配中。
- -E 使用扩展正则表达式
- -R 递归的匹配目录下的文件
- -n 显示行号
shell三剑客之sed
sed是一个流式的替换查找的命令,使用基本正则表达式格式如下 sed [options] 'start,end /mode/pattern/mode-args/' [...file]
替换文件中的unix文本为linux。 echo "this is a unix operator" | sed 's/unix/linux/'
选项中有-i 直接在源文件上进行编辑,-n 仅显示处理后的结果(否则会展示整个文件内容) -e 设置多个处理命令
行定位
- 1,4 第一行到第四行
- 1,+3 第一行到+3行
- 4,$ 4行到尾行
- /test/,/linux/,出现第一次test的行到出现/linux/的行
- 3,5!除了3~5行的意外匹配
sed查找内容 p、=和n
n表示跳过当前这行, ;是执行多个命令,p;n表示读入当前这行,然后立刻跳过下一行,所以是技术行执行 p表示打印输出 p会输出整行,如果只需要显示匹配的行号使用 =
sed查找并替换c 和 s
s 替换字符串,c替换整行 sed 's/linux/unix/g' // 多次替换 sed 's/linux/unix/' // 一行只替换一次 sed 's/linux/unix/2' // 替换第二个匹配的的 sed 's/^/#/' // 行首添加# sed 's/[0-9]*/1&' // 所有数字前面添加1
sed 删除某些行
sed '3d' 删除第三行 sed '2,+1d' 删除2+1行 sed '$ d' 删除末尾那行
sed '/^$/ d' 删除所有空行
H 和 a
H复制部分到剪贴板,a插入到某行后面 sed -i "1,3H;9a/adfs/" 复制1,3行到剪贴板并且在9行后面插入adfs
shell三剑客之awk
awk使用扩展正则表达式,先按照行处理,再将行进行分割。命令格式如下,务必记住是单引号
awk [option] 'mode {action}' ...files
- awk -F ":" '/^root/' < /etc/passwd 查找root开头的行
- awk -F ":" 'NF==2{print NF,$0}' 打印第一行,并添加行号
- awk -F ""
bat脚本
bat脚本是运行在dos上面的脚本, 大致也是通过一条一条的指令来实现
变量
通过set指令 set a=123 获取变量通过%a%,通过 set /p 获取用户输入, 通过 set /a 设置一个数字
bat
@REM
set a=123
echo var is %a%控制语句
if [not] %a%==1 (command) else command
括号中的语句可以多行执行
bat
@echo off
echo 请输入数字,如果文字大于10,显示true,反之显示false
echo.
set /p input=
if not %input% GTR 10 (
echo 小于等10
) else (
echo 大于10
)运算符
算术运算 + - * / %%(取余),在表达式中会开启运算(set /a,if) 逻辑运算 equ neq lss leq gtr geq 赋值运算 += -= *= /= %=
函数
通过call :functionName调用,函数末尾通过got:eof标记结束。 对比一下shell和bat脚本的函数
sh
sum() {
sum_a=$1
sum_b=$2
echo $(($1+$2))
}
result=$(sum 3 5)
echo $result # echo 8bat
@echo off
:: 不需要添加%result%。自动处理
call:sum 4 5 result
echo %result%
:: 函数需要放在最后或者单独定义一个函数区来跳过
goto func_end
:func_start
:sum
echo %1
echo %2
:: 通过修改传入的参数来返回函数值
set /a %~3=%1+%2
echo %~3
goto:eof
:func_endbat的函数功能严格意义上应该是call的调用另一个bat脚本,在当前cmd中执行, 而且还会等到call的脚本执行完毕才会回到主进程 除了call, 还有start脚本,是另外开一个窗口执行bat脚本。
输入输出
输入输出和shell上一致,都有重定向输出>,追加>>和错误输出2>err.txt,同样也是 dir>nul输出到垃圾箱。 2 &>1 2和1相同输出。
多命令执行
bat通过一行一个指令的执行,&&、||相同 ,还有一个 & 和 shell的;相同,同一行执行多条命令。除此之外还有()可以扩展成多行
bat 技巧
扩展参数
将参数扩展为完整的路径,相对路径补全成绝对路径
bat
@echo off
:: 当前脚本第一个参数
echo %~1
:: 当前脚本第一个参数,并且补全到不带驱动器的圈路径,到目录
echo %~p1
:: 当前脚本第一个参数,并且补全到带驱动器的全路径
echo %~dp1
:: 脚本本身的补全
echo %~dp0
@REM 全路径补全,包含文件本身
echo %~f0
pause