bash 脚本
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bash 脚本相关的知识,希望对你有一定的参考价值。
bash 脚本
一 创建shell脚本
1 创建包含bash命令的文本文件
#!/bin/bash###写在文件的第一行,说明用什么解释器,来解释以下写的内容###
2 chmod +x scripts
3 将文件放置在用户的$PATH的目录中
~/bin – 用于用户的私有程序
/usr/local/bin – 本地开发、系统上的其他人使用的脚本
/usr/local/sbin - 本地开发、由root使用的脚本
直接运行脚本和使用source命令运行脚本是不同的!
[[email protected] ~]# vim /mnt/test.sh
[[email protected] ~]# chmod +x /mnt/test.sh
[[email protected] ~]# /mnt/test.sh
^C
[[email protected] ~]# ps f###f看到从属关系###
PID TTY STAT TIME COMMAND
3819 pts/1 Ss 0:00 -bash
3927 pts/1 R+ 0:00 \_ ps f
2656 pts/0 Ss+ 0:00 /bin/bash
611 tty1 Ss+ 0:11 /usr/bin/Xorg :0 -background none -verbose -auth /run
3218 ttyS0 Ss+ 0:00 /sbin/agetty --keep-baud ttyS0 115200 38400 9600
脚本调试模式:
1 #!/bin/bash -x
2 bash -x scripts
+:指执行的命令,即输入
不带加号:输出结果
例:
[[email protected] ~]# bash -x /mnt/test3.sh
+ (( i=5 ))
+ (( i>=0 ))
+ echo 5
5
+ sleep 1
+ (( i-- ))
+ (( i>=0 ))
+ echo 4
4
+ sleep 1
+ (( i-- ))
+ (( i>=0 ))
+ echo 3
3
+ sleep 1
+ (( i-- ))
+ (( i>=0 ))
+ echo 2
2
+ sleep 1
+ (( i-- ))
+ (( i>=0 ))
+ echo 1
1
+ sleep 1
+ (( i-- ))
+ (( i>=0 ))
+ echo 0
0
+ sleep 1
+ (( i-- ))
+ (( i>=0 ))
二 引用和转义
引用和转义在shell解析字符串时用于去除字符串中特殊字符或保留词语的特殊含义。这会导致按字面处理字符串,而不是展开变量或将其部分内容视作具有特殊含义。
1 弱引用
双引号:弱引用, ` ! $ 不引用
将字符串放置在双引号中,保留字符串中所有字符的文字值, $、`、\和!字符除外,即` ! $ 不引用,仍会执行该特殊字符
例:显示"$5"
""$5""是错误的,前面的双引号是一对,后面的""是一对的,因此要用‘"$5"‘
2 强引用
单引号:转义所有字符
将字符串放置在单引号中,保留字符串中所有字符的文字值同时禁用所有扩展。
3 转义
非引用的\是转义字符。它保留了下一个字符的文字值。
\:转义
三 变量
shell变量用于为稍后在脚本中使用的名称指定值,并且仅限于shell命令行或从中声明变
量的脚本。
$变量:变量的值
四 命令替换
命令替换在子shell中执行指定命令并用命令输出替换脚本中的命令替换。
语法:
$(shell command)
例:将/etc下的以.conf结尾的文件复制到/mnt下的etconf-年-月-日-秒的目录
#!/bin/bash
TODAY=$(date +%Y-%m-d-%S)
mkdir /mnt/etconfig.$(date +%Y-%m-%d-%S)
cp /etc/*.conf /mnt/etconfig.$(date +%Y-%m-%d-%S)
想要显示1b
a=1
echo ${a}b
五 循环
for循环用于值列表中的相同命令的重复
&&:前面的命令是正确的
||:错误的
例1:将ip为172.25.254.1到172.25.254.10的主机开启的显示172.25.254.x 是up否则是down。
ping -c1 -w1 ###-c1 执行次数,-w1:等待时间
#!/bin/bash
for NUM in {1..10}
do
ping -c1 -w1 172.25.254.$NUM &> /dev/null && echo 172.25.254.$NUM is up || echo 172.25.254.$NUM is down
done
例2:建立一个用户文件,写一个脚本使得该用户可以被建立。
#!/bin/bash
COUNT=`wc -l username | cut -c 1`
for NUM in `seq 1 $COUNT`
do
useradd `tail /root/username | sed -ne ${NUM}p`
done
例3:5秒倒计时:
#!/bin/bash
for ((SEC=5;SEC>0;SEC--))
do
echo -n After ${SEC}s is end###-n ###不换行###
echo -ne "\r\r" ###-e 动作 ,\r 刷新,将前面的内容覆盖###
sleep 1###每秒执行一次###
done
六 Shell计算命令
用$[]表示数学运算
echo $[1+2]
用(())表示数学运算。bash内建功能,效率高
例:循环与计算结合:
5秒倒计时:
#!/bin/bash
for ((SEC=5;SEC>0;SEC--))
do
echo -n After ${SEC}s is end###-n ###不换行###
echo -ne "\r\r" ###-e 动作 ,\r 刷新,将前面的内容覆盖###
sleep 1###每秒执行一次###
done
七 bash位置参数
有两种简单的方法可以将用户输入读入bash中的变量。第一个方法是使用read提示用户输入(使用-p选项)并将其直接存储到一个或多个变量:
交互式输入
read -p
例1:显示提示符:please give me a interface:输入接口eth0后,显示ip
#!/bin/bash
read -p "please give me a interface:"
ifconfig eth0 | grep netmask | awk -F " " ‘{print $2}‘
例2:编写脚本,若是脚本后面没有字符,则显示please give me a ip address,当给出ip后判断能否ping的通,ping的通则显示ip is up,反之,则显示ip is down
#!/bin/bash
while [ "$#" -eq "0" ]### $#表示字符串的个数###
do
echo "please give me a ip address "
exit 1
done
ping -w1 -c1 $1 > /dev/null && echo "$1 is up" ||echo "$1 is down"
八 退出状态
Linux命令完成时,将返回退出状态。成功完成程序时,将返回0的推出状态。这被bash当作逻辑True值。非零退出状态通常表示发生了错误,并且被bash当作逻辑False值。
出状态的值被存储在"?"中,可以使用以下命令查看:
echo $?
若为0,则表示命令是正确的
例:判断文件是否存在
#!/bin/bash
ls $1 &> /dev/null
[ "$?" -eq "0" ] && echo $1 is exist || echo $1 is not exist
九 test条件判断
test命令可用于评估bash脚本中的表达式。它评估其参数所指定的表达式,如果表达式为true,返回零退出状态,如果表达式为false,则返回非零退出状态。test具有替代语法,使用方括号"[]"将表达式括起来,这样更易于阅读
test n:非零 z:为零
例:
[[email protected] ~]# a=1
[[email protected] ~]# test -n "$a" ;echo $?
0
[[email protected] ~]# test -z "$a" ;echo $?
1
[[email protected] ~]# [ -n "$a" ] && echo yes
yes
[[email protected] ~]# [ -z "$a" ] && echo yes || echo no
no
例1:写一个脚本/mnt/check_ip.sh,要求若脚本后面没有跟ip,则显示please give me a ipaddr,若是ping的通,就显示ip is up,否则显示ip is down
#!/bin/bash
[ -n "$*" ] &&(
ping -c1 -w1 $* &> /dev/null && echo $* is up || echo $* is down
)||(
echo "please give me a ipaddr"
)
字符串比较运算符:=、!=
数字比较运算符:-eq、-ne、-lt、-le、-gt、-ge
-eq :等于
-gt :大于
-ge:大于等于
-a:并且
-lt:小于
-le:小于等于
-o:或者
例2:判断两个数字和的大小
#!/bin/bash
while [ "$#" -lt 2 ]
do
echo "please give two number"
exit 1
done
COUNT=$[ $1+$2 ]
while [ $COUNT -lt 10 ]
do
echo "the sum is smaller than 10"
break
done
while [ $COUNT -ge 10 ]
do
echo "the sum is bigger than 10"
break
done
不用while,则:
((NUM=$1+$2))
[ "$NUM" -lt "10" ]&& echo "the sum is smaller than 10" || echo "the sum is bigger than 10"
文件状态运算符:test -{b|c|e|f|d|r|w|x|s|L}
-b:表示文件类型为块设备
-c:字符设备
-d:目录
-L:链接
-S:套接字
-:文件
-f:常规文件
-e:表示存在
二进制文件运算符:-ef、-nt、-ot
-ef:硬连接
-nt:哪个文件新
-ot:哪个文件老
例:判断文件类型
#!/bin/bash
if
[ "$#" -eq "0" ]
then
echo "please give mw a file"
elif
[ ! -e $1 ]
then
echo "$1 is not exist"
elif
[ -L "$1" ]
then
echo "$1 is a soft link"
elif
[ -d "$1" ]
then
echo "$1 is a directory"
fi
十 if语句
f命令检查if后面的命令或列表的退出值。如果第一个命令评估为true/零,则运行then之后的命令列表,直至任一else。如果第一个命令评估为false/非零,则运行else与fi之间的命令列表(反向平写if,标记if块的结束)。
例1:建立用户
#!/bin/bash
if
[ -z "$1" ]
then
echo please give me a userfile
elif
[ ! -e "$1" ]
then
echo "$1 is not exist"
else
for NAME in `cat $1`
do
USER=`getent passwd $NAME`
[ -z $USER ]&&(
useradd $NAME
echo westos | passwd --stdin $NAME &>/dev/null
)||(
echo "$NAME is exist"
)
done
fi
或者:
#!/bin/bash
if
[ -z "$1" ]
then
echo please give me a userfile
elif
[ ! -e "$1" ]
then
echo "$1 is not exist"
else
for NAME in `cat $1`
do
USER=`getent passwd $NAME`
if
[ -z "$USER" ]
then
useradd $NAME
echo westos | passwd --stdin $NAME &> /dev/null
else
echo $NAME is exit!!
fi
done
fi
例2:当脚本后面接create时,建立/mnt/userfile的用户,当脚本后面接delete,删除/mnt/userfile的用户。
#!/bin/bash
if
[ "$#" -lt "2" ]
then
echo "Usage: /mnt/ctrl_user.sh <create|delete> <userfile>"
fi
if
[ "$1" = "create" ]
then
if
[ -z "$2" ]
then
echo please give me a userfile
elif
[ ! -e "$2" ]
then
echo "$2 is not exist"
else
for NAME in `cat $2`
do
USER=`getent passwd $NAME`
[ -z $USER ]&&(
useradd $NAME &> /dev/null
echo westos | passwd --stdin $NAME &>/dev/null
)||(
echo "$NAME is exist"
)
done
fi
elif
[ "$1" = "delete" ]
then
if
[ -z "$2" ]
then
echo please give me a userfile
elif
[ ! -e "$2" ]
then
echo "$2 is not exist"
else
for NAME in `cat $2`
do
USER=`getent passwd $NAME`
[ -n $USER ]&&(
userdel -r $NAME &> /dev/null
)||(
echo "$NAME is not exist"
)
done
fi
fi
十一 case语句
case语句 :它能够把变量的内容与多个模板进行匹配,再根据成功匹配的模板去决定应该执行哪
部分代码。
例2:当脚本后面接create时,建立/mnt/userfile的用户,当脚本后面接delete,删除/mnt/userfile的用户。
#!/bin/bash
case "$1" in
create)
[ "$#" -lt "2" ] && echo "Usage: /mnt/ctrl_user.sh <create|delete> <userfile>" || ( for NAME in `cat $2`
do
USER=`getent passwd $NAME`
[ -z $USER ] && (
useradd $NAME &> /dev/null
echo westos | passwd --stdin $NAME &> /dev/null
) || (echo "$NAME is exit")
done
)
;;
delete)
[ "$#" -lt "2" ] && echo "Usage: /mnt/ctrl_user.sh <create|delete> <userfile>" || ( for NAME in `cat $2`
do
USER=`getent passwd $NAME`
[ -n $USER ] && (
userdel -r $NAME &> /dev/null
) || (echo "$NAME is not exit")
done
)
;;
esac
十一 expect 语句
在shell中利用expect实现自动应答脚本
#!/usr/bin/expect
这一行告诉操作系统脚本里的代码使用那一个shell来执行。
set timeout 10
设置后面所有的expect命令的等待响应的超时时间,单位为秒。
spawn talk
spawn是expect的内部命令,作用是给后面的shell指令加个壳,用来传递交互指令。
expect "who"
判断上次输出结果里是否包含“who”的字符串,如果有则立即返回,否则等待超时时间后返回。
send "westos\n"
执行交互动作,相当于手工输入"westos"。
expect eof
作用是在输出中搜索文件结束符,如果没有这一行,脚本会立即退出,得不到正确结果。
interact
执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。否则退出登录。
$argv 参数数组
expect脚本可以接受从bash传递过来的参数.可以使用[lindex $argv n]获得,n从0开始,分别表示第
一个,第二个,第三个....参数。
例1:expect的使用示例
yum install expect -y
[[email protected] ~]# which expect
/usr/bin/expect
[[email protected] ~]# vim /mnt/ask.sh
#!/bin/bash
read -p "Who are you: " NAME
read -p "How old are you: " AGE
read -p "what‘s you class: " CLASS
read -p "Are you happy: " FEEL
echo $NAME is $AGE old study $CLASS and feel $FEEL
[[email protected] ~]# vim answer.exp
#!/usr/bin/expect
set NAME [ lindex $argv 0 ]###变量###
set AGE [ lindex $argv 1 ]
set CLASS [ lindex $argv 2 ]
set FEEL [ lindex $argv 3 ]
spawn /mnt/ask.sh###监控/mnt/ask.sh
expect "Who"###\r换行###
send "$NAME\r"
expect "How"
send "$AGE\r"
expect "class"
send "$CLASS\r"
expect "happy"
send "$FEEL\r"
expect eof###退出expect###
测试:
[[email protected] ~]# expect answer.exp
spawn /mnt/ask.sh
Who are you:
How old are you:
what‘s you class:
Are you happy:
is old study and feel
[[email protected] ~]# expect answer.exp hello 18 linux happy
spawn /mnt/ask.sh
Who are you: hello
How old are you: 18
what‘s you class: linux
Are you happy: happy
hello is 18 old study linux and feel happy
例2:用expect编写登入172.25.254.178
#!/usr/bin/expect
set ip [ lindex $argv 0]
spawn ssh [email protected]$ip
set password [ lindex $argv 1 ]
expect {###一个expect回答两个问题###
"yes/no" {send "yes\r";exp_continue}###exp_continue表示如果有这个问题,就回答,没有往下继续###
"password:" {send "$password\r"}
}
interact###表示停留在expect界面###
测试:
[[email protected] ~]# vim /mnt/test2.exp
Connection to 172.25.254.178 closed.
[[email protected] ~]# expect /mnt/test2.exp 172.25.254.178 redhat
spawn ssh [email protected]
[email protected]‘s password:
Last login: Tue Jun 20 23:40:20 2017 from 172.25.254.17
十二 环境变量
shell和脚本使用变量来存储数据 ,有些变量可以连同它们的内容传递给子进程,这些
变量我们称之为环境变量
使用env命令显示所有环境变量
使用set命令现实所有本地定义的shell变量
环境级变量:只针对当前环境生效
用户级变量:只针对用户生效~/.bash_profile
系统级变量:所有用户都生效/etc/profile
在用户登录的时候,会运行全局变量文件/etc/profile,和用户自定义变量文件~/.bash_profile去初始化它们的环境变量。
例:####使用export a=1,能够使bash下的子bash也识别该变量,但是,当该bash关闭后,再次登入后就识别不了
[[email protected] ~]# a=1
[[email protected] ~]# echo $a
1
[[email protected] ~]# vim file
内容:
echo $a
[[email protected] ~]# sh file
[[email protected] ~]# export a=1###使得该变量在该环境下均可以用###
[[email protected] ~]# sh file
1
[[email protected] ~]#
若是想要该用户在该bash关闭再次打开后仍能够识别该变量,则需要在~/.bash_profile下编写export 变量=变量的值,但当你切换用户的时候仍然不能使用该变量,此时就要在/etc/profile下编写
[[email protected] ~]# source .bash_profile ###使文件生效###
[[email protected] ~]# echo $a
1
[[email protected] ~]# tail -n 1 .bash_profile
export a=1
[[email protected] ~]# su - student
[[email protected] ~]$ echo $a
[[email protected] ~]$ exit
logout
[[email protected] ~]# vim /etc/profile
[[email protected] ~]# tail -n 1 /etc/profile
export a=2
[[email protected] ~]# source /etc/profile###在用户级文件和系统级文件均编写的情况下,哪个文件后生效,就使用哪个,一般是用户级的后生效,系统级的先生效###
[[email protected] ~]# echo $a
2
[[email protected] ~]# su - student
Last login: Mon Jun 19 23:59:37 EDT 2017 on pts/1
[[email protected] ~]$ echo $a
2
[[email protected] ~]$ exit
logout
[[email protected] ~]# source .bash_profile
[[email protected] ~]# echo $a
1
[[email protected] ~]# su - student
Last login: Tue Jun 20 00:05:27 EDT 2017 on pts/1
[[email protected] ~]$ echo $a
2
当要执行脚本时,就要写出该脚本的绝对路径,若不想写出绝对路径,可以在~/.bash_profile
,/etc/profile下编写该文件的路径,例:export PATH=$PATH:/mnt
[[email protected] ~]# vim .bash_profile
[[email protected] ~]# tail -n 1 .bash_profile
export PATH=$PATH:/mnt
[[email protected] ~]# cd /mnt/
[[email protected] mnt]# ls
ctrl.user.sh file file.sh password test1.sh test.sh userfile
[[email protected] mnt]# cd
[[email protected] ~]# file.sh
1
[[email protected] ~]# su - student
Last login: Tue Jun 20 22:38:26 EDT 2017 on pts/1
[[email protected] ~]$ file.sh###要想所有用户均生效,在/etc/profile下编写###
bash: file.sh: command not found...
[[email protected] ~]$ exit
logout
[[email protected] ~]# vim /etc/profile
[[email protected] ~]# source /etc/profile
[[email protected] ~]# su - student
Last login: Tue Jun 20 23:03:57 EDT 2017 on pts/1
[[email protected] ~]$ file.sh
2
[[email protected] ~]$
使用别名
alias命令可以用来自定义属于自己的系统命令,写入~/.bashrc 文件永久生效。
查看别名:
[[email protected] ~]# alias
alias cp=‘cp -i‘
alias egrep=‘egrep --color=auto‘
alias fgrep=‘fgrep --color=auto‘
alias grep=‘grep --color=auto‘
alias l.=‘ls -d .* --color=auto‘
alias ll=‘ls -l --color=auto‘
alias ls=‘ls --color=auto‘
alias mv=‘mv -i‘
alias rm=‘rm -i‘
alias which=‘alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde‘
设置别名:
alias mycom=‘echo hello;hostname‘
例:设置cat的别名kan
[[email protected] ~]# alias kan=‘cat‘
[[email protected] ~]# kan /mnt/file.sh
#!/bin/bash
echo $a
例:在文件例编写
[[email protected] ~]# vim /etc/profile
[[email protected] ~]# tail -n 1 /etc/pro
tail: cannot open ‘/etc/pro’ for reading: No such file or directory
[[email protected] ~]# tail -n 1 /etc/profile
alias xie=‘vim‘
[[email protected] ~]# source /etc/profile
[[email protected] ~]# alias
alias cp=‘cp -i‘
alias egrep=‘egrep --color=auto‘
alias fgrep=‘fgrep --color=auto‘
alias grep=‘grep --color=auto‘
alias kan=‘cat‘
alias l.=‘ls -d .* --color=auto‘
alias ll=‘ls -l --color=auto‘
alias ls=‘ls --color=auto‘
alias mv=‘mv -i‘
alias rm=‘rm -i‘
alias which=‘alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde‘
alias xie=‘vim‘
[[email protected] ~]#
删除别名:
unalias mycomm
例:删除别名vim的别名xie
[[email protected] ~]# unalias xie
[[email protected] ~]# alias
alias cp=‘cp -i‘
alias egrep=‘egrep --color=auto‘
alias fgrep=‘fgrep --color=auto‘
alias grep=‘grep --color=auto‘
alias kan=‘cat‘
alias l.=‘ls -d .* --color=auto‘
alias ll=‘ls -l --color=auto‘
alias ls=‘ls --color=auto‘
alias mv=‘mv -i‘
alias rm=‘rm -i‘
alias which=‘alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde‘
[[email protected] ~]# xie file
bash: xie: command not found...
以上是关于bash 脚本的主要内容,如果未能解决你的问题,请参考以下文章
通过 bash 脚本将 bash 脚本添加到 crontab