目录
1.数组的基本脚本
2.子shell与父shell
3.子shell与父shell进阶
4.基础函数
5.函数进阶之作用域域返回值
6.进程控制与文件描述和管道
7.排序算法
8.花阔号的使用
9.波浪号
10.变量替换
11.高级变量替换
12.命令替换
13.算术替换
14.进程替换
15.单词切割
16.路径替换
17.随机获取密码
18.shell解释器和属性
19.初始化命令shopt终端与tput终端
20.trap信号捕捉
21.shell脚本内容排错与进度条
22.xargs参数传参
23.shift移动参数
24.其他
#!/bin/bash name[0]="jacob" # 定义一个数组 name[1]="Rose" # 附加数组 name[2]="Rick" # 附加数组 name[3+3]="TinTin" # 追加数组,相当与在第6位追加 echo ${name[0]} # 打印数组第一个内容 echo ${name[1]} # 打印数组第二个内容 echo ${name[6]} # 打印数组第四个内容或echo ${name[3+3]} name[3+3]="Tin" # 如果有值则是修改 echo ${name[*]} # 打印数组所有值,会把值当成一个整体 echo ${name[-1]} # 打印数组最后一个值 echo ${name[-2]} # 打印数组最后第二个值 echo ${#name[*]} # 打印数组内的值的个数 echo ${name[@]} # 打印数组所有值,分为单个个体 i=1 addr[$i]="shanghai" # 变量名不能用变量定义,但数组内的索引可以 name1=(jacob Rose Rick TinTin) # 数组还可以用这样定义 echo ${!name1[*]} # 获取所有数组的索引或echo ${!name1[@]} root=$(df / |tail -n +2) # 定义一个命令数组变量 echo ${root[*]} # 打印该命令数组所有值 echo ${root[1]} # 打印该命令数组第二个值 # 数组进阶---------------关联数组 #!/bin/bash daclare -A man # 声明数组是关联数组 # 定义关联数组 man[name]=TOM man[age]=23 man[addr]=shangzheng man[phon]=12345678987 echo ${man[*]} # 打印所有关联数组的值 woman=([name]=lisi [age]=25 [addr]=shaoyan [phon]=98765432123) # 或者可以这么>定义 unset woman[age] # 删除数组内的某个键 unset woman # 删除数组 ###--------------脚本1------------------ #!/bin/bash # 遍历数组内容 name=(zhansan lisi wanger fudo) for i in "${name[@]}" do echo $i done ###--------------脚本2------------------ #!/bin/bash # 遍历数组下标,并根据小标打印出数组的值 for i in ${!name[@]} do echo ${name[i]} done ###--------------脚本3------------------ #!/bin/bash # 遍历数组下标,用while循环的方式循环 i=0 while [ $i -le ${#name[@]} ] do echo ${name[i]} let i++ done ###--------------脚本4------------------ #!/bin/bash # 取出关联数组的值 declare -A woman woman=([name]=zhangsan [age]=45 [addr]=shan [phon]=1111111111) # echo ${!woman[*]} # 此时取出的是关联数组名,值不会取出 for i in ${!woman[@]} do echo ${woman[$i]} done
###--------------脚本1------------------### #!/bin/bash hi="hello" echo "++++++++++++++++++++++++++++" echo "+ 我是父shell +" echo "++++++++++++++++++++++++++++" echo "PWD=$PWD" echo "bash_subshll=$BASH_SUBSHELL" # 当前父shell是0 # 通过()开启子shell ( sub_hi="I am a subshell" echo -e "\t+++++++++++++++++++++++" echo -e "\t 进入子shell +" echo -e "\t+++++++++++++++++++++++" echo -e "\tPWD=$PWD" echo -e "\tbash_subshll=$BASH_SUBSHELL" # 当前子shell是1 echo -e "\thi=$hi" echo -e "\tsub_hi=$sub_hi" # 此时在子shell中定义的变量可以打印 cd /etc;echo -e "\tPWD=$PWD" ) echo "++++++++++++++++++++++++++++" echo "+ 返回父shell +" echo "++++++++++++++++++++++++++++" echo "PWD=$PWD" echo "hi=$hi" echo "sub_hi=$sub_hi" # 此时在子shell中定义的变量不可以打印 echo "bash_subshll=$BASH_SUBSHELL" # 返回父shell就是0 ###--------------脚本2------------------### #!/bin/bash hi="hello" echo "++++++++++++++++++++++++++++" echo "+ 我是父shell +" echo "++++++++++++++++++++++++++++" echo "bash_subshll=$BASH_SUBSHELL" # 通过()开启子shell ( echo -e "\t+++++++++++++++++++++++" echo -e "\t+ 进入子shell +" echo -e "\t+++++++++++++++++++++++" echo -e "\tbash_subshll=$BASH_SUBSHELL" ( echo -e "\t\t+++++++++++++++++++++++" echo -e "\t\t+ 进入子shell +" echo -e "\t\t+++++++++++++++++++++++" echo -e "\t\tbash_subshll=$BASH_SUBSHELL" pstree | grep subshell ) ) echo "++++++++++++++++++++++++++++" echo "+ 返回父shell +" echo "++++++++++++++++++++++++++++" echo "bash_subshll=$BASH_SUBSHELL" ###--------------脚本3------------------### #!/bin/bash # 使用管道开启子shell sum=0 df | grep "^/"|while read name total used free other do echo "free=$free" let sum+=free echo "sum=$sum" done echo $sum # 注意此时如果是0的话就是错误 ###--------------脚本4------------------### # 如果不想起子shell则不要用管道 #!/bin/bash tmp_file="/tmp/subshell-$$.txt" df | grep "^/" > $tmp_file while read name total used free other do let sum+=free done < $tmp_file rm -rf $tmp_file echo $sum ###--------------脚本5------------------### # 加载其他外部命令或加载其他变量的脚本也会开启子shell #!/bin/bash file="/etc/passwd" password="I-have-a-dream" error_info="Please try again later" # 另起一个脚本 #!/bin/bash pstree bash /root/shell.sh/env.sh echo "passwd=$password" # 当执行的是bash后,子进程进入后就会退出所以变量不会有 echo "Error:$error_info" source ./env.sh # 当执行的是source后,不会进入子进程所以变量会一直存在 echo "passwd=$password" echo "Error:$error_info" ###--------------脚本6------------------### #!/bin/bash # 使用&在后台开启子shell count=0 for i in {1..254} do ping -c1 -i0.2 -W1 172.17.0.$i && let count++ & done echo $coun
1)fork方式
使用绝对路径或相对路径执行一个命令时,都会由父进程开启一个子进程
#!/bin/bash sleep 5 /root/shell.sh/env.sh cd /root/shell.sh/; ./env.sh # 调用外部命令时会fork子进程,env.sh脚本就只有一个pstree
2)exec方式
使用exec不会开启子进程,而是使用新的程序替换当前shell环境,一般使用先把exec写入另外一个脚本,使用fork方式调用exec脚本
#!/bin/bash # 使用exec方式调用外部命令或脚本 exec ls echo "test" # 此时不会执行,因为现在整个脚本只会执行ls cd /etc # 不会执行整个脚本只会执行ls # 注意:当exec后面的参数是文件重定向时,不会替换当前shell,脚本后面也就不会有影响
3)source方式
可以不开启子shell,而是当前shell环境中将需要执行的命令加载进来,执行完加载命令之后,继续执行脚本后续指令
#!/bin/bash # 使用source加载外部脚本 source /root/shell.sh/env.sh echo "hi $password" ls / # source将env.sh脚本的内容加载到当前shell而,后面的直接调用
###--------------脚本1------------------### #!/bin/bash # 定义函数............................. mymkdir () { mkdir /root/shell.sh/test touch /root/shell.sh/test/hi.txt } mymkdir # 调用函数 unset mymkdir # 取消函数定义 ###--------------脚本2------------------### # 调用函数 #!/bin/bash function print_usage() { cat << EOF Usage: --help | -h Print help information for script Usage: --memory | -m Monitor memory information Usage: --network | -n Monitor network interface information EOF } case $1 in --memory|-m) free;; --network|-n) ip -s link;; --help|-h) print_usage;; *) print_usage esac ###--------------脚本3------------------### # 定义函数位置变量 mymkdir() { mkdir -p $1 touch $1/$2; } mymkdir $1 $2 # 调用
###--------------脚本1------------------### # 默认函数启动都是不开启子shell的,所以当外函数外定义变量函数内也可以用甚至可以修改,在函数内定义变量亦然;注:次变量与export声明的变量还是有区别的,只在当前shell有效 #!/bin/bash # 默认定义变量为当前Shell中全局有效 global_var1="hello" global_var2="world" # 定义demo函数,在函数内定义新的变量,并修改函数。 function demo() { echo -e "\033[46mfunction [demo] started....\033[0m" func_var="Topic" global_var2="this is xiugai" echo "$global_var1 $global_var2" echo -e "\033[46mfunction [demo] end.\033[0m" } demo echo echo -e "func_var=$func_var \nglobal_var1=$global_var1 \nglobal_var2=$global_var2" # 此时发现变量global_var2修改了 ###--------------脚本2------------------### # 在函数内加入local就会声明局部变量,就不会修改外部的变量了 #!/bin/bash global_var1="hello" global_var2="world" function demo() { echo -e "\033[46mfunction [demo] started....\033[0m" local global_var2="this is xiugai" # 这里声明局部变量local echo "$global_var1 $global_var2" echo -e "\033[46mfunction [demo] end.\033[0m" } demo echo echo "$global_var1 $global_var2" ## 注意函数不调用函数内定义的函数就不会生效 ###--------------脚本3------------------### # 注:如果在函数内写exit就会退出整个脚本,但是用return设置返回值就不会退出脚本, #!/bin/bash function demo1() { uname -r } demo2() { echo "start demo2" return 100 echo "demo2 end" } demo3() { echo "hello" exit # 注意这里使用了exit所以最后一个echo没有执行 } demo1 echo "demo1 status $?" demo2 echo "demo2 status $?" demo3 echo "demo3 status $?"
1)文件描述符
#!/bin/bash ps aux # 查看并发进程 ls -l /proc/$$/fd # 查看当前shell的文件描述符 ls -l /proc/1/fd # 查看systemd的文件描述符 ps -ao user,pid,comm| grep vim # 查看所有并发进程并过滤出vim进程的user,pid,comm字段 ls -l /proc/23078/fd | tail -1 # vim默认会将所有文件存入该*.swp文件中,只有保存才会写入 exec 12> test.txt # 创建仅可输出的文件 echo hello >&12 # 通过&12调用文件描述符 exec 12<&- # 关闭输出描述符 exec 13< test.txt # 创建仅可输入的文件,文件尽可读取一次 exec 13<&- # 关闭输入描述符 exec 13<>test.txt # 创建可读写的文件描述 cat &<14 # 读取文件内容 echo "Rick" >&14 # 重定向写入 exec 14<&- # 关闭输入描述符 read -u12 content # 读取一行赋值给变量(echo $cotent查看)【注:每次仅可以读 取一行,读取结束为空】 read -u12 -n1 content # 仅读取一个字符的数据-n指定个数
2)密名管道符
#!/bin/bash mkfifo pipe_file1 # 创建命名管道,不指定权限 mkfifo -m 644 pipe_file2 # 创建命名管道,指定权限 ls -l pipe_file1 # 查看文件属性,第一列为pecho "hello world" > pipe_file1 # 使用该命令会进入(交互)阻塞状态,只有去查看才会结束 cat pipe_file1 # 另开个终端,上面的就会结束,如果上面的没有写入数据>会进入(交互)阻塞状态,写入才会结束 ###————————————————————————————————————————————————————————————### #!/bin/bash # 创建命名管道文件,并绑定固定的文件描述符 pipefile=/tmp/procs_$$.tmp mkfifo $pipefile exec 12<>$pipefile # 通过文件描述符往管道中写入5行任意数据,用与控制进程数 for i in {1..5} do echo "" >&12 & done # 通过read -u命令选项指定从特定的文件描述符中读取数据行 for j in {1..20} do read -u12 { echo -e "\033[32mstart sleep No.$j\033[0m" sleep 5 echo -e "\033[31mstart sleep No.$j\033[0m" echo "" >&12 } & done wait rm -rf $pipefile
1)冒泡排序
###---------脚本1随机6个数字,对比5次-------### #!/bin/bash for i in {1..6} do read -p "请输入数字:" tmp if echo $tmp | grep -qP "\D" ;then echo "您输入的不是数字" exit fi num[$i]=$tmp done echo "您输入的数字序列为:${num[@]}" # 冒牌排序 # 使用i控制进行几轮比较,使用j控制每轮比较的次数, # 对6个数字而言,需要进行5论比较,每进行一轮后,下一轮就可以少比较一次 for ((i=1;i<=5;i++)) do for ((j=1;j<=$[6-i];j++)) do if [ ${num[j]} -gt ${num[j+1]} ];then tmp=${num[j]} num[$j]=${num[j+1]} num[j+1]=$tmp fi done done echo "排序后的数字为:${num[@]}" ###---------脚本2-------### #!/bin/bash # 根据进程所占物理内存大小进行排序 # 保存系统所有进程的名称及物理内存大小的所有数据文件 tmpfile="/tmp/procs_mem_$$.txt" ps --no-headers -eo comm,rss > $tmpfile # 定义冒泡排序 # 使用i控制几轮的比较,使用j控制比较的次数 # 使用变量len读取数组个数,根据内存大小排序,并调整对应的进程名称顺序 bubble() { local i j local len=$1 for ((i=1;i<=$[len-1];i++)) do for ((j=1;j<=$[len-1];j++)) do if [ ${mem[j]} -gt ${mem[j+1]} ];then tmp=${mem[j]} mem[$j]=${mem[j+1]} mem[j+1]=$tmp tmp=${name[j]} name[$j]=${name[j+1]} name[j+1]$tmp fi done done echo "排序后进程序列" echo "----------------------------------------------------------" echo "${name[@]}" echo "----------------------------------------------------------" echo "${mem[@]}" echo "----------------------------------------------------------" } i=1 while read proc_name proc_mem do name[$i]=$proc_name mem[$i]=$proc_mem let i++ done < $tmpfile rm -rf $tmpfile bubble ${#mem[@]}
2)快速排序
#!/bin/bash num=(5 3 8 4 7 9 2) quick_sort() { # 先判断需要进行比较的数字个数,$1是数字最左边的坐标,$2是数组的最右边的坐标 # 当左边的坐标小于右边的左标,表示需要排序的数字只有一个,不需要排序直接退出函数 if [ $1 -ge $2 ];then return fi # 定义局部变量,base为基准数字,这里选择的是最左边的数字num[$1] # i表示左边的坐标,right表示右边的坐标(也可以用i和j表示) local base=${num[$1]} local left=$1 local right=$2 # 在排序的数字序列中,比基数大的数字放右边,比基准数小的数字放左边 while [ $left -lt $right ] do # right向左移动,查找比base基数小的元素 while [[ ${num[right]} -ge $base && $left -lt $right ]] do let right-- done # left向右移动,查找比基数base大的元素 while [[ ${num[left]} -le $base && $left -lt $right ]] do let left++ done if [ $left -lt $right ];then local tmp=${num[$left]} num[$left]=${num[right]} num[$right]=$tmp fi done # 将基数字与left坐标元素交换 num[$1]=${num[left]} num[left]=$base # 递归调用快速排序算法,对i左边的元素快速排序 quick_sort $1 $[left-1] # 递归调用快速排序算法,对i右边的元素快速排序 quick_sort $[left+1] $2 } # 调用函数对数组进行排序,排序后输出数组的所有元素 quick_sort 0 ${#num[@]} echo ${num[*]}
3)插入排序
#!/bin/bash set -uex # 随机创建5个数字赋值给数组变量num for x in {1..5} do read -p "请输入整数:" tmp num[$x]=$tmp done # 默认第一个已经为有序数字 # 使用变量j对比 for ((i=2;i<=5;i++)) do # 使用j控制第i个元素前面需要比较数字 # j从第i-1个数字元素开始,每循环一次往前移动一位 tmp=${num[i]} for ((j=$[i-1];j>=0 && $tmp
4)计数排序
#!/bin/bash # 创建需要排序的数组 num=(2 8 3 7 1 4 3 2 4 7 4 2 5 1 8 5 2 1 9) # 创建另一个对应上面最大值得数组的初始值为0,最大值为9所以创建10个 count=(0 0 0 0 0 0 0 0 0 0) # num数组中有19个数,小标为0~18,使用循环读取num每个元素的值 # 以每个元素的值为count的下标,进行自加1的统计运算 for i in `seq 0 18` do let count[${num[$i]}]++ done # 使用循环读取count数组中的每个元素值(也就是次数) # 根据次数打印对应下标 for i in `seq 0 9` do for j in `seq ${count[i]}` do echo -n "$i" done done echo ###---------脚本2---------------### # 自动分析排序数组最大值 #!/bin/bash # 创建需要排序的数组 num=(2 8 3 7 1 4 3 2 4 7 4 2 5 1 8 5 2 1 9) # 自动分析排序数组最大值 max=${num[0]} for i in `seq $[${#num[@]}-1]` do [ ${num[i]} -gt $max ] && max=${num[i]} done for i in `seq 0 $max` do count[$i]=0 done for i in `seq 0 $[${#num[@]}-1]` do let count[${num[$i]}]++ done # 使用循环读取count数组中的每个元素值(也就是次数) # 根据次数打印对应下标 for i in `seq 0 $[${#count[@]-1}]` do for j in `seq ${count[i]}` do echo -n "$i" done done echo
#!/bin/bash echo {a,b,c} # 对字符串扩展 echo {hello,world} echo {22,33,44,55} echo {a..z} # 对字符串序列扩展 echo {a..z..2} # a至z,步长为2 echo {a..z..3} echo {1..9..2} # 1至9,步长为2 # 注花括号使用不可以使用引号 echo t{i,o}p # 花括号前后可以添加字符串并支持扩展嵌套 echo t{o,e{a,m}}p mkdir -p /test/t{o,e{a,m}}p touch /test/t{o,e{a,m}}p/{a,b,c,d,e}t.txt # 可以用tree /test查看 cp /test/top/at.txt{,.bak} # 利用扩展备份文件使用ls /test/top/at*查看 mv /test/top/at.txt{,bt.doc} # 利用扩展,重命名
#!/bin/bash echo ~ # 显示当前用户的家目录 echo ~/test echo ~tom # 显示特定用户的家目录 echo ~+ # 显示当前工作目录 echo ~- # 显示当前工作目录的前一个目录
#!/bin/bash # 正常的调用 h="go Spurs Go" echo $h echo ${h} # 防止与其他字符混淆 # 间接的调用变量,类似与调用数组的变量 player="DUNCAN" mvp=player echo ${mvp} # 直接返回变量player本身 echo ${!mvp} # 间接的调用player变量的值,不过尽可以实现一层的 # 定义初值(当变量非空时,返回变量本身) echo $animals # 当调用的变量为空或不存在是不会打印东西的 echo ${animals:-dog} # 此时变量还是没有赋值,所以会打印出dog echo ${animals:=dog} # 此时变量没有赋值,会打印出dog并给变量赋值 # 判断变量是否有值 echo ${ai:?'你没有输入的变量的值'} echo ${key:+lock} # 当变量有值时返回值,没有值则返回空 ###---------脚本2---------------### #!/bin/bash home="Hello World I'am have python book" echo ${home:2} # 从位置2开始截取到变量的末尾,没有设置截取范围默认截取到末尾 echo ${home:14} # 从位置14开始截取到变量的末尾 echo ${home:14:6} # 从位置14开始截取6个字符结束 echo ${home#Hello} # 从左往右匹配将匹配的Hello删除,掐头,只匹配第一个 echo ${home#World} # 变量开头无法匹配World,返回变量本身 echo ${home#*o} # 匹配o及左边的所有内容删除,但只匹配第一个 echo ${home##*o} # 匹配o及左边的所有内容删除,匹配所有 echo ${home%book} # 从右往左匹配将匹配的book删除,去尾,只匹配第一个 echo ${home%%o*} # 从右往左匹配将匹配的所有o删除,,匹配所有
#!/bin/bash tools=(this is a tools) echo ${tools[0]} # 正常提取数组中的一个元素 echo ${tools[0]:0:2} # 对数组中的某一个元素截取字符串,从第0个开始往后截取两个 echo ${tools[1]:0:2} echo ${tools[2]:3:2} echo ${tools[4]#*o} # 根据数组中某个元素进行掐头操作 echo ${tools[4]##*o} echo ${tools[0]%*s} # 根据数组中某个元素进行去尾操作 echo ${tools[0]%%*s} ###---------脚本2---------------### #!/bin/bash echo ${!U*} # 列出以U开头的所有变量,打印出来会成为一个整体 echo ${!U@} # 列出以U开头的所有变量,打印出来会成为多个 echo ${!HO@} test1=(11 22 33 44) test2=(77 88 99 00) echo ${!test1[*]} # 返回数组下标 declare -A str # 定义关联数组 str[a]=aaa str[b]=bbb str[word]="key value" echo ${!str[*]} # 打印所有数组的下标 echo ${!str[@]} play="Go Spurs Go" echo ${#play} # 统计变量长度 hi=(hello the world) echo ${#hi} # 没有写数组下标默认hi[0]的长度 echo ${#hi[0]} echo ${#hi[1]} phone=18811011011 echo ${phone/1/x} # 将1替换成x,仅替换第一个 echo ${phone//1/x} # 将1替换成x,替换所有 echo ${phone/110/x} # 仅替换第一个110为x echo ${phone//110/x} #替换所有110为x echo $phone #注意变量不会真的改变 lowers="hello the world" echo ${lowers^} # 将首字母替换为大写 echo ${lowers^^} # 将所有字母替换为大写 echo $lowers #注意变量不会真的改变 echo ${lowers^h} # 将第一个h字母替换为大写 echo ${lowers^^h} # 将所有h字母替换为大写 echo ${lowers^^[heo]} # 将所有h,e,o字母替换为大写 uppers="HELLO THE WORLD" echo ${lowers,} # 将首字母替换为小写 echo ${lowers,,} # 将所有字母替换为小写 echo "${lowers,H}" # 将第一个H字母替换为小写 echo "${lowers,,H}" # 将所有H字母替换为小写 echo ${lowers,,[HEO]} # 将所有H,E,O字母替换为小写 ###---------脚本3---------------### #!/bin/bash # 批量修改扩展名 if [[ -z "$1" || -z "$2" ]];then echo "Usage:$0 旧扩展名 新扩展名" exit fi for i in `ls *.$1` do mv $i ${i%.$1}.$2 done ###---------脚本4---------------### #!/bin/bash # 批量修改扩展名,指定路径 if [[ -z "$1" || -z "$2" || -z "$3" ]];then echo "Usage:$0 指定路径 旧扩展名 新扩展名 " exit fi for i in `ls $1/*.$2` do mv $i ${i%.$2}.$3 done
#!/bin/bash echo -e "system CPU load:\n$(date +%Y-%m-%d;uptime)" # 嵌套替换 echo -e "system CPU load:\n`date +%Y-%m-%d;uptime`" echo "系统当前登录人数:$(who|wc -l)" echo "系统当前登录人数:`who|wc -l`" du -sh $(pwd)
#!/bin/bash i=1 # 定义初始值 echo $((i++)) # 先显示i的值,再自加1 echo $((i++)) # 再加一 echo $((++i)) # 先自加1,再显示i的值 echo $((--i)) # 先自减1,再显示i的值 echo $((1+2)) # 加法运算 echo $((2*3)) # 乘法运算 echo $((20/5)) # 除法计算 echo $((20%5)) # 取余计算,20除5余0 echo $((2**3)) # 幂运算 echo $((2>=3)) # 逻辑比较,1表示对,0表示错 echo $((8>=3)) echo $((3>=3)) echo $((3>3)) echo $((3<=3)) echo $((3==3)) echo $((3==4)) echo $((3!=4))
#!/bin/bash who | wc -l # 通过管道传递给另一个进程作为输入的内容 wc -l <(who) # 使用<(who)会把结果保存到/dev/fd/63这个文件描述符,然后wc -l输出,文件描 述符是实时动态的当进程结束,文件描述符失效 paste <(cut -d: -f1,6 /etc/passwd) <(cut -d: -f2 /etc/shadow) # paste整合文件/etc/passwd第1列与第6列和文件/etc/shadow第2列 ls /etc/*.conf > /tmp/conf.log # 重定向后屏幕无输出 ls /etc/*.conf | tee /tmp/conf.log # 覆盖重定向后屏幕有输出 ls | tee >(grep sh$ >sh.log) >(grep conf$ > conf.log) # 使用ls|tee的输出结果通过进程替换 写入临时的文件描述符,最后通过grep对文件描述符进行过滤见以sh结尾的文件名输出到sh.log,以 conf结尾的文件重定向到conf.log
#!/bin/bash read -p "请输入3个字符:" x y z # 默认使用空格TAB制表符和换行符做分词处;请输入3个字符 1 2 3 read -p "请输入3个字符:" x y z # 请输入3个字符: 123 ;把123作为一个整体赋值给x IFS=$',' # 设置分词符号 read -p "请输入3个字符:" x y z # 请输入3个字符: 1,2,3 ;这样就会把123分别给xyz赋值
#!/bin/bash touch {a,A,b,B}.txt shopt -s nocaseglob # 设置shell属性不区分大小写 shopt nocaseglob # 查看设置好的属性,检查结果为on ls b* shopt -u nocaseglob # 关闭忽略大小写属性 ls !(a.txt) # 列出除a.txt以外的所有文件名 ls !(a.txt|b.txt) rm -rf [a-z].txt # 使用通配符删除文件 basename /a/b/c/d.txt # 获取一个路径中的文件名 dirname /a/b/c/d.txt # 仅保留路径,删除文件名 ###---------脚本2---------------### #!/bin/bash # 循环多个文件进行备份操作 for i in `ls /etc/*.conf` do tar -czf /root/log/$(basename $i).tar.gz $i done
###---------使用字符串随机获取------------### #!/bin/bash # 使用字符串截取的方式生成随机密码 # 定义变量:10个数字+52个字符 key="abcdefghijkmnopqstuvwxyzABCDFGHIJKMNOPQSTUVWXYZ0123456789" randpass() { if [ -z "$1" ];then echo "randpass 函数需要一个参数,用来指定提取的随机个数" return 127 fi # 调用$1参数,循环提取任意一个数据字符 # 用随机数对62取余,返回的结果为[0-6] pass="" for i in `seq $1` do num=$[RANDOM%${#key}] local tmp=${key:num:1} pass=${pass}${tmp} done echo $pass } # 创建临时测试账户,为账户配置随机密码,并将密码保存至/tmp/pass.log useradd tomcat passwd=$(randpass 6) echo $passwd | passwd --stdin tomcat echo $passwd > /tmp/pass.log ###---------使用命令随机获取------------### #!/bin/bash uuidgen # 生成随机字符串 openssl rand -hex 1 # 生成16进制随机字符串 openssl rand -hex 2 openssl rand -base64 1 # 生成含特殊符号的随机字符串,使用base64算法生成随机数据,其最终 长度为(n/3)向上取整后乘以4 openssl rand -base64 6 date +%s # 通过时间提取随机数字 ###---------使用块设备随机获取------------### #!/bin/bash cat /dev/random # 查看产生随机数据的设备文件 cat /dev/urandom # 与上一样 strings /dev/random tr -cd '_a-zA-Z0-9' /dev/random | head -c 10 # 查看产生随机数据的设备文件,只提取10位包 含字符,数字和下滑线的数据 ###---------使用Hash值随机获取------------### #!/bin/bash echo a | md5sum # 使用md5的hash值 echo a | md5sum | cut -d' ' -f1 md5sum /etc/passwd | cut -d' ' -f1 ###---------使用进程数获取------------### #!/bin/bash # 根据进程号生成随机文件 touch /tmp/$$.tmp # 根据进程数量生成随机文件 pnum=`ps aux | wc -l` touch /tmp/$pnum.tmp # 根据文件个数生成随机文件 fnum=`find /etc | wc -l` touch /tmp/$fnum.tmp # 根据文件行数生成随机文件 cnum=`cat /var/log/messages | wc -l` touch /tmp/$cnum.tmp
#!/bin/bash set -o # 查看shell属性 shopt # 与上类似 # 在字shell执行父变量 test=123 set -a # 开启allexport bash echo $test # 获取父shell set +a # 关闭allexport # Bash默认支持花括号,braceexpand echo {a..c} # 默认支持花括号 set +B # 关闭花括号扩展 set -B # 开启花括号扩展 # 当命令返回值非0时脚本直接退出 set -e useradd root echo "123456" | passwd --stdin root echo "已经将root密码修改改为:123456" # 使用Hash记录命令 hash # 通过hash命令查看记录列表 hash -d ip # 删除Hash表中的记录,注意当命令位置移动,在使用会报错,这个使用只要删除记录 就可以了 hash -r # 清空整个Hash表 set +h # 禁用Hash表 set -h # 开启Hash表 # 使用histexpand支持使用!调用历史命令 !yum # 默认支持 set +H # 禁用histexpand # 设置noclobber防止属性被覆盖 echo hello > test.txt # 默认支持覆盖重定向 set -C # 设置noclobber防止被覆盖 # 开启bash的nounset属性,防止变量未定义 set -u useradd $1 echo "$2" | passwd --stdin $1 ###-------------使用锁文件执行脚本--------### #!/bin/bash # 通过设置锁文件防止脚本重复执行 trap 'rm -rf /tmp/lockfile;exit' HUP INT # 检查是否锁文件 lock_check() { if (set -C; :> /tmp/lockfile) 2>/dev/null ;then backup else echo -e "\003[91m Warning: 其他用户在执行该脚本.\033[0m" fi } # 备份前锁文件,sleep10防止小数据备份太快,无法验证重复执行 backup() { touch /tmp/lockfile mysqldump --all-database > /var/log/mysql-$(date +%Y%m%d).bak sleep 10 rm -rf /tmp/lockfile } lock_check backup
####-----------shopt终端----------------#### #!/bin/bash hello=/etc # 定义变量 shopt -s cdable_vars # 开启cdable_vars属性 cd hello # 没有heelo目录但进入/etc shopt -u cdable_vars # 关闭cdable_vars属性 shopt -s cdspell # 开启自动纠错属性 cd /ect # 只能纠正部分错误 # 如果hash表中无法找到命令会报错,开启checkhash属性,没有找到命令继续正常的命令路径搜索 ip link show hash mv /usr/sbin/ip /usr/bin ip link show # 此时就会报错 shopt -s checkhash # 开启checkhash ip link show # cmdhist属性可以将多条命令保存到一条记录里,默认开启 for i in {1..3} do echo $i done history | tail -2 # 历史记录一行 shopt -u cmdhist # 关闭cmdhist,此时就会记录多条记录 shopt -s cmdhist # 开启cmdhist ###---------------tput终端---------------### #!/bin/bash tput cols # 显示当前终端的列数 tput lines # 通过lines显示当前终端的行数 tput clear # 通过clear终端清屏,与clear和ctrl+l一样 tput cup 10 20 # 移动光标至10行20列 tput sc # 保存当前光标 tput rc # 恢复光标位置 tput civis # 不显示光标 tput cnorm # 显示光标 tput civvis tput bold # 终端设置闪烁模式 tput blink # 终端设置加粗 tput rev # 当前终端字体与背景替换 tput smcup # 保存屏幕状态 tput rmcup # 恢复屏幕状态 tput sgr0 # 取消所有终端属性 reset # 重置终端为初始状态
#!/bin/bash kill -l # 显示所有信号 ps -aux | grep -v grep | grep loop.sh # 过滤指定进程 kill -19 14448 # 传递暂停信号 kill -CONT 14448 # 传递恢复信号 kill -SIGINT 14448 # 中断结束循环脚本 ###---------------trap代码---------------### #!/bin/bash # 用trap代码实现捕捉信号 trap 'echo "打死不中断|睡眠.";sleep 3' INT TSTP trap 'echo 测试;sleep 3' HUP while : do echo "signal" echo "demo" done # 注意shell不能捕捉所有代码,像TERM,KILL之类的
#!/bin/bash bash -x a.sh # -x排错功能,(++)表示该命令在子shell执行或者在脚本中使用一个-x ###---------------脚本1---------------### # 当执行一个脚本时,可以额外添加一些命令如下 #!bin/bash i=1 while [ $i -le 100 ] do let sum+=i done echo $sum # 当执行时会一直陷入死循环应为i始终为1 ###---------------脚本2---------------### #!/bin/bash ## 使用进度条作为提示符 # 防止Ctrl+c组合键无法结束进度条 trap 'kill $!' INT # 定义函数;实现无限显示不换行的#符号 bar() { while : do echo -n '#' sleep 0.3 done } bar & cp -r $1 $2 kill $! echo "复制结束" ~]# chmod +x shell16.sh ~]# ./shell16.sh /etc /mnt/ #############################复制结束 ## 注:$!表示shell中的最后一个后台PID ###---------------脚本3-----------------### #!/bin/bash ##使用背景色作为进度条 # 防止Ctrl+c组合键无法结束进度条 trap 'kill $!' INT # 定义函数;实现无限显示不换行的#符号 bar() { while : do echo -ne '\033[42m \033[0m' sleep 0.3 done } bar & cp -r $1 $2 kill $! echo "复制结束" ###---------------脚本4-----------------### #!/bin/bash ## 使用背景色作为进度条,复制大文件的进度条 # 防止Ctrl+c组合键无法结束进度条 trap 'kill $!' INT # 定义函数,当宽度为50的范围内输出进度条,#和占用48个空间,竖线占用2个宽度#1个#组合47个>格子=48,2个#组合46个格子=48,3个#组合45个格子=48,依此类推 # 输出\r光标移至行首 bar() { while : do pound="" for ((i=47;i>=1;i=--)) do pound+=# printf "|%s%${i}s|\r" "$pound" sleep 0.2 done done } bar & cp -r $1 $2 kill $! echo "复制结束" ###---------------脚本5-----------------### #!/bin/bash ## 使用指针 # 防止Ctrl+c组合键无法结束进度条 trap 'kill $!' INT # 定义变量用来存指针 rotate='|/-\' # 定义函数;实现指针进度条 bar() { printf ' ' while : do printf "\b%.1s" "$rotate" rotate=${rotate#?}${rotate%???} sleep 0.2 done } bar & cp -r $1 $2 kill $! echo "复制结束" ###---------------脚本6-----------------### #!/bin/bash ## 百分比的样式 trap 'kill $!' INT # 存储与目标大小,目标大小始终为0 src=$(du -s $1 | cut -f1) dst=0 # 定义函数;实时对比源文件与目标文件的大小,计算复制进度 bar() { while : do size=$(echo "scale=2;$dst/$src*100" | bc) echo -en "\r|$size%|" [ -f $2 ] && dst=$(du -s $2 | cut -f1) [ -d $2 ] && dst=$(du -s $2/$1 | cut -f1) sleep 0.3 done } bar $1 $2& cp -r $1 $2 kill $! echo "复制结束" ## 注意只支持一级目录
#!/bin/bash cat /etc/redhat-release | xargs # 将数据传递给其他程序,不指定程序时xargs默认传递给echo cat /etc/redhat-release | xargs echo cut -d: -f1 /etc/passwd | xargs find /etc -name *.conf -type f | xargs find /etc -name *.conf -type f | xargs grep "python" # 过滤查找文件 find /etc -name *.conf -type f | xargs ls -l # 查找文件属性 echo /mnt/nginx/* | xargs rm -rf # 删除文件或目录 cat /var/run/crond.pid | xargs kill # 成功杀死crond进程 find ./ -name "*.txt" -print0 | xargs -0 rm # 指定结束符为NULL xargs -a /etc/redhat-release echo # 读取参数传递给其他程序 seq 5 | xargs # 默认一次调用全部参数 seq 5 | xargs -n 2 # 设置一次调用2个参数 echo "abcdefghijklnuvwxyz" | xargs -da # 指定分隔符,默认tab空格,换行符 ls * .txt | xargs -I{} cp {} /tmp/ # 复制所有txt文件到/tmp,大i指定替换 字符 ls * .txt | xargs -I[] cp [] /tmp/ # 设置[]为替换字符串
#!/bin/bash # shift命令,左移位置参数 echo "arg1=$1,arg2=$2,ag3=$3,ag4=$4,ag5=$5,ag6=$6,count=$#" # 正常显示 shift # 左移一位,shift没有参数默认移动一位 echo "arg1=$1,arg2=$2,ag3=$3,ag4=$4,ag5=$5,ag6=$6,count=$#" # 向左移一位 shift 2 # 在移动第一位之后再左移二位 echo "arg1=$1,arg2=$2,ag3=$3,ag4=$4,ag5=$5,ag6=$6,count=$#" # 向左移两位 ~]# bash shift.sh a b c 1 2 3 ###---------使用shift判断文件是否存在------### #!/bin/bash ## 测试文件为空则删除文件 if [ $# -eq 0 ];then echo "用法:$0 文件名...." exit 1 fi # 测试位置变量个数,个数为0时循环结束 while (($#)) do if [ ! -s $1 ];then echo -e "\033[31m$1为空文件,正在删除该文.\033[0m" rm -rf $1 else [ -f $1 ] && echo -e "\033[32m$1为非空文件.\033[0m" [ -d $1 ] && echo -e "\033[32m$1为目录,不是文件名.\033[0m" fi shift done ###-------------对文件进行切割------------### #!/bin/bash dd if=/dev/urandom of=/root/test.txt bs=1M count=500 # 生成随机500MB的文件 split -b 20M test.txt test_ # 把文件分20M切割 split -l 1000 test.txt test_ # 按行数切割 split -b 4 test.txt multi_ # 按四个字节切割,但不会换行
df / | tail -n +2 # 去掉标题从第二行打印 # tr的使用 #!/bin/bash echo "hello then world" | tr 'h' 'H' # 小写h转换成大写 echo "hello then world" | tr 'a-z' 'A-Z' # 小写h转换成大写 echo "HELLO THEN WORLD" | tr 'A-Z' 'a-z' # 大写h转换成小写 echo "hello then world" | tr h 9 # 将h替换为9 echo "hello then world" | tr h x # 将h替换为x tr 'a-z' 'A-Z' < /etc/passwd # 从标准输入读取数据 echo "aaaa" | tr -s a # 删除重复的a echo "aaabbb" | tr -s ab # 删除重复的a和b,非连续的字符不会删除 echo "hello then world" | tr -d 'h' # 删除字母h echo "hello then world" | tr -d 'ho' # 删除字母h和o echo "hello then world" | tr -d 'a-e' # 删除包含a-e的所有字母 echo "hello then world" | tr -cd 'o' # 对字母o取反,也就是只取o echo "hello 99987" | tr -cd a-z # 仅保留小写字母 echo "hello 666" | tr -cd 0-9 # 仅保留数字 echo "hello 666" | tr -cd '0-9\n' # 仅保留数字和换行符 echo "hello 666" | tr -c '0-9' x # 将数字外的符号替换为x echo "hello789" | tr -c 'a-z' x # 将小写外的符号替换为x # 换行的使用 #!/bin/bash echo -e "abc\r12" # 输出abc后光标移至行首,字符ab被替换成12,c没有影响 echo -e "abcdef \r12 " # 输出abcdef后光标移至行首,字符ab被替换成12后面四个空格替换 cdef,所以输出12 i=5 printf "|%s%${i}s|" abc # |abc |abc后面有5个空格 # printf打印指定字符 #!/bin/bash printf "%10s" xyz # 指定宽度10,宽度不够自动补空格 printf "%3s" xyzabc # 指定宽度3,实际宽度大于3 printf "%.2s" xyzabc # 指定实际字符中的两个字符 printf x;printf "\b%s" y # 显示字符x,删除x后再显示字符y printf x;printf "\b%.1s" yz # 显示字符x,删除x后仅显示字符y # 使用替换改变位置 #!/bin/bash rotate='|/-\' echo ${rotate#?} # 对变量掐头,删除第一个字符 echo ${rotate%???} # 对变量去尾 rotate2=${rotate#?}${rotate%???} # 将第一个字符移动到最后 echo ${rotate2} ## 每次复制1mb,生成500次,生成一个500mb的数据 dd if=/dev/urandom of=/root/test.txt bs=1M count=500