shell编程,即脚本编程,属于动态语言(即解释型),是命令的堆砌。
脚本后缀为.sh
,执行格式为ELF(file /bin/ls test.sh
可查看)。
shebang符号:#!
开头有#!/bin/bash
,否则sh为文本格式;但不添加的话 bash test.sh
指定解释器也能执行 。
一般脚本写完后都要给执行权限
chmod +x test.sh
./test.sh
测试语法错误:bash -n test.sh
单步执行:bash -x test.sh
作用域:当前shell和子进程
本地变量转环境变量:export NAME
定义环境变量:export NAME = VALUE
脚本执行时启动子shell进程
命令行中启动的脚本会继承当前shell环境变量
系统自动执行的脚本(非命令行启动)需要自己定义环境变量
NAME=foobar
echo $NAME
执行bash再次echo
,则无输出。
./test.sh arg1 arg2 ...
可以用$0, $1, $2
来引用test.sh,arg1,arg2
$?
:上一个命令的执行状态返回值(0-255),0为执行成功。1,2,127为系统预留。
执行状态返回值默认定向为/dev/null
,即软件设备,又叫bit bucket,数据黑洞。
$#
:参数的个数。
$@
:参数列表。
$*
: 参数列表,
#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done
echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done
[$!]bash 1.sh "a b" c
-- $* 演示 ---
a b c
-- $@ 演示 ---
a b
c
要区分返回值(return)与返回结果(result)
RESULT=`ls`
左式为右式的返回结果,不是返回状态结果。
exit
:退出脚本,
exit 状态码
若未自定义状态码,则返回exit上一条命令的状态码
注意区别if CMD
有无反引号,命令带反引号则是执行结果;不带则是return结果,0为真。
#参数很多时,可以用shift实现队列移进效果
#!/bin/bash
echo $1
shift
echo $1
shift 2
echo $1
${var}
用来引用变量,多数情况下不会混淆,所以花括号一般省略。
引用时注意强弱引号。
撤销变量:unset VARNAME
,不加$
定义时set
其实是被省略的。
set
命令可以查看当前shell变量
查看环境变量方法:printenv, env, export
保存多个值的方法
$PATH=/sbin:/bin
或
$PATH=/sbin
$PATH=$PATH:/bin
默认所有变量都是字符串!!!
declare # 或者typeset,用来声明变量类型。声明的同时可以赋值。
-i #整数
-a
-f #显示函数
-F #显示函数名称
-x var #,设置为环境变量,可以同时赋值。
-r #read-only
条件测试:
3种条件测试表达式:
条件间的逻辑运算
-a
:与-o
:或!
:非eg.
if [ $# -ge 1 -a $# -le 3 ]
if [ $# -ge 1 ] && [ $# -le 3 ]
命令间的逻辑关系:
与&&
或||
非!
-eq
:等值比较,相等则为真。
a=1 b=3
[ $a -eq $b ]
(注意空格)
echo $?
返回0-255,0表示相等。
-ne #不等比较,不等为真。
-gt #大于
-lt #小于
-ge #大于等于
-le #小于等于
简单运算:
高级运算:
a=1
b=1
let c=$a+$b
c=$[$a+$b]
c=$(($a+$b))
c=`expr $a + $b `
bc <<< "scale=2;3/2"
echo "scale=2;3/2" | bc
#or
bc #enter the bc cli
# let补充
let a+=$b
let a+=1
let a++
生成0-32767
相当于echo $RANDOM
linux有两个软件模拟的随机数生成器
/dev/random
/dev/urandom
它们到系统中的熵池取随机数,注意是取走,不是复制。
这些随机数与系统相关,比如敲键盘间隔,中断请求间隔。
熵池空后,urandom会通过软件模拟生成,而random会阻塞。
所以,urandom好用,但random更安全。
-e FILE #是否exist
-f FILE #是否为普通文件
-d FILE #是否为目录
-r -w -x #当前用户是否有r/w/x权限
# eg.
[ -e FILE ]
[ ! -e FILE ]
#文件赋值
FILE=/etc/passwd
# == 和 = 都行
[ $a == $b]
[ $a != $b]
-n str # 测试字符串是否为空,空为真
-z str # 测试字符串是否为空,不空为真
# if
if ...; then
statement
elif ...; then
statement
else
statement
fi
# case
case $FOOBAR in
value1)
statement
value2)
statement
*)
statement
esac
#case 和 esac 相对应,类似if 与f。
# 示例
#! /bin/bash
case $FOOBAR in
[0-9])
echo "digit"
[a-z])
echo "lower"
*)
echo ""other character
esac
#! /bin/bash
case $1 in
"start")
echo "start server";;
"stop")
echo "stop server";;
"-h"|"--help") #引号可加可不加
echo "help";;
*)
echo "`basename $0` {start|stop}";;
esac
#!/bin/bash
# 判断passwd行数是否大于100
USERNUM=`wc -l /etc/passwd | cut -d: -f1`
echo "$USERNUM lines"
[ $USERNUM -gt 100 ] && echo "passwd is a big file" || echo "passwd is a small file"
# 判断用户是否存在
id foobar &> /dev/null && echo "user exists." || useradd foobar
#!/bin/bash
# 用户user1不存在则添加,并修改密码。
! id user1 &> /dev/null && useradd user1 && echo "passwd1" | passwd --stdin user1 &> /dev/null || echo "user1 exists"
#显示有多少用户。
USERS=`wc -l /etc/passwd | cut -d: -f1`
echo "$USERS users"
#!/bin/bash
# 如果UID为0,则显示这是管理员,否则显示为普通用户。
USER=root
USERID=`id -u $USER`
[ $USERID -eq 0 ] && echo "admin." || echo "common user."
# or in this way
if [ $USERID -eq 0 ]; then
# or [ `id -u $NAME` -eq 0 ]
echo "admin."
else
echo "common user."
fi
#!/bin/bash
# 统计有多少用户使用bash,有则显示其中一个用户名。
RESULT=`grep "\$" /etc/passwd`
if [ $? -eq 0 ]; then
USERNUM=`grep "\$" /etc/passwd | wc -l`
# echo $RESULT | wc -l 结果为1
echo "$USERNUM users use bash"
ONEUSER=`grep "\$" /etc/passwd | head -1 | cut -d: -f1`
echo "eg. $ONEUSER "
else
echo "no result."
fi
#!/bin/bash
#判断某用户密码剩余使用期限是否小于警告期限
WARN=`grep "foobar" /etc/shadow | cut -d: -f6`
NOW=`date +%s`
NOW=$[$NOW/$[24*60*60]]
MAX=`grep "foobar" /etc/shadow | cut -d: -f5`
CHANGE=`grep "foobar" /etc/shadow | cut -d: -f3`
REMAIN=$[$MAX-$[$NOW-$CHANGE]]
if [ $REMAIN -lt $WARN ]; then
echo "warning"
else
echo "no problem"
fi
#判断命令历史数是否大于1000
#注意,which查不到的都是bash内置命令,history是bash内置命令,能不能开启由bash说了算。
#暂时只需知道如何编写
#/bin/bash
SIZE=`history | tail -1 | cut -d" " -f2`
echo $SIZE
if [ $SIZE -gt 1000 ]; then
echo "greater than 1000"
else
echo "less than 1000"
fi
#!/bin/bash
#判断uid和gid是否相等
if [ `id -n -u $1` == `id -n -g $1` ]; then
echo "same"
else
echo "not same"
fi
#!/bin/bash
#给定参数add则添加用户user0-9,del则删除用户0-9.
if [ $# -lt 1 ]; then
echo "test.sh --add/del"
exit 7
fi
if [ $1 == "--add" ]; then
for i in {0..9}; do
if id user$i &> /dev/null; then
echo "user$i exists."
else
useradd user$i
echo "user$i" | passwd --stdin user$i &> /dev/null
echo "user$i added successfully."
fi
done
elif [ $1 == "--del" ]; then
for i in {0..9}; then
if id user$i &> /dev/null; then
userdel -r user$i
echo "user$i deleted successfully."
else
echo "user$i not exist."
fi
done
else
echo "wrong using."
exit 8
fi
#! /bin/bash
#1-100奇数偶数的和
declare -i oddsum=0
declare -i evensum=0
for i in {1..100}; do
if [ $[$i%2] -eq 0 ]; then
let evensum+=$i
else
let oddsum+=$i
fi
done
echo "oddsum:$oddsum"
echo "evensum:$evensum"
#!/bin/bash
#添加/删除用户
DEBUG=0
ADD=0
DEL=0
for i in `seq 1 $#`; do
if [ $# -gt 0 ]; then
echo "\$#:$#" # $#会减小
case $1 in
-v|--verbose)
DEBUG=1
shift
;;
-h|--help)
echo "Usage:`basename $0` -v|--verbose --add USER_LIST --del USER_LIST -h|--help"
exit 0
;;
--add)
ADD=1
ADDUSERS=$2
shift 2
;;
--del)
DEL=1
DELUSERS=$2
shift 2
;;
*)
echo "Usage:`basename $0` -v|--verbose --add USER_LIST --del USER_LIST -h|--help"
exit 1
;;
esac
fi
done
echo \$DEBUG,\$ADD,\$DEL:$DEBUG,$ADD,$DEL
echo \$ADDUSERS:$ADDUSERS
echo \$DELUSERS:$DELUSERS
if [ $ADD -eq 1 ]; then
for USER in `echo $ADDUSERS | sed 's#,# #g'`; do
if id $USER &> /dev/null; then
[ $DEBUG -eq 1 ] && echo "$USER exists."
else
# useradd $USER
[ $DEBUG -eq 1 ] && echo "add user $USER."
fi
done
fi
if [ $DEL -eq 1 ]; then
for USER in `echo $DELUSERS | sed 's#,# #g'`; do
if id $USER &> /dev/null; then
# userdel $USER
[ $DEBUG -eq 1] && echo "delete user $USER"
else
[ $DEBUG -eq 1 ] && echo "no user $USER."
fi
done
fi
三种循环:for while until
# for
for i in list; do
...
done
for (( expr1 ; expr2 ; expr3 )); do
statement
done
# until
until CONDITION; do
statement
done
# while
while CONDITION; do
statement
done
#死循环
while ; do
done
#shell也有break和continue的用法
#生成列表:
{1..100}
seq [start [step]] end
#读取文件的每一行
while read LINE ; do
done < FILE
#定义函数的两种方法
function FUNCNAME() {
cmd
}
FUNCNAME() {
cmd
}
函数本身也有返回状态值
可以return 定义返回值,范围0-255
输出可以这样判定:
if [ $? -eq 0 ]; then
echo "success"
else
echo "fail"
fi
#!/bin/bash
# 接收参数
function ADD() {
echo $[$1+$2]
return 1
}
SUM=`ADD 3 4`
RET=$?
echo "sum:"$SUM
echo "ret:"$RET
# 输出
# sum:7
# ret:1
basename PATH
命令
显示基名
脚本帮助信息可以这样写
echo “Usage: `basename $0` [options] [args]”
read A
键盘输入a b
echo $A
输出a b
read A B
输入a b
echo $A,$B
输出a,b
read A B
输入a b c
echo $A,$B
输出a,b c
#!/bin/bash
#
read -p "input two nums:" A B
echo "$A+$B=$[$A+$B]"
read -t TIMEOUT FOOBAR
设置超时秒数。
echo -e "\033[1mHello\033[0m,world."
1m
的1
表示字体为粗体,2
为灰体,3
为斜体,4
为下划线。
31
表示前景色为红色,31-37
表示不同的前景色
41-47
表示不同的背景色。
echo -e "\033[31;42mHello\033[0m,world."
同时设置前景色和背景色。
echo -e "\033[1;31;42mHello\033[0m,world."
同时设置粗体,前景色和背景色。
字体、前景色、背景色的顺序随意。
#!/bin/bash
#计算1+2+..+100
let SUM=0
for i in {1..100}; do
#for i in `seq 1 100`; do
let SUM=$[$SUM+$i]
# or
#SUM=$[$SUM+$i]
#let SUM=$SUM+$i
done
echo "sum:$SUM"
# 向每个用户打招呼
for i in `seq 1 $USERS`; do echo "Hello `head -n $i /etc/passwd | tail -1 | cut -d: -f1`" ; done
#! /bin/bash
for i in `echo $1 | sed 's#,# #g'`; do
echo "$i"
done
#!/bin/bash
FILE=/etc/passwd
while read LINE ; do
[ `echo $LINE | awk -F : '{print $7}'` == '/bin/bash' ] && echo $LINE | awk -F : '{print $1}'
done < $FILE
PING() {
if ping -c1 -W1 $1 &> /dev/null; then
echo "$1 is up"
else
echo "$1 is down"
fi
}
for i in {1..10}; do
ping 192.168.1.$i
done