linux-shell编程

文章目录

  • 0x00.前言
  • 0x01.bash变量类型
    • 环境变量
    • 本地变量
    • 位置变量
    • 特殊变量
      • 返回值和返回结果
      • shift
    • 变量操作
  • 0x02.条件测试
    • 整数测试
      • linux的数学运算
      • random
    • 文件测试
    • 字符测试
    • 条件判断式
    • 练习
  • 0x03 循环
  • 0x04 函数
    • 定义
    • 返回值
    • 参数
  • 0x05 交互
    • basename
    • read
    • 颜色
  • 练习

0x00.前言

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

0x01.bash变量类型

  • 环境变量
  • 本地变量(局部变量)
  • 位置变量
  • 特殊变量

环境变量

作用域:当前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

#参数很多时,可以用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

0x02.条件测试

条件测试:

  • 整数测试
  • 字符测试
  • 文件测试

3种条件测试表达式:

  • [expression]
  • [[expression]]
  • test expression

条件间的逻辑运算

  • -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  #小于等于

linux的数学运算

简单运算:

  • et
  • $[]
  • $(())

高级运算:

  • expr
  • bc
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++

random

生成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

0x03 循环

三种循环: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

0x04 函数

定义

#定义函数的两种方法

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

0x05 交互

basename

basename PATH命令
显示基名
脚本帮助信息可以这样写

echo “Usage: `basename $0` [options] [args]”

read

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."
1m1表示字体为粗体,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

你可能感兴趣的:(linux)