shell脚本案例以及问题

常用

引用变量的时候,最好加上双引号

cmd=`hostname`
AAA=$(echo "$cmd" | grep )  ##最好加上双引号

json 处理

获取json数据:
英文字符串的时候:cat map.json | python3 -c “import sys, json; print(json.load(sys.stdin)[‘data’][‘ip’])”

cat map.json | /usr/bin/python2.7 -c “import json; import sys; obj=json.load(sys.stdin); print obj[‘province’][1][‘name’].encode(‘utf-8’)”

$ json_data='{"aaa": "111", "bbb": "222"}'

$ value=`echo $json_data | sed s/[[:space:]]//g`
$ echo $value
{"aaa":"111","bbb":"222"}

$ echo "${value//\"/}" ##去除双引号
{aaa:111,bbb:222}

$ echo "${value//\"/}" | sed "s#.*"bbb":\([^,}]*\).*#\1#"
222

处理json,涉及中文的,可能会存在乱码

$ cat sample.json
{
    "4.大学课程": [
        "English",
        "Math",
        "Chinese"
    ],
    "5.高中课程": [
        "Phy",
        "Che",
        "Geo"
    ]
}
有时候会出现中文乱码的问题。为了解决这个问题,我们需要指定ensure_ascii参数为False,并使用json.dumps()函数将Python对象重新编码为JSON字符串。ensure_ascii=False参数保证中文字符不被转义
处理:
json=`cat sample.json | python -c "import json; import sys; obj=json.load(sys.stdin); obj_str=json.dumps(obj, ensure_ascii=False);print(obj_str)"`
echo $json  ##结果如下
{"4.大学课程": ["English", "Math", "Chinese"], "5.高中课程": ["Phy", "Che", "Geo"]}
$ json_1=$(echo $json| sed 's/[{},]//g')
$ echo $json_1  ###去除{}和,
"4.大学课程": ["English" "Math" "Chinese"] "5.高中课程": ["Phy" "Che" "Geo"]
$ echo "${json_1//\"/}"  ##去除双引号
4.大学课程: [English Math Chinese] 5.高中课程: [Phy Che Geo]
$ echo "${json_1//\"/}" | sed "s#.*"4.大学课程":\([^.}]*\).*#\1#"                [English Math Chinese] 5
$ echo "${json_1//\"/}" | sed "s#.*"5.高中课程":\([^.}]*\).*#\1#"
 [Phy Che Geo]

$ echo "${json_1//\"/}" | xargs -n4
4.大学课程: [English Math Chinese]
5.高中课程: [Phy Che Geo]

$ echo "${json_1//\"/}" | xargs -n4 | awk -F: '{print $1}'
4.大学课程
5.高中课程

在这里插入代码片

整数比较

序号 参数 说明
1 -eq 等于
2 -ne 不等于
3 -gt 大于
4 -ge 大于等于
5 -lt 小于
6 -le 小于等于

文件表达式

1 -e filename 如果 filename存在,则为真
2 -d filename 如果 filename为目录,则为真
3 -f filename 如果 filename为常规文件,则为真
4 -L filename 如果 filename为符号链接,则为真
5 -r filename 如果 filename可读,则为真
6 -w filename 如果 filename可写,则为真
7 -x filename 如果 filename可执行,则为真
8 -s filename 如果文件长度不为0,则为真
9 -h filename 如果文件是软链接,则为真
10 filename1 -nt filename2 如果 filename1比 filename2新,则为真
11 filename1 -ot filename2 如果 filename1比 filename2旧,则为真

if [ ! -d "/path/temp" ]; then
    # 如果目录不存在,则创建目录
    mkdir -p /path/temp
else
    echo "目录已存在"
fi

特殊变量

序号 参数 说明
1 $0 当前脚本的文件名
2 $n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是1
3 $# 传递给脚本或函数的参数个数
4 $* 传递给脚本或函数的所有参数
5 $@ 传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同
6 $? 上个命令的退出状态,或函数的返回值
7 $$ 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID
8 $! Shell最后运行的后台Process的PID(后台运行的最后一个进程的进程ID号)

字符串比较

1 $a = $b 如果string1等于string2,则为真
2 $string1 != $string2 如果string1不等于string2,则为真
3 -n $string 如果string 非空(非0),返回0(true)
4 -z $string 如果string 为空,则为真
5 $string 如果string 非空,返回0 (和-n类似)
6 !表达式 条件表达式的相反[逻辑非]
7 表达式1 –a 表达式2 条件表达式的并列[逻辑与]
8 表达式1 –o 表达式2 条件表达式的或[逻辑或]
[ -z “$pid” ] 单对中括号变量必须要加双引号
[[ -z $pid ]] 双对括号,变量不用加双引号

字符串或者变量处理

1. ${变量#匹配规则}  		#从头开始匹配, 最短删除(从开头删除,一直删除到第一次匹配的)
2. ${变量##匹配规则} 		#从头开始匹配,最长删除(从开头删除,一直删除到最后一次匹配的)
3. ${变量%匹配规则} 			#从尾开始匹配,最短删除
4. ${变量%%匹配规则} 		#从尾开始匹配,最长删除
5.${变量/旧字符串/新字符串} 	#替换变量内的旧字符串为新字符串,只替换第一个
5. ${变量//旧字符串/新字符串}	#替换变量内的旧字符串为新字符串,全部替换
6. ${var}  ##返回变量var内容
7. ${#var}  ##返回变量var内容得长度
8. ${var:offset} ##AAA="xueXi";$ echo ${AAA:2}  ##eXi 返回变量var截取,从变量var 下标为offset=2得开始输出;默认是0,打印所有内容
9. ${var:offset:length} ##从变量var中得偏移量offset开始截取长度为length得字符串
10. ${var#*.} ##从变量var中删除第一个匹配得点. 及其左边的所有字符串
11. ${var##*.} ##从变量var中删除最后一个匹配得点. 及其左边的所有字符串
12. ${var%*.} ##从变量var中删除最后一个匹配的点. 及其右边的所有字符串
13. ${var%%*.} ##从变量var中删除第一个匹配的点. 及其右边的所有字符串
14. ${var,} ##首字母小写
15. ${var,,} ##全部转小写
16. ${var^} ##首字母转大写
17. ${var^^} ##全部转大写




var1="I love you, Do you love me?"
echo $var1 # I love you, Do you love me?
#从头开始匹配, 最短删除
echo ${var1#*ov} # e you, Do you love me?  
  • 变量分割:
    按字符分割:${parameter//pattern/string} 。用string来替换parameter变量中所有匹配的pattern
$ string="hello,shell,split,test"
$ echo ${string//,/ }
hello shell split test
  • 变量截取:
    使用#号可以截取指定字符(或者子字符串)右边的所有字符,具体格式如下:${string#*chars}
url="http://www.baidu.com/test"
echo ${url#*/}

结果/www.baidu.com/test

如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用##,格式为:${string##*chars}

url="http://www.baidu.com/test"
echo ${url##*/}
结果:test

使用 % 截取左边字符
${string%chars*}
${string%chars*}

#!/bin/bash
url="http://www.baidu.com/index.html"
echo ${url%/*}  #结果为 http://www.baidu.com
echo ${url%%/*}  #结果为 http:
str="---bb+++bb@@@"
echo ${str%bb*}  #结果为 ---bb+++
echo ${str%%bb*}  #结果为 ---

${string: start :length} 从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
${string: start} 从 string 字符串的左边第 start 个字符开始截取,直到最后。
${string: 0-start :length} 从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${string: 0-start} 从 string 字符串的右边第 start 个字符开始截取,直到最后。
${string#*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string##*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string%*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。
${string%%*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。

  • 拼接:
str01="1""2"
s3=${s1}${s2}
s4=${s1}"2"
s5=`date`${s1}
s6="${a}${b}"
  • 大小写转化:
$ echo ${test^}  # 把变量中的第一个字符换成大写
$ echo ${test^^} # 把变量中的所有小写字母,全部替换为大写
$ echo ${test,}  # 把变量中的第一个字符换成小写
$ echo ${test,,} # 把变量中的所有大写字母,全部替换为小写
$ echo ${#test}  # 获取字符串长度

  • 替换:
${var-string} # 若变量var未赋值,则用string来替换此处的值,最常用。
${var:-string} # 若变量var未赋值或者为空,则用string来替换此处的值,最常用。
${var:=string} # 若变量var为空,则用string来替换此处的值,把string赋给变量var。
${var+string} # 若变量var不为空(包括为空串”“),则用string来替换此处的值。
${var:+string} # 若变量var有值不为空,则用string来替换此处的值。

# 若变量var不为空,则用string来替换此处的值;
# 若变量var为空,则把string输出到标准错误中,并从脚本中退出。
# 常用利用此特性来检查是否设置了变量的值。
${var:?string}

${var/old/new} # 只替换第1个子串
${var//old/new} # 替换全部子串

条件判断:
if [ ! -n “KaTeX parse error: Expected 'EOF', got '#' at position 16: word" ] ;then #̲#判断变量是否为空 if [ …dmin” = “” ] ;then ##判断变量是否为空
if [ “$sum_value” -gt 0 ];then

  • 变量是否含某个字符串:
    方法一:
strA="long string"
strB="string"
result=$(echo $strA | grep "${strB}")
if [[ "$result" != "" ]]
then
    echo "包含"
else
    echo "不包含"
fi

方法二: 字符串比较符号

strA="helloworld"
strB="low"
if [[ $strA =~ $strB ]]
then
    echo "包含"
else
    echo "不包含"
fi

函数

调用函数:

  • 直接写函数名: func
  • 直接写函数名: func 1 2 带参数的
  • sum_value=$(sum 1 2) 要有接收名字

通过function表明定义函数

function sum()
{
    let y=$1+$2
    echo $y
    return $?
}
sum_value=$(sum 1 2)
echo "sum value is :"$sum_value""

采用$()方式调用函数,内部echo命令不会输出到标准输出;
采用$()方式调用,必须由变量接收,否则报错;
还有一种调用方式: sum 1 2
当参数中含有 空格时,要用 引号引用起来.

传变量:
var=name
name=ren
func “$var” #直接给func函数传递变量name字符串
func ${!var} ##间接给函数func 传递ren这个字符串

[] 和[[]]的区别

[ ]是符合POSIX标准的测试语句,兼容性更强,几乎可以运行在所有的Shell解释器中
[[ ]]仅可运行在特定的几个Shell解释器中(如Bash等)
多数情况下[ ]和[[]]是可以通用的。
[ ]里字符串比较运算符用和!=,整数比较只能使用-eq,-gt这种形式。
[ ]中使用-a和-o表示逻辑与和逻辑或,[[ ]]使用&&和||来表示
[[ ]]不支持-a
[ -z “$pid” ] 单对中括号变量必须要加双引号
[[ -z $pid ]] 双对括号,变量不用加双引号
在[ ]中
是字符匹配,在[[ ]]中是模式匹配
[ ]不支持正则匹配,[[ ]]支持用=~进行正则匹配
[ ]中如果变量没有定义,那么需用双引号引起来,[[ ]]中不需要

while 循环

time sh ping_scaleout_server.sh ##time + 命令,可以查看运行多久

info ssh
     -n      Redirects stdin from /dev/null (actually, prevents reading from
             stdin).  This must be used when ssh is run in the background.  A
             common trick is to use this to run X11 programs on a remote
             machine.  For example, ssh -n shadows.cs.hut.fi emacs & will
             start an emacs on shadows.cs.hut.fi, and the X11 connection will
             be automatically forwarded over an encrypted channel.  The ssh
             program will be put in the background.  (This does not work if
             ssh needs to ask for a password or passphrase; see also the -f
             option.)

[root@k8s-master ansible]# cat csren.sh
#! /bin/bash
while read line
do
    echo $line
    ssh -q compute-1 'date'
done << EOF
11
22
32
42
52
EOF
[root@k8s-master ansible]# cat csren.sh
#! /bin/bash
while read line
do
    echo $line
    ssh -q compute-1 'date'
done < 1.txt

[root@k8s-master ansible]# cat csren.sh
#! /bin/bash
while read line
do
    echo $line
    ssh -q compute-1 'date'
done < $1或者$i

[root@k8s-master ansible]# cat csren11.sh
#! /bin/bash
cat $hostfile | while read line
do
    echo $line ":"
done
##执行脚本,输出结果只是打印了第一行11,没有打印所有的循环,也就是只跑了一遍
[root@k8s-master ansible]# bash csren.sh 
11
Mon May  9 01:33:46 JST 2022

分析原因:
当ssh和while一起使用时,ssh在执行时会进行读取所有的输入(也就是第一次就把11-52全部读取),导致在第二次执行时没有内容可读,因此会退出while循环。while中使用了重定向机制,input中的内容都已经读入并重定向给了整个while语句。
重定向的while里要避免使用接收标准输入的命令。
解决办法:
改为for循环或者使用下面的方法:
ssh -n command等同于 < /dev/null
1. SSH 加-n 选项:
[root@k8s-master ansible]# cat csren.sh 
#! /bin/bash

while read line
do
    echo $line
    ssh -q -n compute-1 'date'
done << EOF
11
22
32
42
52
EOF
[root@k8s-master ansible]# bash csren.sh 
11
Mon May  9 01:39:27 JST 2022
22
Mon May  9 01:39:27 JST 2022
32
Mon May  9 01:39:27 JST 2022
42
Mon May  9 01:39:27 JST 2022
52
Mon May  9 01:39:28 JST 2022

2. 使用</dev/null
用/dev/null来当ssh的输入,阻止ssh读取本地的标准输入内容,也就是说用第一次全部读取的内容当输入,不再第二次读取
[root@k8s-master ansible]# cat csren.sh 
#! /bin/bash
while read line
do
    echo $line
    ssh -q  compute-1 'date' </dev/null
done << EOF
11
22
32
42
52
EOF
##使用time + 执行命令,可以看命令或者脚本执行时间
[root@k8s-master ansible]# time bash csren.sh 
11
Mon May  9 01:46:33 JST 2022
22
Mon May  9 01:46:33 JST 2022
32
Mon May  9 01:46:33 JST 2022
42
Mon May  9 01:46:33 JST 2022
52
Mon May  9 01:46:34 JST 2022

real	0m1.179s
user	0m0.046s
sys	0m0.012s

要想分析原因可以通过:
truss, strace, ltrace工具
truss -o fenxi.log bash csren.sh
strace -f -o fenxi.log bash csren.sh
-f: 除了跟踪当前进程外,还跟踪子进程
-o: 将输出信息写到文件file中
ltrace -p 234: 跟踪一个pid为234的已经在运行的进程

while 读取文件以及变量

  1. 当只有一个变量的时候,传入
[root@k8s-master bash]# cat test.txt 
12 34 qazwsx
34 56 esdacrc
wesd ds wsaa

[root@k8s-master bash]# cat while.sh 
#! /bin/bash
cat test.txt | while read line1;  #line1 依次取的是test.txt 文件中的每行内容,,当只有一个变量的时候
do
	echo $line1
done
##文件有三行,循环三次,所以打印了三遍
[root@k8s-master bash]# bash while.sh 
12 34 qazwsx
34 56 esdacrc
wesd ds wsaa
  1. 当传入两个变量的时候,读取
#! /bin/bash
cat test.txt | while read line1 line2;  #
do
        echo "the first parameter: $line1"
        echo "the second parameter: $line2"
done

当需要设定分割符时候
```bash
IFS=";"
echo "aa;bb"|while read a b;do
  echo $a,$b
done
#按设定的分号做分割,打印出
aa,bb

三个变量: 如果只要其中两个的时候,就写相应的顺序

IFS=";"
echo "aa;bb;cc"|while read a b c;do
  echo $b,$c
done

#按设定的分号做分割,打印出
bb,cc

while IFS='-' read -r Server_type Shelf_id Blade_id;do echo -n "$Shelf_id-$Blade_id " ;done <1111.txt

[root@k8s-master bash]# bash while.sh
##第一次循环的时候,因为只传两个参数,默认以空格分割,
##12 34 qazwsx要分成两部分,第一部分是“12”;第二部分是“34 qazwsx”
the first parameter: 12
the second parameter: 34 qazwsx
##第二次循环
the first parameter: 34
the second parameter: 56 esdacrc
##第三次循环
the first parameter: wesd
the second parameter: ds wsaa

###while read line;do echo $line;done < file这种效率最高,如果涉及到ssh连接server的时候,记得加-n或者在ssh那行最后加< /dev/null

3、 第三种
exec < filename
while read -r line
do
处理逻辑
done


while 读取文档的时候,无法读取到最后一行
现象: txt文档中有中文字符,在用while read line; do echo $line;done,无法打印中文下一行的内容
解决方案:
```bash
#!bin/bash
while read line || [[ -n ${line} ]]
do
    data=`...line...`
    echo ${data}
done < $1

if 函数

if [ 条件1 -o 条件2 ] 或者 if [[ 条件1 || 条件2 ]] ;then
判断${line}中是否包含:dmccs

if [[ "${line}" =~ ":dmccs" ]];then
  tmp=${line#git@gitee.com:dmccs/};
 fi

for 循环

在读取文件时,for i in cat xx.txt 或者 for i in $(<file.txt);do done 效率最高

使用for循环从文件中读取内容时,默认情况下是以空格作为分隔符来读取文件内容;

因为for循环以环境变量IFS的值作为分隔符,而IFS的默认值是“<space/空格>”“<tab/制表符>”“<newline/新行>”
若IFS字段值未设置,则默认以空格作为分隔符来读取文件内容;
若IFS字段值设为“\n”,则以行作为分隔符来读取文件内容;
若IFS字段值设为“\t”,则以制表符作为分隔符来读取文件内容。

#!/bin/bash
IFS=$'\n'
for line in `cat filename`
do
        echo $line
done

[root@k8s-master bash]# cat test.txt 
12 34 qazwsx
34 56 esdacrc
wesd ds wsaa
##for 循环,默认以空格读取文件中所有内容,挨个打印出来
[root@k8s-master bash]# cat for.sh 
#! /bin/bash
for i in `cat test.txt`
do
	echo "the first parameter: $i"
done
[root@k8s-master bash]# 
[root@k8s-master bash]# bash for.sh 
the first parameter: 12
the first parameter: 34
the first parameter: qazwsx
the first parameter: 34
the first parameter: 56
the first parameter: esdacrc
the first parameter: wesd
the first parameter: ds
the first parameter: wsaa

[root@k8s-master bash]# cat for.sh 
#! /bin/bash
for i in $(<test.txt)
do
	echo "the first parameter: $i"
done
[root@k8s-master bash]# bash for.sh 
the first parameter: 12
the first parameter: 34
the first parameter: qazwsx
the first parameter: 34
the first parameter: 56
the first parameter: esdacrc
the first parameter: wesd
the first parameter: ds
the first parameter: wsaa

调试

[root@k8s-master bash]# cat for.sh 
#! /bin/bash
set -x
for i in $(<test.txt)
do
	echo "the first parameter: $i"
done
[root@k8s-master bash]# bash for.sh 
+ for i in '$(<test.txt)'
+ echo 'the first parameter: 12'
the first parameter: 12
+ for i in '$(<test.txt)'
+ echo 'the first parameter: 34'
the first parameter: 34
+ for i in '$(<test.txt)'

#### 在脚本中添加set -x,启用调式模式
#### PS4='$LINENO: ' 运行时会打印脚本运行内容行号
[root@k8s-master bash]# bash for.sh 
4: for i in '$(<test.txt)'
6: echo 'the first parameter: 12'
the first parameter: 12
4: for i in '$(<test.txt)'
6: echo 'the first parameter: 34'



shell 参数

[[ ${1:-“”} != “” ]] ====[ -n “$1” ]
${1:-}的意思就是说,如果函数有第一个参数,就返回这个参数,如果没有,就返回空。
a = ${1:-“false”}
$1 :代表传入的第一个参数。
如果$1存在并且不为空那么a=$1,否则就是a=false;
echo $@ 输出所有参数
echo @ : 2 从第二参数开始打印 a = {@:2} 从第二参数开始打印 a= @:2从第二参数开始打印a={@:3:1} 从第3个开始,输出1个
echo “脚本名称: $0”
echo “参数个数: KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲" echo "输入的参数:*”
echo “进程id: $$” Shell本身的PID(ProcessID)
command1 && command2 &&代表 它前面的命令执行成功,它后面的命令才可以执行
command1 || command2 || 代表 它前面的命令执行不成功,它后面的命令才可以执行;

#!/bin/bash
echo $0
echo $@
echo ${@:2}
echo ${@:3:1}
[root@k8s-master bash]# bash 1.sh 11 22 33 44
1.sh
11 22 33 44
22 33 44
33

当shell中加入shift
shift(shift 1) 命令每执行一次,变量的个数($#)减1(之前的$1变量被销毁,之后的$2就变成了$1),而变量值提前一位。
同理,shift n后,前n位参数都会被销毁

# -f 文件存在
if[ -f /etc/sysconfig/network ]; then
# -z 字串为空/ -o or / -a and
if[ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ];
log_file=`find ./ -type f -name "*.log" -exec ls {} \;`
echo $log_file        无换行符   打印出的是一行字符串
echo "$log_file"      有换行符,和直接在linux输出结果格式一样
script_dir=$( cd "$( dirname "$0"  )" && pwd )
echo "script_dir: ${script_dir}"
script_name=$(basename ${0})
echo "file name: $script_name"

printf 可以格式化输出
[root@mytest ~]# printf 'hello\n'
hello
[root@mytest ~]# printf "ID: %-5s  NAME: %-20s  Avage: %4.2f\n" 1  tom 74.23333;printf "ID: %-5s  NAME: %-20s  Avage: %4.2f\n" 5  jack 81.66666;printf "ID: %-5s  NAME: %-20s  Avage: %4.2f\n" 3  stve 78.50000
ID: 1      NAME: tom                   Avage: 74.23
ID: 5      NAME: jack                  Avage: 81.67
ID: 3      NAME: stve                  Avage: 78.50

echo "my name is $name666" #拼接失败,要将变量和字符串用大括号分开
echo "my name is ${name}666" #大括号拼接成功
echo "my name is 666$name" #拼接成功

多线程

后台执行:在Shell脚本中,你可以使用&符号将某个命令放在后台执行,这样可以同时执行多个命令,达到多线程的效果
#!/bin/bash
command1 &
command2 &
command3 &
wait # 等待所有后台任务完成
echo “所有任务执行完成”

1、while实现

#! /bin/bash
startTime=`date +%Y%m%d-%H:%M:%S`
startTime_s=`date +%s`

while read Hostname ip; 
do
{
        ping=`ping -c 3 $ip |grep -w "0% packet loss"`
		ret=$?
        if [ $ret -eq 0 ] ; then 
                echo "$Hostname passed" > validation_summary.txt
        else 
                echo "$Hostname failed" >> validation_summary.txt
        fi
}&
done < hostname_ip_info
wait  ##上面跑完之后才会继续往下面跑

endTime=`date +%Y%m%d-%H:%M:%S`
endTime_s=`date +%s`
 
sumTime=$[ $endTime_s - $startTime_s ]
 
echo "$startTime ---> $endTime" "Total:$sumTime seconds"

######
#cat hostname_ip_info
#compute-2  192.168.0.1
#compute-3  192.168.0.2
#compute-4  192.168.0.3
#compute-5  192.168.0.4

2、函数实现
使用xargs命令:xargs命令可以从标准输入中读取数据,并将其作为参数传递给指定的命令。通过指定-P参数可以实现并行执行多个命令

#! /bin/bash
startTime=`date +%Y%m%d-%H:%M:%S`
startTime_s=`date +%s`

process_data() {
    echo "Processing data: $1"
    echo "$1" | while read Hostname ip; 
    do
    {
        ping=`ping -c 3 $ip |grep -w "0% packet loss"`
        ret=$?
        if [ $ret -eq 0 ] ; then 
            echo "$Hostname passed" > validation_summary.txt
        else 
            echo "$Hostname failed" >> validation_summary.txt
        fi
    }
    done
    }

export -f process_data

cat hostname_ip_info | xargs -I {} -P 50 bash -c 'process_data "$@"' _ {}

endTime=`date +%Y%m%d-%H:%M:%S`
endTime_s=`date +%s`
 
sumTime=$[ $endTime_s - $startTime_s ]
 
echo "$startTime ---> $endTime" "Total:$sumTime seconds"

3、for实现

#!/bin/bash

startTime=`date +%Y%m%d-%H:%M:%S`
startTime_s=`date +%s`
for i in {120..133};do
#注意,我的括号是放在for循环里面的,一定要放在循环里面,放外面无效!
{
        ip="192.168.198.$i"
        ping=`ping -c 2 $ip |grep -w "0% packet loss"` 
        #echo $ping
        if [ $? -eq 0 ] ; then
                echo 192.168.198.$i is up 
                echo 192.168.198.$i is up >> up.log
        else
                echo 192.168.198.$i not up 
                echo 192.168.198.$i not up >> notup.log
        fi
#中括号结束位置也是在for循环里面
}&
done
wait
echo "END"
endTime=`date +%Y%m%d-%H:%M:%S`
endTime_s=`date +%s`
 
sumTime=$[ $endTime_s - $startTime_s ]
 
echo "$startTime ---> $endTime" "Total:$sumTime seconds"

#wait命令的意思是,等待(wait命令)上面的命令(放入后台的)都执行完毕了再往下执行。
在这里写wait是因为,一条命令一旦被放入后台后,这条任务就交给了操作系统shell脚本会继续往下运行(也就是说:shell脚本里面一旦碰到&符号就只管把它前面的命令放入后台就算完成任务了,具体执行交给操作系统去做,脚本会继续往下执行),所以要在这个位置加上wait命令,等待操作系统执行完所有后台命令

4、控制并发进程的数量
利用文件描述符号和命名管道实现多进程并发控制,控制并发数量为50
ping 110多个ip不通,大概36s

#!/bin/bash
 
start_time=`date +%s`  #定义脚本运行的开始时间
[ -e /tmp/fd1 ] || mkfifo /tmp/fd1  # 如果fd1文件不存在就新建一个FIFO类型的文件
exec 6<>/tmp/fd1   # 将FD6指向FIFO类型,创建文件描述符,以可读(<)可写(>)的方式关联管道文件,这时候文件描述符3就有了有名管道文件的所有特性
rm -rf /tmp/fd1  #删也可以,留下文件描述符来用就可以了
thread_num=50  # 定义最大线程数

#根据线程总数量设置令牌个数
#事实上就是在fd6中放置了$thread_num个回车符
for ((i=0;i<${thread_num};i++));do
    echo
done >&6
 
exec < hostname_ip_info
while read -r Hostname ip;
do
    # 一个read -u6命令执行一次,就从FD6中减去一个回车符,然后向下执行
    # 当FD6中没有回车符时 ,就停止,从而实现线程数量控制
    read -u6
    {
        ping=`ping -c 3 $ip |grep -w "0% packet loss"`
        ret=$?
        if [ $ret -eq 0 ] ; then 
            echo "$Hostname passed" > validation_summary.txt
        else 
            echo "$Hostname failed" >> validation_summary.txt
        fi
        echo >&6 # 当进程结束以后,再向FD6中加上一个回车符,即补上了read -u6减去的那个
    } &
done
wait # 要有wait,等待所有线程结束
stop_time=`date +%s` # 定义脚本运行的结束时间
echo "TIME:`expr $stop_time - $start_time`" # 输出脚本运行时间

exec 6<&-      #关闭文件描述符的读
exec 6>&-      #关闭文件描述符的写
echo "End Shell" # 表示脚本运行结束
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值