Shell编程注意点 ( by quqi99 )

版权声明:本文为博主原创文章,如需转载,请注明出处! https://blog.csdn.net/quqi99/article/details/6305253

                                   Shell编程注意点 ( by quqi99 )


作者:张华 写于:发表于:2011-04-06
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明

( http://blog.csdn.net/quqi99 )

 

1shell脚本中调用另一个脚本的三种不同方式(fork,exec, source)

       我们先谈谈在shell脚本中调用另一个脚本的三种不同方式的区别(fork,exec, source )

  • fork ( /directory/script.sh ), fork是最普通的,就是直接在脚本里面用/directory/script.sh来调用script.sh这个脚本.运行的时候开一个sub-shell执行调用的脚本,sub-shell执行的时候,parent-shell还在。sub-shell执行完毕后返回parent-shell.sub-shellparent-shell继承环境变量.但是sub-shell中的环境变量不会带回parent-shell

  • exec(exec /directory/script.sh) execfork不同,不需要新开一个sub-shell来执行被调用的脚本被调用的脚本与父脚本在同一个shell内执行。但是使用exec调用一个新脚本以后,父脚本中exec行之后的内容就不会再执行了。这是execsource的区别

  • source(source/directory/script.sh)fork的区别是不新开一个sub-shell来执行被调用的脚本,而是在同一个shell中执行.所以被调用的脚本中声明的变量和环境变量,都可以在主脚本中得到和使用.

可以通过下面这两个脚本来体会三种调用方式的不同:

1.sh 

#!/bin/bash
A=B
echo "PID for 1.sh before exec/source/fork:$$"
exportA
echo "1.sh: /$A is $A"
case $1 in
       exec)
               echo "using exec…"
               exec ./2.sh ;;
       source)
               echo "using source…"
               ../2.sh ;;
        *)
               echo"using fork by default…"
               ./2.sh ;;
esac
echo"PID for 1.sh after exec/source/fork:$$"
echo "1.sh:/$A is $A"

2.sh 

#!/bin/bash
echo"PID for 2.sh: $$"
echo "2.sh get /$A=$A from1.sh"
A=C
export A
echo "2.sh: /$A is $A"


执行情况:
$./1.sh    
PID for 1.sh beforeexec/source/fork:5845364
1.sh: $A is B
using fork bydefault…
PID for 2.sh: 5242940
2.sh get $A=B from 1.sh
2.sh:$A is C
PID for 1.sh after exec/source/fork:5845364
1.sh: $A isB
$ ./1.sh exec
PID for 1.sh beforeexec/source/fork:5562668
1.sh: $A is B
using exec…
PID for2.sh: 5562668
2.sh get $A=B from 1.sh
2.sh: $A is C
$./1.sh source
PID for 1.sh beforeexec/source/fork:5156894
1.sh: $A is B
using source…
PIDfor 2.sh: 5156894
2.sh get $A=B from 1.sh
2.sh: $A is C
PIDfor 1.sh after exec/source/fork:5156894
1.sh: $A is C
$

2函数调用

先看一个例子,执行mysql的函数mysqlExec,如下:

source“mysql.conf”

mysqlExec(){
sql=$1
sqlOp=`echo${sql:0:6}| tr A-Z a-z`
if[ "$sqlOp" != "select" ]; then
  sql=$sql";select row_count();"
fi
#use different mysql command depends on the password

if[ -z $MYSQL_PASSWORD ]
then
  $mysql $MYSQL_DATABASE -h$MYSQL_HOSTNAME -u$MYSQL_USERNAME -se "${sql};"
else
  $mysql $MYSQL_DATABASE -h$MYSQL_HOSTNAME -u$MYSQL_USERNAME -p$MYSQL_PASSWORD-se "${sql};"
fi
status=$?
if[ $status -eq 0 ]; then
  if[ "$sqlOp" != "select" ]; then
    log "OK $sql"
  fi
else
  log "Occur DB Error, can retry in 3 seconds later -> $sql"
  sleep3
  echo "DB_ERROR"
fi
return $status
exit
}
函数调用要注意两点:
1函数中可以用echo,如上面的echo "DB_ERROR",在调用时要获取echo的值,应该这样:
campaign=`mysqlExec"$sql"`
if[ "x$campaign" == "x" -o "$campaign" =="DB_ERROR" ]; then
continue
fi
2)函数中也可以有返回值,如上面的return $status,在调用时应该通过$?获得,如:
if[ $? -eq 0 ]; then
echo“zhanghua”

fi

3) 如果想从被调用的函数处返回一个值,可以这样

   调用gen_conf函数,传一个引用(注意不是变量)config_file进去, gen_conf $host config_file

   在gen_conf函数中通过__resultvar变量返回值:

   gen_config{

         local  __resultvar=$2
        eval $__resultvar="'$config'"

   }

3shell中的要用局部变量很纠结

你会看到shell有一个非常大的缺点,就是它在函数调用时,没有局部变量与全局变量之分,如A脚本调用B脚本中的一个函数,在B脚本内部有一个变量vari(你可能受JAVA影响认为它是局部变量那就大错特错了),如果A脚本中也有这个名为vari的变量,那么在函数返回时,B脚本的那个vari变量会将A脚本的vari变量覆盖,举个例子:
updateWithOptimisticLock(){
rand=$1
campaignId=$2
seq=1
updateVal=-1
status=1
while[ true ]; do
if[ $seq -gt 3 ]; then
log"FATAL ERROR, Update num error, $updatesql"
break
fi
cur=`queryCur"$campaignId"`
if[ "$cur" == "DB_ERROR" ]; then
continue
fi
updateVal=$(($cur+$rand))
if[ "$rand" == "0" ]; then
break
fi
updatesql="updatet_campaign_ set num=$updateVal where campaign_id=$campaignId andnum=$cur"
affectRows=`mysqlExec"$updatesql"`
if[ "$affectRows" == "1" ]; then
status=0
break
fi
seq=$(($seq+1))
done
echo$updateVal
return$status
}
调用的伪码如下,这时里面的seq变量会被上述updateWithOptimisticLock函数中的变量seq给覆盖,所以在shell中没有局部变量一说
seq=1
while[ $seq -le $cycleNum ]; do
updateFakeNumWithOptimisticLock$rand $campaignId
done
4使用xargs来传参数
shell中的管道符|很强大,可以将前一条命令的标准输出作为下一条命令的标准输入,但是如果下一条命令不是标准输入而是需要传参的话,那怎么办呢,用xargs即可,例如下列shellxargs命令的-i选项告诉xargs用前一条命令的标准输出去替换{}

find. | xargs zgrep "<URL>/Search?" | sed's/.*q=/([-_*()~.%+0-9A-Za-z]*/).*//1/' | sort -nr | uniq -c | sort-nr | head -1000 | xargs -i php -r "echorawurldecode('{}')./"/n/";" > result.out &


另, 修改/etc/hosts, 能处理127或localhost打头的多个hostname项如(ubuntu.me.com  ubuntu), 用‘/usr/bin/awk '$1 ~ /^127|localhost/ {print $0}' /etc/hosts’是为了避免IPv6 中的node如ff02::1 ip6-allnodes

/usr/bin/awk '$1 ~ /^127|localhost/ {print $0}' /etc/hosts |awk '$1 ~ /^127|localhost/ {print $0}' | /bin/sed "s/\s*\(${CURRENT_HOSTNAME}\)\(\s*\)/\t${NETCFG_HOSTNAME}/g"


当然exec也可以实现上述功能,只是exec都是一次性读入内存容易内存溢出,如:
find. -name "*.m4" -exec grep --color -H "catalina"{} /;

5shell中的sed命令使用的正则是缩水版

shell中的sed命令使用的正则引擎和我们java中平常所用正则引擎并不一样,功能比较弱。

如上节中的|sed 's/.*q=/([-_*()~.%+0-9A-Za-z]*/),就是因为shell的很多正则不支持,才在使用sed命令时用了那么多枚举。

nova service-list --bi nova-compute | grep nova-compute | cut -d ' ' -f 4 | xargs -n 1 -I {} ssh -o StrictHostKeyChecking=no  ubuntu@{} "date; hostname; zgrep MessagingTimeout /var/log/nova/nova-compute.log*; echo -e '-----------------------------\n'"








 

没有更多推荐了,返回首页