Shell 脚本
Shell 脚本(shell script),是一种为 shell 编写的脚本程序。
业界所说的 shell 通常都是指 shell 脚本,但读者朋友要知道,shell 和 shell script 是两个不同的概念。
由于习惯的原因,简洁起见,本文出现的 “shell编程” 都是指 shell 脚本编程,不是指开发 shell 自身。
常见基础命令:
单引 双引 反引用[] [[]]
将命令的输出读入一个变量中,可以将它放入双引号中,即可保留空格和换行符(\n)
out=$(cat text.txt)
输出1 2 3
out="$(cat text.txt)"
输出:
1
2
3
逻辑运算符:
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于
逻辑与-a
[ $var1 -eq 0 -a $var2 -gt 2 ]
逻辑或
[ $var1 -eq 0 -o $var2 -gt 2 ]
[ condition ] && action 等价于if…fi
if [ “$LOOK_OUT” -gt “85” ]
if [ -e /home/slynux ]; then
…
fi
-----------------[[]]一般用于字符串比较
if [[ -n $str1 ]] && [[ -z $str2 ]] ;
then
commands;
fi
========================
1、字符串判断
str1 = str2 当两个串有相同内容、长度时为真
str1 != str2 当串str1和str2不等时为真
-n str1 当串的长度大于0时为真(串非空) if [[ -n $1 ]]
-z str1 当串的长度为0时为真(空串)
str1 当串str1为非空时为真
2、数字的判断
int1 -eq int2 两数相等为真
int1 -ne int2 两数不等为真
int1 -gt int2 int1大于int2为真
int1 -ge int2 int1大于等于int2为真
int1 -lt int2 int1小于int2为真
int1 -le int2 int1小于等于int2为真
3 目录文件的判断(if [ ])
-r file 用户可读为真
-w file 用户可写为真
-x file 用户可执行为真
-f file 文件为正规文件为真
-d file 文件为目录为真
-c file 文件为字符特殊文件为真
-b file 文件为块特殊文件为真
-s file 文件大小非0时为真
-t file 当文件描述符(默认为1)指定的设备为终端时为真
3、复杂逻辑判断
-a 与
-o 或
! 非
下面是一些使用实例:
#!/bin/sh
myPath="/var/log/httpd/"
myFile="/var /log/httpd/access.log"
#这里的-x 参数判断
m
y
P
a
t
h
是
否
存
在
并
且
是
否
具
有
可
执
行
权
限
i
f
[
!
−
x
"
myPath是否存在并且是否具有可执行权限 if [ ! -x "
myPath是否存在并且是否具有可执行权限if[!−x"myPath"]; then
mkdir “$myPath”
fi
#这里的-d 参数判断
m
y
P
a
t
h
是
否
存
在
i
f
[
!
−
d
"
myPath是否存在 if [ ! -d "
myPath是否存在if[!−d"myPath"]; then
mkdir “$myPath”
fi
#这里的-f参数判断
m
y
F
i
l
e
是
否
存
在
i
f
[
!
−
f
"
myFile是否存在 if [ ! -f "
myFile是否存在if[!−f"myFile" ]; then
touch “$myFile”
fi
#其他参数还有-n,-n是判断一个变量是否是否有值
if [ ! -n “
m
y
V
a
r
"
]
;
t
h
e
n
e
c
h
o
"
myVar" ]; then echo "
myVar"];thenecho"myVar is empty”
exit 0
fi
#两个变量判断是否相等
if [ “
v
a
r
1
"
=
=
"
var1" == "
var1"=="var2” ]; then
echo ‘$var1 eq
v
a
r
2
′
e
l
s
e
e
c
h
o
′
var2' else echo '
var2′elseecho′var1 not eq $var2’
fi
-----------------获取名称.扩展名
file_jpg=“sample.jpg”
name=${file_jpg%.*}
输出sample
file_jpg=“sample.jpg”
extension=${file_jpg#*.}
输出jpg
------------------ time 计算命令执行时间
time command
--------------- 重定向
0 stdin标准输入
1 stdout标准输出
2 stderr标准错误
文件打开模式:
等同于1> 截断模式
等同于1>> 追加模式
<用于从文件中读取至stdin 只读模式
ls + 2> out.txt
stdout不会有任何输出,因为错误已经重定向到out.txt中了
可以将stderr单独重定向到一个文件,将stdout重定向到另一个文件:
cmd 2>stderr.txt 1>stdout.txt
将stderr转换成stdout,使得stderr和stdout都被重定向到同一个文件:
cmd 2>&1 output.txt
或者
cmd &> output.txt
将stderr输出丢弃
some_command 2> /dev/null
将文件重定向到命令
cmd < file
向log文件中写入头部数据
#!/bin/bash
cat <log…tt
LOG FILE HEADER
this is a test log file
EOF
-----------------------/dev/null 2>&1
*/1 * * * * root /usr/local/php/bin/php /var/w.php > /dev/null 2>&1
crontab内容:50 18 5-30 * * /script/myscript.sh 1> /dev/null 2>&1
其中 1> /dev/null 2>&1是什么意思??
dev/null 为系统垃圾箱
&为后台运行
但是 myscript 后面的1 和 /null后面的2 还有 &后面的1是什么意思?
1代表标准输出,2代表错误信息输出.
1>/dev/null 就是指将标准输出定向到空设备,
2>&1,的意思是将错误输出定向到和1一样的输出设备,也同样是空.
换句话说,就是不显示该程序执行过程中的任何信息
cmd >a 2>a 和 cmd >a 2>&1 为什么不同?
cmd >a 2>a :stdout和stderr都直接送往文件 a ,a文件会被打开两遍 ,由此导致stdout和stderr互相覆盖。
cmd >a 2>&1 :stdout直接送往文件a ,stderr是继承了FD1的管道之后,再被送往文件a 。a文件只被打开一遍,就是FD1将其打开
他们的不同点在于:
cmd >a 2>a 相当于使用了FD1、FD2两个互相竞争使用文件 a 的管道;
而cmd >a 2>&1 只使用了一个管道FD1, 但已经包括了stdout和stderr。
从IO效率上来讲,cmd >a 2>&1的效率更高。
----------------- 算数运算 let expr
let 可以直接执行基本的算数操作
no1=4
no2=5
let result=no1+no2
echo $result
let no1++
let no1+=6
操作符[] ,expr 和let命令类似
result=$[ no1 + no2 ]
result=$[ $no1 + $no2 ]
result=expr 3 + 4
result=$(expr $no1 + 5)
---------------- 浮点运算 bc
#echo “4 * 0.56” | bc
2.24
-----------------获得字符串长度
var=123456
echo ${#var}
-----------------环境变量$PATH和export
echo $PATH
PATH通常定义在/etc/environment或/etc/profile或~/.bashrc中
export命令用来设置环境变量;
添加新的路径:
export PATH="$PATH:/home/user/bin"
或者
PATH="$PATH:/home/user/bin"
export PATH
其它环境变量:HOME,PWD,USER,UID,SHELL
查看进程相关的环境变量:
cat /proc/$PID/environ
----------------- printf 格式化输出
printf “%-5s %-10s %-4s\n” No Name Mark
printf “%-5s %-10s %-4.2f\n” 1 James 91.32
输出为:
No Name Mark
1 James 91.32
%-5s 指明了一个格式为左对齐且宽度为5的字符串替代(- 表示左对齐)
-4.2f 表示对浮点数的处理格式
-------------- 读取命令返回值$?
cmd;
echo $?;
-------------------------shell 调试
#!/bin/bash -xv 不用任何其他选项就可以启用调试功能了
sh -n sh16.sh 不执行script,仅查询语法
sh -x sh16.sh 将script执行过程全部列出来
需要给变量赋值时,可以这么写:
变量名=值
要取用一个变量的值,只需在变量名前面加一个$
a=“hello world”
echo “A is:” $a
//if 注意空格
a=$1
if [[ $a -eq 2 ]] ;then
echo “1”
else
echo “2”
fi
if [[ $a = gjslint ]] ;then
echo “1”
exit 0
else
echo “2”
fi
exit 0
===================== 调用php的sh
#!/bin/bash
if [[ $0 = /* ]]
then
curfile="$0"
else
curfile=" P W D / PWD/ PWD/{0#./}"
fi
#得到curfile 为/usr/local/shell/automation/autoupdate_host.sh
php_path=dirname $curfile
#得到php_path为/usr/local/shell/automation
PHP="/usr/local/php/bin/php -q "
PROGRAM="${php_path}/clear_his.php"
#echo $PHP $PROGRAM &
$PHP $PROGRAM &
====================== [和[[有什么不同
$ type [
[ is a shell builtin
$ type [[
[[ is a shell keyword
也就是说[处理里面的字串是当作参数来处理的,而[[对待其中的字串是当作表达式来处理的
那么当作参数和表达式有什么不同呢?
表达式中不会有wordsplitting 或者glob expansion,而参数处理会有
$ ls
file file 1 #注意是2个文件(file 和file 1)
$ (foo=“file 1”;[[ -f KaTeX parse error: Expected 'EOF', got '&' at position 7: foo ]]&̲&echo "foo is a file")
file 1 is a file
]$ (foo=“file 1”;[ -f KaTeX parse error: Expected 'EOF', got '&' at position 6: foo ]&̲&echo "foo is a file") # 这里file 1被分成2个word,所以出错
bash: [: file: binary operator expected
再来看看glob expansion
$ touch '’
$ (foo="";[ -f KaTeX parse error: Expected 'EOF', got '&' at position 6: foo ]&̲&echo "foo is a file") #为什么显示too many arguments,因为 被扩展为所有目录下的文件
bash: [: too many arguments
$ (foo="";[[ -f KaTeX parse error: Expected 'EOF', got '&' at position 7: foo ]]&̲&echo "foo is a file") # *被当成普通字符了
- is a file
参数传递中<和>会被解析成重定向符号,所以必须转义掉
$ ([ “s” < “l” ]&&echo yes) #错误使用
bash: l: No such file or directory
$ ([ “s” > “l” ] &&echo yes) #正确使用
yes
$ ([[ “s” > “l” ]] &&echo yes) #而在表达式中比较符号不会被当作重定向符号
yes
参数传递中小括号会被分割成token,而在表达式中则会被解析成运算顺序
$ ([ “s” > “l” -a ( file “l” > “a” -o “l” > “p” ) ]&&echo yes) #(和)必须被转义,以避免参数解析中的不正确分词
bash: syntax error near unexpected token `(’
$ ([ “s” > “l” -a “l”“a"−o"l”“p” ] &&echo yes)
yes
$ ([[ “s” > “l” && ( “l” > “a” || “l” > “p” ) ]] &&echo yes; ) #而表达式则不需要考虑这个
yes
================ shell判断文件是否存在
shell判断文件,目录是否存在或者具有权限
#!/bin/sh
myPath="/var/log/httpd/"
myFile="/var /log/httpd/access.log"
这里的-x 参数判断$myPath是否存在并且是否具有可执行权限
if [ ! -x “
m
y
P
a
t
h
"
]
;
t
h
e
n
m
k
d
i
r
"
myPath"]; then mkdir "
myPath"];thenmkdir"myPath”
fi
这里的-d 参数判断$myPath是否存在
if [ ! -d “
m
y
P
a
t
h
"
]
;
t
h
e
n
m
k
d
i
r
"
myPath"]; then mkdir "
myPath"];thenmkdir"myPath”
fi
这里的-f参数判断$myFile是否存在
if [ ! -f “
m
y
F
i
l
e
"
]
;
t
h
e
n
t
o
u
c
h
"
myFile" ]; then touch "
myFile"];thentouch"myFile”
fi
其他参数还有-n,-n是判断一个变量是否是否有值
if [ ! -n “
m
y
V
a
r
"
]
;
t
h
e
n
e
c
h
o
"
myVar" ]; then echo "
myVar"];thenecho"myVar is empty”
exit 0
fi
两个变量判断是否相等
if [ “
v
a
r
1
"
=
"
var1" = "
var1"="var2” ]; then
echo ‘$var1 eq
v
a
r
2
′
e
l
s
e
e
c
h
o
′
var2' else echo '
var2′elseecho′var1 not eq $var2’
fi
-f 和-e的区别
Conditional Logic on Files
-a file exists.
-b file exists and is a block special file.
-c file exists and is a character special file.
-d file exists and is a directory.
-e file exists (just the same as -a).
-f file exists and is a regular file.
-g file exists and has its setgid(2) bit set.
-G file exists and has the same group ID as this process.
-k file exists and has its sticky bit set.
-L file exists and is a symbolic link.
-n string length is not zero.
-o Named option is set on.
-O file exists and is owned by the user ID of this process.
-p file exists and is a first in, first out (FIFO) special file or named pipe.
-r file exists and is readable by the current process.
-s file exists and has a size greater than zero.
-S file exists and is a socket.
-t file descriptor number fildes is open and associated with aterminal device.
-u file exists and has its setuid(2) bit set.
-w file exists and is writable by the current process.
-x file exists and is executable by the current process.
-z string length is zero.
==================bash中的特殊符号
- 万用字符,代表一个或多个字符(或数字)
? 万用字符,代表一个字母
| 分隔两个管线命令的界定;
-
; 连续性命令的界定(注意!与管线命令并不相同)
- 使用者的家目录
$ 亦即是变量之前需要加的变量取代值
& 将指令变成背景下工作
! 逻辑运算意义上的『非』 not 的意思!
/ 路径分隔的符号
, >> 输出导向,分别是『取代』与『累加』
’ 单引号,不具有变量置换的功能
" 具有变量置换的功能!
两个『 ` 』中间为可以先执行的指令!
( ) 在中间为子 shell 的起始与结束
[ ] 在中间为字符的组合
{ } 在中间为命令区块的组合!
exit 1:退出整个程序
--------------------dirname
dirname /home/bin/abc
得到/home/bin
------------------------- 循环读取每行 :
all_corp=sql.txt
cat $all_corp | while read line
do
echo $line
done
-----------------------整数比较
-eq 等于,如:if [ “
a
"
−
e
q
"
a" -eq "
a"−eq"b” ]
-ne 不等于,如:if [ “
a
"
−
n
e
"
a" -ne "
a"−ne"b” ]
-gt 大于,如:if [ “
a
"
−
g
t
"
a" -gt "
a"−gt"b” ]
-ge 大于等于,如:if [ “
a
"
−
g
e
"
a" -ge "
a"−ge"b” ]
-lt 小于,如:if [ “
a
"
−
l
t
"
a" -lt "
a"−lt"b” ]
-le 小于等于,如:if [ “
a
"
−
l
e
"
a" -le "
a"−le"b” ]
if [ $counter -gt 1 ]; then
…
fi
< 小于(需要双括号),如:((“
a
"
<
"
a" < "
a"<"b”))
<= 小于等于(需要双括号),如:((“
a
"
<
=
"
a" <= "
a"<="b”))
大于(需要双括号),如:((“ a " > " a" > " a">"b”))
= 大于等于(需要双括号),如:((“ a " > = " a" >= " a">="b”))
---------------------------字符串比较
= 等于,如:if [ “
a
"
=
"
a" = "
a"="b” ]
== 等于,如:if [ “
a
"
=
=
"
a" == "
a"=="b” ],与=等价
注意:==的功能在[[]]和[]中的行为是不同的,如下:
1 [[ KaTeX parse error: Expected 'EOF', got '#' at position 12: a == z* ]] #̲ 如果a以"z"开头(模式匹配 )那么将为true
2 [ KaTeX parse error: Expected 'EOF', got '#' at position 13: a == "z*" ] #̲ 如果a等于z*(字符匹配 ),那么结果为true
3
4 [ KaTeX parse error: Expected 'EOF', got '#' at position 11: a == z* ] #̲ File globbing …a" == “z*” ] # 如果
a
等
于
z
∗
(
字
符
匹
配
)
,
那
么
结
果
为
t
r
u
e
一
点
解
释
,
关
于
F
i
l
e
g
l
o
b
b
i
n
g
是
一
种
关
于
文
件
的
速
记
法
,
比
如
"
∗
.
c
"
就
是
,
再
如
也
是
.
但
是
f
i
l
e
g
l
o
b
b
i
n
g
并
不
是
严
格
的
正
则
表
达
式
,
虽
然
绝
大
多
数
情
况
下
结
构
比
较
像
.
!
=
不
等
于
,
如
:
i
f
[
"
a等于z*(字符匹配),那么结果为true 一点解释,关于File globbing是一种关于文件的速记法,比如"*.c"就是,再如~也是. 但是file globbing并不是严格的正则表达式,虽然绝大多数情况下结构比较像. != 不等于,如:if [ "
a等于z∗(字符匹配),那么结果为true一点解释,关于Fileglobbing是一种关于文件的速记法,比如"∗.c"就是,再如 也是.但是fileglobbing并不是严格的正则表达式,虽然绝大多数情况下结构比较像.!=不等于,如:if["a" != “
b
"
]
这
个
操
作
符
将
在
[
[
]
]
结
构
中
使
用
模
式
匹
配
.
<
小
于
,
在
A
S
C
I
I
字
母
顺
序
下
.
如
:
i
f
[
[
"
b" ] 这个操作符将在[[]]结构中使用模式匹配. < 小于,在ASCII字母顺序下.如: if [[ "
b"]这个操作符将在[[]]结构中使用模式匹配.<小于,在ASCII字母顺序下.如:if[["a” < “
b
"
]
]
i
f
[
"
b" ]] if [ "
b"]]if["a” < “$b” ]
注意:在[]结构中"<"需要被转义.
大于,在ASCII字母顺序下.如:
if [[ “ a " > " a" > " a">"b” ]]
if [ “ a " " a" \> " a""b” ]
注意:在[]结构中">“需要被转义.
具体参考Example 26-11来查看这个操作符应用的例子.
-z 字符串为"null”.就是长度为0.
-n 字符串不为"null"
注意:
使用-n在[]结构中测试必须要用"“把变量引起来.使用一个未被”“的字符串来使用! -z
或者就是未用”“引用的字符串本身,放到[]结构中。虽然一般情况下可
以工作,但这是不安全的.习惯于使用”"来测试字符串是一种好习惯.
awk ‘{print $2}’ class.txt | grep ‘1’ > res
iptables 配置文件目录 /etc/sysconfig/iptables
1.别名 alias,存放位置 $HOME/.bashrc
alias ll=‘ls -lh’
2.print=helloworld echo ${print}
3.变量:内存中一块存储单元
本地变量:登陆登出生命周期内有效,只限制于本用户
变量名=变量值
set 查看
LOCAL=“TEST”
echo $LOCAL
设置变量名为只读,不能在更改,同是也不能在恢复:readonly 变量名,不能删除。
环境变量:系统变量,用于所有用户,只有环境变量才能用于所有的子进程中,本地变量不可以。
存放于/etc/profile .bash_profile
export 变量名=变量值
env或者export查看
unset 变量名 删除
变量替换:用变量的值替换变量的名
print=helloworld
echo ${print}
echo
f
i
l
e
1
+
{file1}+
file1+{file2}
--------------------位置变量:$0 $1…$9
$0:脚本的名字
$1:脚本的第一变量
$2:脚本的第二变量
向脚本中使用位置参数:./test.sh a b c
向系统命令传递参数
find /root/ -name $1 -print
./test.sh aa
标准变量:bash默认的,可以在/etc/profile中定义
EXINIT
HOME
IFS:设置分隔符,默认为空格 IFS=":"SHE
LOGNAME
MAIL 默认邮箱位置
MAILPATH多个邮箱时可以设置
TERM 显示终端类型
PS1 当前shell目录格式PS1=“WANGJIAN:”
ps2 >
pwd显示当前路径 SHELL 显示当前shell
MANPATH TERMINFO
----------------------------------------------------------特殊变量
$# 变量的个数 $* 显示脚本全部参数(参数列表)
$$脚本运行的当前id号
$?显示前一个命令的运行状态
$!后台运行的最后一个进程id号
#!/bin/bash
echo “tesh.sh”
echo “this is first variable locate:$1”
echo “this is second variable locate:$2”
echo “this is third variable locate:
3
"
s
h
i
f
t
e
c
h
o
"
c
o
u
n
t
:
3" shift echo "count:
3"shiftecho"count:#”
echo “all list:
∗
"
e
c
h
o
"
p
i
d
:
*" echo "pid:
∗"echo"pid:
"
e
c
h
o
"
s
t
a
t
u
s
:
" echo "status:
"echo"status:?”
./tesh.sh a b c
declare :设置或显示变量 -f只显示函数名
export:创建环境变量 -p显示所有环境变量
readonly:设置只读变量,不能修改删除
unset:取消变量的定义。-f 删除只读变量
shift:输入的位置变量改变位置 shift 表位置上移一个位置,shift 2 表上移动两个位置
双引号"":引用字符或字符串除$ \ 单引号\'\':直接引用为字符或字符串 反引号``: 作为系统命令执行 echo
echo wangjian` wangjian
反斜杆:转义特殊字符(KaTeX parse error: Expected 'EOF', got '&' at position 26: …算符:~取反 << >>移位 &̲与(同1为1,否0) |或(有…[]:表示对其中的表达式求值 echo $[2+8] echo $[3&4] $[]等价于(())
[base#n] n(base>n)表示基数从2到36的任何基数 echo [10#8+1] 结果为9
let a+=3 a=a+3
表达式优先级别[] *= || && | ^ & ==
shell 的输入与输出
echo
-e 解析转义字符 echo -e “this is a bag \n\n\n”
-n 回车不换行,默认换行 echo -n “this is a cat”
转义字符(\c回车不换行 \f禁止 \t跳格相当tab \n回车换行)
read:可以从键盘或文件的某一行文本中读入信息,并将其赋给一个变量。
read varible1 varible2
如果只指定了一个变量,那么read将会把所有的输入赋给改变量,直至遇到文件结束或回车。如果多个变量,就依次赋给。shell用空格作为变量之间的分隔符。
echo -n “first name:”
read firstname
echo -n “last name:”
read lastname
echo -e "your first name :KaTeX parse error: Undefined control sequence: \n at position 12: {firstname}\̲n̲" echo -e "your…{lastname}\n "
read -t 5 variable 5秒钟超时
read -p "Please enter your Username: " user -p prompt 提示语
[root@ceshiji ~]#Please enter your Username:
read -s -p "Please enter your Password: " pass -s charaters are not echoed. 字符不回显示。指输入之后,不在回显
0-9. ↩︎