首页 > 代码库 > 九、shell编程
九、shell编程
变量初始化:在变量声明的时候给变量一个初始值,初始化相当于给里面放东西。
变量赋值:手动给变量空间中存储数据的过程。
变量类型转换:显式、隐式,比如讲字符型转换成数值型。
变量类型:
本地变量:
set var_name=value
unset var_name
${var_name}
作用范围:当前shell进程。
局部变量:
local var_name=value
unset var_name
${var_name}
作用范围:当前shell进程的局部范围内有效
环境变量:
export var_name=value
unse var_name
${var_name}
作用范围:当前shell及其子shell。
位置变量:$1/$2................
特殊变量:$$/$?/$#/$@...................
脚本的写法格式:
指定脚本运行的shell环境:
独立执行脚本的条件:
1、要有执行权限。
2、定义好shebang,及脚本的第一行:#! /path/to/explainer 例如:/bin/bash ,/usr/bin/python。
3、定义命令的path环境变量。
bash的配置文件:
profile:声明环境变量,执行程序脚本 ,/etc/profile,/etc/profile.d/*.sh,~/bash_profile,交互式登陆
bashrc:定义本地变量、定义命令别名/etc/bashrc,~/.bashrc,非交互式
脚本文件中,除第一行外其他所有已#开头的行均为注释行。
练习一、添加一组用户,并且用户名同密码一样。
#!/bin/bash
#author:gongbing
#time :20150731
#version:1.0
#description:add user
useradd user1
echo user1 |passwd --stdin user1&>/dev/null
echo "add user1 successful!"
useradd user2
echo user2 |passwd --stdin user2 >/dev/null
echo "add user1 successful!"
useradd user3
echo user3 |passwd --stdin user3 >/dev/null
echo "add user1 successful!"
编写完毕保存为.sh文件,并赋予执行权限。
检查脚本的语法错误:
bash -n 脚本路径/脚本名
调试执行脚本语法:
bash -x /path/to/script_file.sh
变量名称取名规则:
1、数字、字母、下划线
2、不能以数字开头
3、区分大小写
4、风格统一,见名知意。
面向过程的编译语言,都有语言控制结构:
1、顺序执行:默认、逐条执行。
2、选择执行:依据条件语句判断来选择执行的步骤。 0:真,非零:假
3、循环执行:依据条件重复执行某一条命令的次数。
控制语句:
不同语句写在同一行里需要用分号;隔开。
bash循环控制语句:
for循环:
事先提供一个元素列表,然后让变量去遍历(挨个提取)此元素列表;每访问一个元素,执行一次循环体,直至元素遍历完毕。
用法: for var_name in 元素列表比如元素1 元素2 元素3
do
语句1;
语句2;
done
while
until
练习二:给练习一调整为循环语句。
#!/bin/bash
for username in user1 user2 user3;
do;
useradd $username ;
echo $username |passwd --stdin $username;
done;
练习三 、显示/et/inittab,/etc/rc.d/rc.sysinit,/etc/fstab三个文件各有多少行。
#!/bin/bash
for PathWc in /etc/inittab /etc/rc.d/rc.sysinit /etc/fstab
do
cat $PathWc |wc -l
done
练习:
1、 使用for循环创建/tmp/1.dir,/tmp/2.dir和/tmp/3.dir,权限为750
#!/bin/bash
for Direc_Gest in 1.dir 2.dir 3.dir
do
mkdir /tmp$Dire_Gest
chmod 750 /tmp/$Dire_Gest
done
2、统计/etc/fstab、/etc/rc.d/rc.sysinit、/etc/inittab文件中各自以#开头的行的行数和空白行行数。
#!/bin/bash
for filecount in /etc/fstab /etc/rc.d/rc.sysinit /etc/inttab
do
egrep ^#|^$ $filecount |wc -l
done
3、将/etc/passwd 、/etc/shadow的最近一次修改时间改为2010年3月12日14:11 3秒
#!/bin/bash
for ModfyTime in /etc/passwd /etc/shadow
do
touch -m -t 201003121411.03 $ModfyTime
#touch -a 修改访问时间 默认当前时间
#touch -m 修改修改时间 当前时间
#touch -m -t yymmddhhmm.ss 修改指定时间。
done
4、将user1、user2、user3加入到testgrp组中,以其为额外组。
#!/bin/bash
groupadd testgrp
for usertogrp in 1 2 3
do
usermod -G testgrp user$usertogrp
done
5、copy /etc/passwd /etc/shadow /etc/inittab 到/tmp/目录下,并将名称已当前时间进行重命名。
#!/bin/bash
for filerename in /etc/passwd /etc/shadow /etc/inittab
do
basefile= `basename $filerename`
cp $filename /tmp/$basefile-`date +%F`
done
6、显示/etc/paswd文件的第1、3、6、12个用户的用户名、id、基本组的组名。
#!/bin/bash
for Line in 1 3 6 12
do
username=`head -n 1 /etc/passwd |tail -n 1 |cut -d : -f 1`
Idname=`head -n 1 /etc/passwd |tail -n 1 |cut -d : -f 3`
Groupname=‘id -gn $username‘
echo "username:$username idname=$Idname groupname=$Groupname"
done
列表的生成:
1、逐个列出,例如1 2 3 4
2、使用通配符列出,例如 /etc/*
#!/bin/bash
for file in /etc/*
do
file $file
done
3、使用命令生成列表。
#!/bin/bash
for File in `ls /var`
do
file /var/$File
done
4、生成数字序列,
{}:例如生成1到100的数值,{1..100}
equ:equ [起始数字] [步长] [结束数字]
练习:
1、取出每个用户的用户名和shell
思路:首先需要确定行数
#!/bin/bash
line=`wc -l /etc/passwd |cut -d : f 1`
for Line in equ 1 $line
do
head -$Line /etc/passwd |tail -1 |cut -d: -f 1,7
done
如何在shell中进行算术运算
shell不支持浮点数(小数),计算结果中如果有浮点数会被圆整为正数(化零为整)
默认变量使用的是字符变量类型。实现算数运算的方法:
1、 $[变量名]=valume
2、 $((变量名))=valume
3、 let 例如:let a=$a+$b
4、 expr 例如:f=`expr $a + $b`
练习:
随意声明两个变量,并给出整数值,然后计算他们的加减乘除的结果。
#!/bin/bash
Valume1=12
Valume2=43
for suanfa in + - * /
do
echo "$Valume1$suanfa$Valume2= `let $Valume$suanfa$Valume`"
done
选择执行语句
可以独自执行的命令不需要添加[]来完成测试。
bash条件测试方式有以下几种:
1、 [ expression ]:[]里面有空格。表示条件选择的依据。
2、 [[ expression ]]:
3、 test expression:
4、 bash 命令:
bash执行命令后有执行结果的状态返回值:echo $? (0表示成功,其他都是错误)
可以使用exit命令在脚本中自定义脚本执行状态返回值,如果没有定义,脚本执行状态返回值取决于脚本执行结束前的最后一条语句的执行结果。
判断格式:
单分支if语句:
1、 if 条件 ;then
语句1
语句2
fi
练习:
1、查看某用户是否存在,并显示相关信息。
#!/bin/bash
Username=user1
if id $Username &>/dev/null
then
echo "$Username is living"
fi
2、如果用户存在,显示用户id和shell信息。
3、如果一个设备已经挂载,显示挂载点。
4、 检查某一文件的 的空白行,并统计行数。
2、if 条件
then
语句1
语句2
else
语句1
语句2
fi
练习:
1、检查用户是否存在,并显示相关信息,如果不存在创建用户名,密码。
#!/bin/bash
Username=zhangliang
if id $Username &>/dev/null
then
echo "$Username is living"
echo "$Username id&shell is`grep "$Username:" /etc/passwd |cut -d : -f 3,7`"
else
useradd $Username
echo "no this username,please wait....useradding...... "
echo "$Username" |passwd --stdin $Username
echo "$Username id&shell is `grep "$Username:" /etc/passwd |cut -d : -f 3,7`"
fi
整数测试:
expression: 数值1 比较符号 数值2
比较符号:
-gt:大于号
-ge:大于或等于号
-eq:等于号
-lt:小于号
-le:小于或等于
-ne:不等于
例如,写一个脚本,随意生成两个整数,比较其大小,显示大数。
#!/bin/bash
a=$RANDOMb=$RANDOMif [$a -gt $b ]thenecho "the max number is $a"elseecho "the max number is $b"fi
2、给定一个整数,判定奇偶性。
3、给定一个用户,如果其ID号大于499,就显示其为普通用户,否则显示其为管理员。
4、给定一个用户,如果其uid等于gid,就说明这个是“good guy”否则显示“bad guy”
#!/bin/bashUsername=pulseif [ `id -u $Username` -eq `id -g $Username` ]thenecho "this user($Username) is good guy! "elseecho "this user($Username) is bad guy!"
嵌套练习:
求200以内的所有为3的整数倍的整数之和
#!/bin/bashsum=0for n in {1..100}doif [ $[$n%3] -eq 0 ]thensum=$[$sum+$n]fidoneecho "300 zhengshu he equal is $sum"
bash编程之位置变量
$1.....$11.......:表示可以通过执行程序,然后加常量的方式来执行并替换程序中的$1等相关变量。
$@:表示可以将多个常数按照一个字符串的形式显示出来
$*:将多个常数分别以各自字符串的形式显示出来。
$#:汇总常数的个数。
$0:脚本名称。
#!/bin/bash
echo $1
echo $2
echo $@
echo $*
echo $#
执行程序:./pos.sh a b c d e
执行结果:a
b
a b c d e
a b c d e
5
例:通过参数传递n个正整数给脚本,并求和。
#!/bin/bashNum=0Count=0for n in $@doCount=$[$Count+$n]doneecho "Count is $Count"
shift:对一组数进行重新定位第一个数。shift之前的数被踢掉。
实现位置参数轮替。
#!/bin/bash
echo $1
shift
echo $1
shift
echo $1
执行:./shift.sh a b c d
结果:a
b
c
练习:
添加10个用户,需要先判断用户是否存在,如果不存在才添加,统计添加的用户数,并显示总的用户数。
#!/bin/bashcountaddu=0for n in `seq 1 10`doif [ `grep "user$n" /etc/passwd` &>/dev/null ]thenecho "old user"elseadduser user$ncountaddu=$[$countaddu+1]fidoneecho "adduser count is $countaddu"echo "countuser is :`wc -l /etc/passwd`"
字符测试:
文件测试:
默认变量使用的是字符变量类型。实现算数运算的方法:
1、 $[变量名]=valume
2、 $((变量名))=valume
3、 let 例如:let a=$a+$b
4、 expr 例如:f=`expr $a + $b`
— +:对两个变量做加法。
— -:对两个变量做减法。
— *:对两个变量做乘法。
— /:对两个变量做除法。
— **:对两个变量做幂运算。
— %:取模运算,第一个变量除以第二个变量求余数。
— +=:加等于,在自身基础上加第二个变量。
— -=:减等于,在第一个变量的基础上减去第二个变量。
— *=:乘等于,在第一个变量的基础上乘以第二个变量。
— /=:除等于,在第一个变量的基础上除以第二个变量。
— %=:取模赋值,第一个变量对第二个变量取模运算,再赋值给第一个变量。
(1)、在括号运算中((i=$j+$k))运算符号两边可以有空格也可以没有空格,但是expr运算i=`expr $j + $k`中运算符号两边必须要有空格!
(2)、乘法运算符号是 \* ,而不是 * ;除法运算 / 表示整除,1/2=0。
2、Let expressions 执行一个或多个表达式。
(1)、表达式中的变量前不必有$.
(2)、如果表达式中包含了空格或其他特殊字符,则必须引起来。(let i=i+1中除了let后面有一个空格外,其他地方不能有一个空格!)
例:let “I = I + 1” 或 let i=i+1
bash脚本的知识点:
条件测试方式:
bash命令:
[ expression ]
test expression
[[ expression ]]
条件测试的方法:
整数测试:
大于:-gt
小于:-lt
等于:-eq
大于等于:-ge
小于等于:-le
不等于:-ne
字符测试
文件测试
命令状态返回值:0 正确
exit [n] :脚本执行的最后一条命令的状态返回值,
bash字符测试:
>:大于
<:小于
==:等于
=~:判定左边的字符串是否能够被右边的模式所匹配,通常使用[[ ]],[]内需要空格。
模式要求: 一般做行尾、行首锚定($^等),不可以做词尾锚定,不能加引号
单目操作:一个操作数,
-z $string:为空则为真,不空为假。
-n $string:为空则为假,不空为真。
例子:写一个脚本,判定用户的shell是否为bash。
#!/bin/bashhell=`grep "$1:" /etc/passwd |cut -d : -f 7`if [ $hell == "/bin/bash" ]thenecho "this is bash user"
ret =0
elseecho "this is not bash user"
ret =8
fi
exit $ret
bash的特殊参数:$0 scriptsname
如果脚本是带路径的时候,要提取脚本名使用如下命令:
basename $0
bash编程之位置变量
$1.....$11.......:表示可以通过执行程序,然后加常量的方式来执行并替换程序中的$1等相关变量。
$@:表示可以将多个常数按照一个字符串的形式显示出来
$*:将多个常数分别以各自字符串的形式显示出来。
$#:汇总常数的个数。
$0:脚本名称。 如果是带路径的$0 ,使用basename $0
bash知识点:组合条件测试
-a:与
-o:或
!:非 单目操作
写一个脚本:给定用户,如果其不存在,就退出脚本。
#!/bin/bashif ! id $1 &>/dev/nullthenexit 6elseecho user is existfi
bash之if多分支语句语法讲解及脚本练习
多分支if语句
语法格式:
if 条件;then
语句1
elif 条件2;then
语句1
elif 条件3;then
语句
else
语句
fi
练习:
判断当前主机的cup制造商,其信息在/proc/cpuinfo文件中vendor id 一行。
如果制造商是genuineintel ,就显示为intel
如果制造商是authenticamd,就显示amd
否则,显示无法识别。
#!/bin/bash
Vendor=`grep "vendor_id" /proc/cpuinfo |uniq |cut -d : -f 2`
if [[$Vendor =~ [[ :space: ]]*GenuienIntel$]];
then
echo "intel"
elif [[$Vendor =~ [[ :space: ]]*AuthenticAMD$]];
then
echo "AMD"
fi
2、通过参数传递给一个脚本参数,如fedora,gentoo,redhat,判断linux发行版所处的发行系列。
如果为fedora,centos,redhat,就显示为redhat
[ $1 == fedora -o $1 == centos -o $1 == redhat ]
如果为suse,opensuse,就显示为suse
如果是ubuntu,mint、debian,就显示为debian
否则,显示为其他。
bash测试之文件测试
方法:操作符 文件路径
-f:测试其是否是普通文件,即文件类型为-。
-d:测试其是否为目录文件,文件类型d
-e:测试文件是否存在,存在为真,否则为假。
-r:测试文档对当前用户来说是否可读。
-w:测试文件对当前用户是否可写。
-x:测试文件是否对当前用户可执行。
-s:测试文件内容是否为空,不空为真,否则为假。
-l:测试文件是否是链接文件。
练习:如果/tmp/test10文件不存在,就创建之:
if [ ! -e /tmp/test10 ];
then
mkdir /tmp/test10
fi
短路操作:
与运算:&& 全真则真 ,有假则假
或运算:|| 有假则全假,有真则全真
通过以上运算思路,可以确定如果一个运算的前半部分是真,对于与运算,就需要进行后面的运算才能知道结果是真还是假,如果是用或运算,则知道前面是假可以推断出后面是任何运算都为假,也就没有必要进行后面的运算。这个就是短路运算的思维。
比如:
前面的运算为真,确实存在这个文件,就不会进行后面的操作,如果前面为假,则会进行后面的操作。
1、mkdir /backup
tar Jcf /backup/etc-`date +%f`.tar.xz /etc/*
2、tar xf /backup/etc-2013-07-22.tar.xz -C /var/tmp/
3、 #!/bin/bash
comdec=$1if [ -z $comdec ];thencomdec=gzipfi[ -d /backup ] || mkdir /backupif [ $comdec == ‘gzip‘ ];thentar zcf /backup/etc-`date +%F-%H-%S`.tar.gz /etc/*[ $? -eq 0 ] && echo "backup etc finished.(gzip)"elif [ $comdec == ‘bzip2‘ ];thentar jcf /backup/etc-`date +%F-%H-%S`.tar.bz2 /etc/*[ $? -eq 0] && echo "backup etc finished.(bzip2)"elif [ $comdec == ‘xz‘ ];thentar Jcf /backup/etc-`date +%F-%H-%S`.tar.xz /etc/*[ $? -eq 0 ] && echo "backup etc finished.(xz)"elseecho "usage: `basename $0` {[gzip|bzip2|xz]}"exit 6fi
bash编程之CASE语句:
case 变量引用(${}) in value1)语句
;;
value2)语句
;;
value3)语句
;;
*)
语句
esac
#!/bin/bashif [ -z $comdec ];thencomdec=gzipfi[ -d /backup ] || mkdir /backupcase $comdec ingzip)tar zcf /backup/etc-`date +%F-%H-%S`.tar.gz /etc/*Retval=$?;;bzip2)tar jcf /backup/etc-`date +%F-%H-%S`.tar.bz2 /etc/*Retval=$?;;xz)tar Jcf /backup/etc-`date +%F-%H-%S`.tar.xz /etc/*Retval=$?;;*)echo "usage: `basename $0` {[gzip|bzip2|xz]}"exit 6;;
esac
[ $Retval -eq 0] && echo "backup etc finished.($comdec)"
read命令
可以读取键盘输入数值到变量中
例如:
read a b
2 5
echo $a
2
1、echo -n 表示不换行,结合read一起使用。
2、read -p “内容” value
参数:
-t :设置超时时间
-p :设置输出说明的内容
练习: 显示一下菜单内容
m|M show memory
d|D show disk
q|Q quit
2、如果用户选择了第一个选项,在显示内存信息
如果选择2,显示磁盘挂载及使用情况
选择3,退出,并显示退出
其他任何信息,均说明错误。
#!/bin/bashecho "(m|M) SHOW MEMORY"echo "d|D show disk"echo "q|Q quit"echo -n "please input (m|d|q)" && read choicecase $choice inm|M)free -m;;d|D)df -lh;;q|Q)echo "you are quit now"exit 0;;*)echo "invalid input"exit 5;;esac~
安全上下文
用户执行访问某一个文件的时候,涉及到两个内容,第一就是使用的命令,第二就是被访问对象,那么他的安全权限就分别由这两个来决定,如果他没有命令执行权限,被访问对象无法执行,同样如果被访问对象没有访问权限,那么即使他有命令执行权限也是没有用的。
通过passwd来理解特殊权限。
s:suid,set uid,属主有s权限,意味着任何用户在执行此程序的时候,其进程的属主不在是发起人本身,而是这个程序的属主。
chmod u+|- s /path/to/somefile
可以通过大小写s来判断这个suid是原来就有的还是后来赋予的,
s:原来有
S:后来加
s:sgid set gid,属组有s权限,意味着执行此程序时,其进程的属组不再是运行者所属的基本组,而是程序的属组。
chmod g+|- s /path/to/somefile
t:sticky,黏贴位,附加在other的权限上,表现为t,作用是只有文件所有者可以删除,其他用户不可以删除。
chmod o+|- t /path/to/somefile
chmod 3775 /tmp/test
练习:
1、复制/bin/cat为/tmp/cat
cp /bin/cat /tmp/cat
2、复制/etc/rc.d/rc.sysinit为/tmp/hello
cp /bin/rc.d/rc.sysinit /tmp/hello
3、设定helllo文件的其它用户权限为无权限。
chmod o-rwx /tmp/hello
4、切换至普通用户,执行/tmp/cat,查看/tmp/hello,验证是否可查。
/tmp/cat /tmp/hello 无权限
5、以root用户身份设定/tmp/cat 具有suid权限,然后用普通用户,查看/tmp/cat,/tmp/hello,并验证。
chmod u+s /tmp/cat
/tmp/cat /tmp/hello 成功 如果反过来只修改hello的suid,是不会执行成功的。
不同命令操作权限不同,结果也不同,总之要求编辑权限既要设置命令权限,也要设置文件权限最好。
练习二:
1、创建组magedu
groupadd magedu
2、添加用户hadoop和hive,均以magedu为额外组。
useradd -g magedu hadoo
3、创建目录/tmp/test,并设定其组为magedu。
mkdir /tmp/test
chgrp magedu /tmp/test
4、分别使用两个普通用户在/tmp/test目录创建文件,看权限。
su hadoo
touch /tmp/test/123 不成功
5、设定/tmp/test目录具有写权限。
chmod g+w /tmp/test
6、再次分别使用普通用户创建文件,查看权限。如果能,查看文件属组
touch /tmp/test/123 不成功
7、设定/tmp/test的属组具有SGID,再次创建并查看。并验证删除操作
chmod g+s /tmp/test/123 成功
8、以root设定/tmp/test具有sticky,验证删除操作。
rm /tmp/test/123 成功
chmod o+t /tmp/test
rm /tmp/test/123 不成功,权限不足。
练习三:
1、复制/etc/issue为/tmp/issue
cp /etc/issue /tmp/issue
2、修改/tmp/issue的权限为属主读写执行,属组读写,其他只读,且具有suid
chmod 4764 /tmp/issue
3、复制文件/etc/fstab为/tmp/fstab,修改/tmp/fstab的权限为属主读写执行,属组读,其他只读,且具有sgid和sticky。
cp /etc/fstab /tmp/fstab
chmod 3744 /tmp/fstab
FACL文件访问控制列表
用于在原有的访问控制机制之外增加一种文件访问控制机制。由于普通用户创建的文件不可以修改自身的权限(chmod)。就可以使用acl来限制其他用户的访问。
1、通过getfacl可以查看文件的权限。
getfacl /tmp/test/123
2、通过setfacl设置文件的访问权限。
-m, --modify=acl modify the current ACL(s) of file(s)
setfacl -m u:username:mode /path/to/file #为用户username设定具有mode的权限
setfacl -m g:groupname:mode /path/to/file #为组groupname设定具有mode的权限
-M, --modify-file=file read ACL entries to modify from file
-x, --remove=acl remove entries from the ACL(s) of file(s)
setfacl -x u:username /path/to/file #取消某一个用户的acl权限。
-X, --remove-file=file read ACL entries to remove from file
-b, --remove-all remove all extended ACL entries
-k, --remove-default remove the default ACL
--set=acl set the ACL of file(s), replacing the current ACL
--set-file=file read ACL entries to set from file
--mask do recalculate the effective rights mask
-n, --no-mask don‘t recalculate the effective rights mask
-d, --default operations apply to the default ACL
-R, --recursive recurse into subdirectories
-L, --logical logical walk, follow symbolic links
-P, --physical physical walk, do not follow symbolic links
--restore=file restore ACLs (inverse of `getfacl -R‘)
--test test mode (ACLs are not modified)
-v, --version print version and exit
-h, --help this help text
对于新加入的磁盘默认情况下是不支持acl功能的,需要通过在挂载的时候添加挂载选项-o acl才可以。
比如:
mount -o acl /dev/sdb1 /mnt/sdb1
拒绝hive用户对文件的任何访问
setfacl -m u:hive:--- /tmp/test/123 设置后即使o有权限也是不能访问的。
bash编程之while和until
while 测试条件;
do
语句
done
条件满足时就循环,直到条件不再满足,就退出循环。
补充:
算数运算符
count=$[$count+$i] 等于 count+=$i
sum=$[$sum+1] 等于 let sum++
类似的 let sum-- 现加后引用
let --sum 先引用后加
计算100以内正整数的和
#!/bin/bash
sum=0
for i in {1..100};
do
sum+=$i
done
echo $sum
#!/bin/bash
sum=0
count=1
while [ $count -le 100];
do
sum+=$count
let count++
done
练习:如果用户的id号是偶数,就显示其名称和shell,对所有用户执行此操作。
while read line ;
do
uid=`echo $line |cut -d: -f 3`
if [ $[$uid%2 ] -eq 0 ];
then
echo $line | cut -d: -f 1,7
fi
done < /etc/passwd
练习:转换用户输入的字符为大写,除了quit(遇见quit退出)
#!/bin/bash
String
read -p "A string:" String
while[ "$String" != ‘quit‘ ]; #引号 用于带空格的字符串转换
do
echo $String |tr ‘a-z‘ ‘A-Z‘
read -p "next(QUIT for quiting)" String
done
until 测试条件;
do
语句
done
条件不满足就循环,满足则不循环。
练习:每个10秒查看hadoo用户是否登陆,如果登陆,显示登陆并退出,否则,显示时间,并说明未登陆。
until who |grep "hadoo" &>/dev/null ;
do
date
sleep 10
done
echo “hadoo is here”
程序在循环过程中不在执行循环体上面的内容了,所有需要继续执行循环体外的内容,需要将其写在循环体里面。
九、shell编程