shell编程笔记1–shell基础
shell编程笔记1–shell基础
shell是linux使用者必须掌握的一项技能,笔者使用了多年的linux一直以来没有来得及做一个简单的学习文档,最近抽空将常用的shell编程要点加以总结,以供后续查阅;后续也会在此基础上持续更新,并补充重要知识点和功能函数。
笔记1–变量
-
变量定义与引用
字符串类型,不解析任何字符。var1='var1'
双引号内部会解析$和反斜杠特殊字符。
var2="var2\var3"
反引号或$()执行系统命令
now_date=\`date\` cur_dir=\$(pwd)
使用变量直接加$即可,变量名外面的花括号是可选的,加花括号是为了帮助解释器识别变量的边界
echo $var1 $var2 $now_date "current dir:\${cur_dir}"
-
变量类型
常见环境变量:
PATH:系统路径;
HOME:当前用户家目录;
HISTSIZE:保存历史命令记录的条数;
LOGNAME:当前用户登录名;
HOATNAME:主机名称,若应用程序要用到主机名的话,一般是从这个环境变量中的取得的;
SHELL:当前用户用的是哪种shell;
LANG:和语言相关的环境变量,使用多种语言的用户可以修改此环境变量;
env 可以查看所有环境变量 -
预定义变量
预定义变量为shell内部使用的变量,与不能重定义,常见如下:
$@ 传入的所有参数
$# 位置参数的数量
$* 所有位置参数的内容
$? 命令执行后返回的状态, 0表示成功,非0表示失败
$$ 当前进程的进程号
$! 后台运行的最后一个进程号,很少用
$0 当前执行的进程名,即执行脚本
$1-n 传入的1-n个参数测试: bash test.sh para1 para2 para3 $@ para1 para2 para3 $# 3 $* para1 para2 para3 $? 0 $$ 18687 $! $0 test.sh $1 para1
-
declare和typeset
命令两者等价,都是用来定义变量类型的,相关参数说明如下:
-r 将变量设为只读
-i 将变量设为整数
-a 将变量定义为数组
-f 显示此脚本前定义过的所有函数及内容
-F 进显示此脚本前定义过的函数名
-x 将变量声明为环境变量,env中可以查看到定义的环境变量
示例:$ num1=1 $ num2=$num1+1 $ echo $num2 1+1 $ declare -i num3 $ num3=$num1+10 $ echo $num3 11
-
注释
单行注释方法为: # 注释内容
多行注释方法:方法1: if false; then echo '1' echo '2' fi 方法2: ((0))&&{ echo '1' echo '2' }
-
重定向
cmd > file #重定向到file
cmd >> file #追加的方式重定向到file
cmd > /dev/null #重定向到空设备文件
linux下输入、输出、错误描述符:
标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据;
标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据;
标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
cmd > file 2>&1 # 将标准输出和标准错误输出重定向到file文件。
cmd > /dev/null 2>&1 # 屏蔽所有输出 -
交互式输入read
read可在shell中实现交互式输入, -p可实现带提示的交互式输入,示例如下:function interact(){ echo 'input var1:' read var1 echo 'var1 is ' $var1 read -p 'input var2:' var2 echo 'var2 is ' $var2 } 执行结果: input var1: 001 var1 is 001 input var2 002 var2 is 002
笔记2–运算、比较操作
-
加减乘除三种表示方法
1-let命令:
let “sum=3+5”
echo $sum
2-expr命令:
sum=`expr 2 – 5` #注意-左右有空格
echo $sum
注意:使用乘法 * 的时候需要转义\*否则会出错。
3-使用(( … )) 的形式:
sum=$((3+5))
echo $sum
4-使用bc进行浮点运算
使用方法:variable=`echo “OPTIONS; OPERATIONS” | bc`
例如:n=`echo “scale=3; 13 / 2” | bc`
echo $n 结果为6.500,其中scale=3表示小数点位数。 - 常见比较操作
-
test整数比较方法
大于 -gt
小于 -lt
大于等于 -ge
小于等于 -le
等于 -eq
不等于 -ne示例: $ a=100 $ b=200 $ test $a -lt $b $ echo $? 0 $ test $a -eq $b $ echo $? 1
-
test字符串比较
测试空字符串-z
测试字符串的长度为非零-n
等于某一个字符串=不等于某一个字符串!= -
test逻辑与非
-a 逻辑与
-o 逻辑非$ a=100 $ b=200 $ test $a -eq 100 -a $b -eq 200 $ echo $? 0 $ test $a -eq 101 -o $b -eq 200 $ echo $? 0 $ test $a -eq 101 -o $b -eq 201 $ echo $? 1
-
文件比较
文件类型说明:
常规文件 –
目录文件 d
字符设备 c
块设备 b
套接字 s
链接 l
test -f $filename #文件存在
test -d $filename #目录存在
test -r $filename #文件是否可读
test -w $filename #文件是否可写
test -x $filename #文件是否可执行
笔记3–字符串 数组
3.1 字符串
-
字符串分割方法:
假设有变量 var=http://www.aaa.com/123.htm.-
#号截取,删除左边字符,保留右边字符。
echo ${var#
//}
其中 var 是变量名,# 号是运算符,
// 表示从左边开始删除第一个 // 号及左边的所有字符
即删除 http://
结果是 :www.aaa.com/123.htm -
# 号截取,删除左边字符,保留右边字符。
echo ${var##
/}
##
/ 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符
即删除 http://www.aaa.com/
结果是 123.htm -
%号截取,删除右边字符,保留左边字符
echo ${var%/
}
%/
表示从右边开始,删除第一个 / 号及右边的字符
结果是:http://www.aaa.com -
%% 号截取,删除右边字符,保留左边字符:
echo ${var%%/
}
%%/
表示从右边开始,删除最后(最左边)一个 / 号及右边的字符
结果是:http: -
从左边第几个字符开始,及字符的个数
复制代码 代码如下:
echo ${var:0:5}
其中的 0 表示左边第一个字符开始,5 表示字符的总个数。
结果是:http: -
从左边第几个字符开始,一直到结束。
echo ${var:7}
其中的 7 表示左边第8个字符开始,一直到结束。
结果是 :www.aaa.com/123.htm -
从右边第几个字符开始,及字符的个数
echo ${var:0-7:3}
其中的 0-7 表示右边算起第七个字符开始,3 表示字符的个数。
结果是:123 -
从右边第几个字符开始,一直到结束。
echo ${var:0-7}
表示从右边第七个字符开始,一直到结束。
结果是:123.htm
注:左边的第一个字符是用 0 表示,右边的第一个字符用 0-1 表示
-
#号截取,删除左边字符,保留右边字符。
-
expr字符串操作
-
字符串长度
$ str1="abcde12345" $ echo ${#str1} 10 $ expr length $str1 10
-
取子字符串
1)expr substr $string $position $length #注意位置编号从1开始 2)echo ${string:\$pos:$length} # 注意位置编号从0开始 $ string="abcde1234567890" $ expr substr $string 1 3 abc $ echo ${string:0:3} abc
-
字符串长度
-
字符串连接
$ str1=abc $ str2=def $ str3=\$str1\$str2 $ echo $str3 abcdef $ str3="\${str1}-${str2}" # 通过{}可以自定义分隔符 $ echo $str3 abc-def
-
字符串替换
$ string="you and you and zhangsan" $ echo ${string/you/YOU} # 只替换一次 YOU and you and zhangsan $ echo ${string//you/YOU} # 全部替换 YOU and YOU and zhangsan
3.2 数组
数组常见操作如下:
1)定义一个数组: myarray=(1 2 3 4 5)
2)读取数组的某一个元素 : echo ${myarray[下标值]} # 注意数组名称必须使用{} 括起来, 下标值从0开始编号
3)数组元素的赋值: myarray[下标值] = xxx
4)显示数组的所有元素: echo ${myarray[*]}
5)获得数组的长度: echo ${#array[@]} 或者 echo ${#array[*]}
6)删除一个数组元素: unset myarray[下标值]。
案例:for循环输出数组
function printarray(){
arr=(0 1 2 3 4 5 6)
len=${#arr[@]}
for (( i=0; i<$len; i++ ))
do
echo ${arr[$i]}
done
}
注意:for循环中 (( 与 ))和其它变量之间有空格
笔记4–顺序 选择 循环结构
4.1 顺序
shell默认采用顺序结构执行各条命令
4.2 选择
-
if else选择
if elif else是最简单的选择结构,具体示例如下:function testslect(){ read -p "input num1: " num1 read -p "input num2: " num2 if [ $num1 -gt $num2 ];then echo $num1 '>' $num2 elif [ $num1 -lt $num2 ];then echo $num1 '<' $num2 else echo $num1 '=' $num2 fi } 输出结果: input num1: 1 input num2: 2 1 < 2
-
case 选择
case是常用的一种选择结构,具体示例如下:#!/bin/bash usage() { echo "this is help!" } client() { echo "this is client!" } master() { echo "this is master!" } case "$1" in -h) usage ;; --help) usage ;; client) client ;; master) master $2 ;; *) echo "Unknown command: $1" usage exit 1 ;; esac #输出结果: $ bash case_test.sh -h this is help! $ bash case_test.sh client this is client!
4.3 循环
-
for 循环
shell中通过for实现循环,示例如下:function printarray(){ arr=(0 1 2 3 4 5 6) len=${#arr[@]} for (( i=0; i<$len; i++ )) do echo ${arr[$i]} done }
shell 中通过 for 遍历文件夹中的目录
for file in `ls`; do echo $file; done
-
while 循环
shell中通过while实现循环,示例如下:#!/bin/bash sum=0 i=0 while ((i<100)) do ((sum=sum+i)) ((i++)) done echo $i echo $sum 案例2: 每隔一小添加用户,防止用户ssh被系统刷掉 #!/bin/bash sum=0 while [ $sum -lt 864000 ] do cd /root bash adduser.sh lifeng06 sleep 3600 let sum=sum+3600 done exit
笔记5–函数
5.1 函数使用方法
shell中函数定义形式如下:
function functionName(){
cmd1
cmd2
}
其中,function可以省略;执行的时候直接functionName $para1 $para2即可。
5.2 shell常见函数
-
时间日期相关函数
1. $ date 2020年 01月 20日 星期一 11:13:45 CST 2. $ date +"%Y%m%d" 20200120 3. $ date +"%Y-%m-%d %H:%M:%S" 2020-01-20 11:17:09 4. date +"%Y%m%d" -d "+n year|month|day" 今天的后n年|月|日 日期 5. date +"%Y%m%d" -d "-n year|month|day" 今天的前n年|月|日 日期 6. date +"%Y-%m-%d %H:%M:%S" -d "+n hour|minute|second" 今天的后 n时|分|秒 日期 7. date +"%Y-%m-%d %H:%M:%S" -d "-n hour|minute|second" 今天的前 n时|分|秒 日期
- to add …
笔记6–常见shell小功能
6.1 批量检测 ip 是否ping通
#!/bin/bash
abspath=$(cd "$(dirname "$0")"; pwd)
logs=$abspath'/test_ping.log'
iplist=(192.168.1.1 192.168.1.2 192.168.1.22)
len=${#iplist[@]}
for (( i=0; $i<$len; i++ ))
do
ping -c 2 ${iplist[$i]} &>/dev/null
if [ $? -eq 0 ]; then
echo -e "$(date) ${iplist[$i]} is up\t" >> $logs
else
echo -e "$(date) ${iplist[$i]} is down\t" >> $logs
fi
done
6.2 批量检测用户是否有 ssh 登录权限
function test_ssh(){
abspath=$(cd "$(dirname "$0")"; pwd)
logs=$abspath'/test_ssh.log'
iplist=$(cat ip.txt|tr -d '\r')
for i in $iplist
do
ping -c 2 $i &>/dev/null
if [ $? -eq 0 ]; then
ssh -o "ConnectTimeout=5" -o "StrictHostKeyChecking=no" -p yourport -l yourname $i exit
if [ $? -eq 0 ]; then
echo -e "[$(date)] [$i is connnected], [\$? $?]\t" >> $logs
else
echo -e "[$(date)] [$i is disconnected] [\$? $?]\t" >> $logs
fi
else
echo -e "[$(date)] [$i is down] [\$? $?]\t" >> $logs
fi
done
}
测试结果:
[Sun 10 Nov 2019 11:14:04 AM CST] [xx is connnected], [$? 0]
[Sun 10 Nov 2019 11:14:15 AM CST] [xx is down] [$? 0]
[Sun 10 Nov 2019 11:14:26 AM CST] [xx is down] [$? 0]
[Sun 10 Nov 2019 11:14:32 AM CST] [xx is disconnected] [$? 0]
注意:需要使用ConnectTimeout、StrictHostKeyChecking、exit三个参数,否则会在相应界面等待输入或占用太长时间。ip.txt中存放所有ip,需按照行存放。tr -d ‘\r’主要为了删除换行符,否则在有些bash里面执行ssh的时候会出现错误。
6.3 help 菜单实现方法
1)直接使用echo
function usage()
{
echo 'usage:'
echo ' bash dh.sh -h|--help|start|stop'
}
2)使用EOF标识
function usage() {
cat <<_EOF
usage:
bash dh.sh -h|--help|start|stop
_EOF
}
3)使用EOF将多行文件写入到指定文件
当系统中没有编辑工具的时候,我可以通过cat 和EOF将多行文件重定向到指定文件
可以使用_EOF,也可以使用EOF
cat <<EOF >target.txt
line one
line two
EOF
6.4 拷贝当前目录指定文件夹到备份文件夹下面
########功能说明###########
# 获取当前目录下所有的_out结尾的文件夹,并在backupfiles目录创建对应的_out文件;
# 根据 file_time 格式,将_out 中指定年月的文件移动到 backupfiles目录对应的_out文件中;
# 使用方式: 将 mvfile.sh 文件和_out 文件夹放在同一级目录下面即可;
##########################
# vim mvfile.sh
#file_time='202007,202008,202009'
file_time='202010'
mkdir -p backupfiles
cd backupfiles
ls ../ |grep _out|xargs mkdir -p
cd ..
for dir_out in `ls|grep _out`
do
pwd
# echo 'aaa'$dir_out
cd $dir_out
for file in `ls|grep $file_time`
do
mv $file ../backupfiles/$dir_out
done
cd ..
done
参考文献