1. 需求已知mobile.txt为:
1 2 haojin 17625160000 voidking 17625160001
需求:根据 mobile.txt 中的内容拼接成SQL,修改不同用户的手机号。例如:
1 update user set mobile= "17625160000" where name= "haojin";
2. 简单实现脚本:
1 2 3 4 5 6 7 8 #!/bin/bash grep -v "^$" mobile.txt | while read line do name=`echo $line | awk '{print $1}' ` mobile=`echo $line | awk '{print $2}' ` echo "update user set mobile=\"${mobile} \" where name=\"${name} \";" done
PS:不能使用for line in cat 'mobile.txt'
,因为这种方法会按照空格或换行切分文本。
3. 更好的实现以上循环读取的方法,对于上面的需求是没有问题的。但是通用性不好,我们再来看另外一个需求。
已知service.txt内容为:
1 2 3 127.0.0.1 80 127.0.0.1 8080 192.168.56.101 8080
需求:探测service.txt中每个服务的连通性,并记录结果。
我们用同样的思路实现脚本:
1 2 3 4 5 6 7 8 9 10 11 #!/bin/bash : > detectresult.txt cat service.txt | while read linedo ip=$(echo $line | awk '{print $1}' ) port=$(echo $line | awk '{print $2}' ) res=$(nc -w 2 -v $ip $port ) echo "$res " >> detectresult.txt done
执行脚本后,我们发现结果文件中只有一条结果!这就不符合预期了。 这是因为while使用重定向机制,while read line
一次性将文件信息读入输入缓存,并按行赋值给变量line,直到输入缓存数据为空。而刚好nc、telnet、ssh等命令,会读取输入缓存中的所有数据,这就导致输入缓存被清空了,while循环结束。
解决办法:
1 res=$(nc -w 2 -v $ip $port < /dev/ null )
此外,因为管道符左右的命令都是在子shell中执行的,所以容易引起变量赋值不会在父shell中生效的问题。
因此,更好的脚本应该改成:
1 2 3 4 5 6 7 8 9 10 #!/bin/bash cat /dev/null > detectresult.txtwhile read linedo ip=$(echo $line | awk '{print $1}' ) port=$(echo $line | awk '{print $2}' ) res=$(nc -w 2 -v $ip $port < /dev/null) echo "$res " >> detectresult.txt done < service.txt
4. while read line进阶使用while read line的循环读取文本的方案,通用性已经很不错,可以应对大多数场景。但是,如果循环读取时,还需要和用户进行交互,那么就不适用了,下面看一个例子。
已知applist.txt内容为:
1 2 3 app1 running hba app2 stop hbe app3 running hna
需求:对applist.txt中的app进行修改,修改每个app前都需要进行确认。
按照while的思路,编写 main.sh 内容为:
1 2 3 4 5 6 7 8 #!/bin/bash while read line;do app_id=$(echo $line | awk '{print $1}' ) idc=$(echo $app_id | awk -F'.' '{print $NF}' ) bash modify.sh ${idc} ${app_id} done < applist.txt
modify.sh内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #!/bin/bash idc=$1 app_id=$2 echo -e "idc: ${idc} " echo -e "app_id: ${app_id} " read -p "确认进行修改?[Y/N]" inputif [[ $input = "y" || $input = "Y" ]];then echo -e "continue..." else echo -e "exit" exit 1 fi
但是问题来了,最终执行效果不符合预期,modify.sh中的确认交互效果会失效!这是因为read会读取缓存中的内容,而不是等待交互。
那么,怎么解决这个问题?非要使用while read line的话,确实没有好的解决办法。 但是,我们可以把while read line替换掉!新的 main.sh 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #!/bin/bash filename="applist.txt" lines=$(cat ${filename} | sed '/^$/d' ) linenum=$(echo "${lines} " | wc -l) index=1 while [[ ${index} -le ${linenum} ]];do line=$(echo "${lines} " | sed -n "${index} p" ) app_id=$(echo $line | awk '{print $1}' ) idc=$(echo $app_id | awk -F'.' '{print $NF}' ) bash modify.sh ${idc} ${app_id} index=$((${index} +1 )) done
或者使用for循环:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/bin/bash filename="applist.txt" lines=$(cat ${filename} | sed '/^$/d' ) linenum=$(echo "${lines} " | wc -l) for index in `seq 1 ${lines} `;do line=$(echo "${lines} " | sed -n "${index} p" ) app_id=$(echo $line | awk '{print $1}' ) idc=$(echo $app_id | awk -F'.' '{print $NF}' ) bash modify.sh ${idc} ${app_id} done