您的位置:首页 > 运维架构 > Shell

shell脚本实例备忘

2013-03-27 04:35 453 查看
将一些练习题以及我自己实际写过的shell脚本记录于此,供学习查询。


用户输入y或Y结束

while方式
#!/bin/bash
read -p "input you choice : " c
while [ "$c" != "Y" -a "$c" != "y" ]
do
read -p "input you choice : " c
done
echo "Ok!"

until方式
#!/bin/bash
until [ "$c" == "Y" -o "$c" == "y" ]
do
read -p "input your choice : " c
done
echo "done!"



case、函数、函数参数使用方法

#!/bin/bash
function print()
{
echo "Your choice is $1"
}
case $1 in
"one")
#echo "one"
print 1
;;
"two")
#echo "two"
print 2
;;
"three")
#echo "othre"
print 3
;;
*)
echo "Usage $0 {one|two|three}"
;;
esac



shell脚本中执行命令的方法

#!/bin/bash
str=$(whoami)    #或者:str=`whoami`(这里的引号为反引号——ESC下面那个键)
echo $str


shell脚本中for的使用方法

循环读取字符串中的每一个子串(以空格隔开)
#!/bin/bash
string="Waiting for a while"
for str in ${string}
do
echo $str
done

循环读取字符串中的每一个子串(以空格隔开)
#!/bin/bash
for str in "Waiting for a while"
do
echo $str
done

循环读取ls命令返回结果的每一个
#!/bin/bash
for str in `ls`
do
echo $str
done

循环次数的例子,for(())是bash支持的
#!/bin/bash
for ((i=0; i<10; i++))    #或者:for i in {1..10}
do
echo $i
done



dirname 命令与脚本语句$(dirname "${0}")

显示指定路径除了最后的文件名或者目录名之外的路径前缀,即截取给定路径的目录部分。例如:dirname /usr/bin/sort 得到/usr/bin。

在shell脚本中经常看到这样的语句:$(dirname "${0}")

说明:${0}表示当前脚本的第一个参数,即程序的完整路径,再执行dirname命令,则取得当前脚本所在的目录。

还有语句:cd $(dirname "$0") || exit 1 

说明:$(dirname "$0")表示当前脚本所在的目录,cd即进入脚本所在目录,失败则exit 1(退出)。

还有常用的获取当前脚本文件所在目录的方式:

[b]THIS_DIR=$(cd "$(dirname $0)"; pwd)[/b]

或者是:

THIS_DIR=$(readlink -f "$(dirname "${0}")")

grep妙用

今天在shell脚本中看到这样一条语句:

NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)"

文件/proc/cpuinfo是cpu的信息,grep -c只统计查询结果的行数但是不输出,正则表达式"^processor"表示以processor开始的行,查看/proc/cpuinfo文件可以看出processor开始的行是cpu的第几个核心的信息。综上可知上面的shell语句就是统计了当前机器的cpu核心数目。

判断字符串是否是数

function isDigit()
{
if [ $# == 1 ];then
if [ `echo $1 | grep -P  "^\d+$" | wc -l` == 1 ];then
return 0;
fi
fi
return 1;
}

isDigit '111'
echo $?
isDigit 'ass'
echo $?

apache httpd自动编译生成的小脚本
脚本功能很简单,先判断当前目录中是否有Makefile文件,有则执行make进行编译生成,否则执行./configure(默认该文件存在,可以加上一条判断语句)生成Makefile文件,再进行判断和执行,过程中用MAKE_RES记录make执行的返回值,最终打印出该值。

#!/bin/bash

MAKE_RES=255	# 记录make的返回值,默认失败

if [ -f Makefile ];then		# 测试Makefile文件是否存在
make				# 执行make
MAKE_RES=$?			# 保存make的返回值
else				# 如果Makefile文件不存在,则执行configure
./configure --with-included-apr --with-pcr
if [ -f Makefile ];then	# 判断是否生成了Makefile文件
make
MAKE_RES=$?
else
echo "./configure failed!"
fi
fi

echo "Make result : "${MAKE_RES}	# 输出make的返回值(0表示成功,其他表示失败)


判断用户输入的字符串是否为“Yes”或“No”(包含各种大小写字母组合)
case "$OPT" in
[Oo][Nn]) CMD='YES';;
[Oo][Ff][Ff]) CMD='NO';;
*)
echo '选项格式错误! 请用on 或off 来切换匿名登录的开关。'
exit 1
;;
esac

for循环一例
#! /bin/bash

DIR="/var"
cd ${DIR}
for f in $(ls ${DIR})
do
[ -d $f ] && du -s $f
done
该脚本用来列出/var目录下各子目录占用磁盘空间的大小。

脚本中用sudo执行命令,怎样自动输入密码,以免脚本执行中断等待用户输入密码
echo "password" | sudo -S netstat -tlnp 
    
The -S (stdin) option causes sudo to read the password from the standard input instead of the terminal device.

 系统/etc/profile 中的一段代码,可以学习借鉴

<span style="font-size:12px;">if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do   #×××
if [ -r $i ]; then
. $i
fi
done
unset i
fi</span>
经常会有这样的需求:获取某个路径下的所有文件或者某些匹配的文件,然后做某种操作。以前的做法是先用find或者ls+grep的方式获取结果,然后再对结果进行循环操作,例如:result=$(find /etc/profile.d/ -name *.sh)  或者 result=$(ls /etc/profile.d/ | grep ".sh$")。

今天开了一下/etc/下的配置文件profile,看到如上的一段代码,其中#×××的语句值得借鉴,根本不需要查找!!!

 一个简单的例子,包含我写脚本的基本框架,以后可以复用
#! /bin/bash
###############################################################################
# author: andy <44452114@qq.com>
# date: 04/24/2014
# descrp: 脚本功能为列出给定目录中所有的文件和目录,通过参数形式给定需要查询
#         的目录。运行方式如下:
#             $ ./list_dir.sh /home/andy
#
###############################################################################

# 判断输入参数是否合法
if [[ $# < 1 ]]; then
echo "[Error] Too few parameters!"
exit 1
fi

# 获得输入的参数
dir=$1
if [ ! -d $dir ]; then
echo "[Error] The path you give($dir) not exist!"
exit 2
fi

# 程序功能实现
list=$(ls $dir)
for item in $list
do
echo "item : $item"
done

# 结束提示
echo "Ok!"


 对目录中所有文件求md5值并输出到文件
sudo find . -type f -print0 | xargs -0 md5sum | grep -v "\./md5sum.txt" > md5sum.txt


 命名临时文件

TMP_file="/tmp/tmp.$$"

$$是Script 的进程编号,利用$$组成临时文件名,这样,可避免不同的Script 开启重复的临时文件。

 一个例子

维护debian源码包时,需要获取源码包需要的编译依赖。要想知道你的软件在编译的时候需要用到哪一个软件包,可以通过下面的方法,在源码根目录运行: 

  strace -f -o /tmp/log ./configure 

部分log文件文件如下:
32171 mmap2(NULL, 126192, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb76e0000
32171 close(3)                          = 0
32171 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
32171 open("/lib/i386-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
32171 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320\n\0\0004\0\0\0"..., 512) = 512
32171 fstat64(3, {st_mode=S_IFREG|0644, st_size=13856, ...}) = 0
32171 mmap2(NULL, 16512, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb76db000
32171 mmap2(0xb76de000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0xb76de000
32171 close(3)                          = 0
32171 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
32171 open("/lib/i386-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
32171 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0F\0\0004\0\0\0"..., 512) = 512
32171 fstat64(3, {st_mode=S_IFREG|0644, st_size=280108, ...}) = 0
在使用下面的脚本分析,提取出依赖的软件包及版本,脚本如下(重要的地方加了注释):
for x in `dpkg -S $(grep open ./log|\
perl -pe 's!.* open\(\"([^\"]*).*!$1!' |\
grep "^/"| sort | uniq|\
grep -v "^\(/tmp\|/dev\|/proc\)" ) 2>/dev/null|\
cut -f1 -d":"| sort | uniq`; \
do
echo -n "$x (>=" `dpkg -s $x|grep ^Version|cut -f2 -d":"` "), ";
done

# 注释说明
# 1行:找到含有open的行;
# 2行:perl -pe 's!!!'或者perl -pe 's///',替换字符串。这里实际上是为了得到$1,例如:
#      上面log的第4行,会提取得到:/lib/i386-linux-gnu/libdl.so.2,注意读一下其中的正则匹配,
#      匹配open后面“”中的内容保存在$1;
# 3行:提取/开头的行;
# 4行:去掉/tmp,/dev,/proc开始的行,即不处理/tmp,/dev,/proc开头的文件。注意这里有grep使用
#      的技巧——查询多个字符串匹配:grep "\(aaa\|bbb\))
# 另外,dpkg -S 查询某个文件属于哪个debian包,dpkg -s 查询debian包的详细信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: