shell 简单使用

一、执行脚本

1、执行脚本的2种方式:
a、bash script-file
  当脚本文件没有可执行权限时,只能用上面的方式执行。
b、加执行权限然后运行
  当执行一个脚本文件时,shell会产生一个子shell(即一个子进程)去执行脚本文件中的命令。因此,脚本文件中的变量值不能传递到当前shell(即父shell)。所以为了使脚本文件中的变量值传递到当前shell,必须在脚本文件名前加“source”或“.”命令,即source script-file 或. Script-file。
例子changedir.sh

#! /bin/sh
  
cd /home/songbw/shell_example/10_topic

执行结果:

[songbw@qndd shell_example]$ pwd
/home/songbw/shell_example
[songbw@qndd shell_example]$ ./changedir.sh
[songbw@qndd shell_example]$ pwd
/home/songbw/shell_example
[songbw@qndd shell_example]$ source changedir.sh
[songbw@qndd 10_topic]$ pwd
/home/songbw/shell_example/10_topic
[songbw@qndd 10_topic]$

分析:
  脚本 changedir.sh 的作用很简单,就是进入到目录 /home/songbw/shell_example/10_topic 下面,一开始的目录是在 /home/songbw/shell_example 下面,但当执行完 ./changedir.sh 后面,并没有进入到 /home/songbw/shell_example/10_topic 下面,因为它是产生的一个子 shell 在运行,当执行 source changedir.sh 时,就真正的进入到了目录 /home/songbw/shell_example/10_topic

二、判断符号[]用法

  • [] 中的每个组件中间都要用空格分隔
      如[空"$HOME"空==空"$SMALL"空](这两个"空"要特别注意,因为特别容易看不出来)
  • 中括号内的变量,最好用双引号来设置
      因为变量是一种完全的替换,假如上面HOME的值为"nice to meet you!“,SMALL的值为"hello”,则上述为[空"nice to meet you!"空==空"hello"空],如果$HOME没有用双引号,则为[ nice to meet you! == hello ](为了看着方便,把"空"去了),显然会出现 too many arguments的错误
  • 中括号内的常量,最好用单引号或者双引号来设置
  • 在用if进行判断时,if和[]之间必须得有空格,否则识别不了
  • [ “$yn” == “N” -o “$yn” == “n”]
      其中"-o"是或的意思,另外"-a" 是并且的意思

2.1 判断输入参数为空

#!/bin/sh

if [ -n "$1" -a -n "$2" ];then
	echo "songbw!"
else
	echo "interupt!"
fi

执行结果:

[songbw@qndd ~]$ ./collectLibrary.sh
interupt!
[songbw@qndd ~]$ ./collectLibrary.sh 1
interupt!
[songbw@qndd ~]$ ./collectLibrary.sh 1 2
songbw!
[songbw@qndd ~]$

解释:
  -n " $1" -a -n "$2"表示第一个参数和第二个参数都不为空,-a是且的意思,-n是判断变量的值是否为空

2.2 判断参数的个数

#!/bin/bash

if [ $# -eq 0 ] || [ $# -gt 2 ]; then
	echo "help:"
	echo "  ./start.sh start a1   #a1 is an agentname"
 	echo "  ./start.sh stop"
 	exit 0;
fi

执行结果:

[songbw@qndd shell_example]$ ./start.sh
help:
./start.sh start a1 #a1 is an agentname
./start.sh stop
[songbw@qndd shell_example]$

解释:

-eq //等于
-ne //不等于
-gt //大于
-lt //小于
-ge //大于等于
-le //小于等于

特殊变量列表:

变量 含义
$0 当前脚本的文件名
$n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$# 传递给脚本或函数的参数个数。
$* 传递给脚本或函数的所有参数。
$@ 传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同,下面将会讲到。
$? 上个命令的退出状态,或函数的返回值。
$$ 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。

三、 条件判断用法

3.1 单层、简单条件判断

if [ 条件判断式 ];then
	当条件判断成立时,可以执行的内容
fi 固定的,条件判断结束的标志

3.1.1 示例

脚本 empty_or_hello.sh 中的内容如下

#!/bin/sh
if [ "$1" == "hello" ];then 
   echo "fine,how are you!"
fi

运行结果

[songbw@qndd shell_example]$ ./empty_or_hello.sh hello
fine,how are you!
[songbw@qndd shell_example]$ ./empty_or_hello.sh xxx
[songbw@qndd shell_example]$

分析:
  $1代表脚本的第一个参数,如果为hello,则输出 fine,how are you! 否则什么也不做,脚本结束

3.2 多重、复杂条件判断

a、一个条件判断,分成功与失败

if [ 条件判断式 ];then
 当条件判断成立时,可以执行的内容
else
 当条件判断不成立时,可以执行的内容
fi  固定的,条件判断结束的标志

3.2.1 示例

脚本empty_or_hello.sh中的内容:

#!/bin/sh
if [ "$1" == "hello" ];then 
   echo "fine,how are you!"
else 
   echo "The first parameter should be "hello""
fi

执行结果

[songbw@qndd shell_example]$ ./empty_or_hello.sh hello
fine,how are you!
[songbw@qndd shell_example]$ ./empty_or_hello.sh xxx
The first parameter should be hello
[songbw@qndd shell_example]$

解析:
  $1代表脚本的第一个参数,如果为hello,则输出 fine,how are you! 否则输出The first parameter should be hello,脚本结束。

b、多个条件判断(if… elif … elif … else)分多钟不同情况执行

if [ 条件判断时一 ];then
 当条件判断一成立时,可以执行的内容
elif [ 条件判断时二 ];then
 当条件判断二成立时,可以执行的内容
else
 当条件判断一和二都不成立时,可以执行的内容
fi  固定的,条件判断结束的标志

3.2.2 示例

脚本 y_or_no_or_other.sh 中内容如下:

#!/bin/sh
#if you input 'y' or'Y' ,it will print "ok,contine!"
#if you input 'n' or'N' ,it will print "interupt!"
#if you input other,it will print "I canot recognize it"
read -p "please input y or n:" yn
if [ "$yn" == "y" ] || [ "$yn" == "Y" ]; then
	echo "ok,continue"
elif [ "$yn" == "n" ] || [ "$yn" == "N" ]; then
	echo "interupt!"
else
	echo "I cannot recognize it"
fi

执行结果:

[songbw@qndd shell_example]$ ./y_or_no_or_other.sh
please input y or n:y
ok,continue
[songbw@qndd shell_example]$ ./y_or_no_or_other.sh
please input y or n:n
interupt!
[songbw@qndd shell_example]$ ./y_or_no_or_other.sh
please input y or n:helo
I cannot recognize it
[songbw@qndd shell_example]$

解析:
  从显示屏输入值,如果为y则输出ok,continue,如果为n,则输出interupt! 否则输出 I cannot recognize it

四、循环

4.1 while do done,until do done

在这里插入图片描述

  while 的中文是『当…时』,所以,这种方式说的是『 当 condition 条件成立时,就进行循环,直到condition 的条件不成立才停止』的意思。

4.1 try_while.sh

#!/bin/sh
#it will print string all the time until you input "yes"
read  str
while [ "$str" != "yes" ] && [ "$str" != "YES" ]
do
  read str
done

执行结果

[songbw@qndd shell_example]$ ./try_while.sh
mm
xx
xxx
xx
mmm
yes
[songbw@qndd shell_example]$

解析:
  输入值赋值给str,如果这个值不是yes或者YES,就一直输入,直到输入这两个值时,脚本就退出。这种循环还是很叼的,因为它是根据判断条件判断是否进行循环的。
下面还有另一种方式:
在这里插入图片描述
  这种方式恰恰与while 相反,它说的是『 当 condition 条件成立时,就终止循环, 否则就持续运行循环的程序段。 』

4.1.2 示例 try_until.sh

#!/bin/sh
#it will print string all the time until you input "yes"
read  str
until [ "$str" == "yes" ] || [ "$str" == "YES" ]
do
  read str
done

执行结果

[songbw@qndd shell_example]$ ./try_until.sh
mm
xx
yes
[songbw@qndd shell_example]$

解析:
  输入值赋值给str,如果这个值不是yes或者YES,就一直输入,直到输入这两个值时,脚本就执行完毕。这种循环还是很叼的,因为它是根据判断条件判断是否进行循环的,这个和4.1.1实现的功能是一样的,只不过上面用的是while,而这里用的是until而已。

4.2 for…do…done

4.2.1 常规

在这里插入图片描述
第一次循环时,$var的内容为con1

第二次循环时,$var的内容为con2

第三次循环时,$var的内容为con3
……

例子 tryFor.sh

#!/bin/sh
for ip in 192.168.127.11  192.168.127.13 192.168.127.30 192.168.128.7 192.168.128.9 192.168.127.33 192.168.127.34
do
   for kafkcfg in kafkaproducer_20.cfg kafkaproducer_call_detail.cfg kafkaproducer_6.cfg kafkaproducer_r_ags_e.cfg
   do
   	cat >>$ip <<EOF
   	$kafkcfg
EOF
   done
done

解释:
  这是一个双层的循环,循环里面套循环,看了例子会发现,这种循环的用法,好简单,就是将第二个循环中的内容写入以第一个循环中内容为文件名的文件中。
注意:EOF必须得顶头写,要不然就会出错的

执行结果:
结果就不展示了,因为生成了太多的文件

例子 move_more_2k.sh

#!/bin/sh
#move more than 10k to /home/songbw/10_topic
#time: 2016 08 01

###############################################################33

for var in `ls -l|awk '{print $9}'`
do
        echo "$var"
done
echo "Done"

执行结果:

[songbw@qndd shell_example]$ ./move_more_2k.sh
ip_mix
move_more_2k.sh
tryFor.sh
try_until.sh
try_while.sh
while.sh
y_or_no_or_other.sh
Done
[songbw@qndd shell_example]$

解析:
此例子和上一个例子的最大的区别就在于:
for循环操作的集合是由命令 ls - l 和 awk的共同作用得来的,这个更实用些感觉,因为for循环里面的内容大多应该是命令得来的,不是手写的吧

4.2.2 专为数值处理

例子close_All_MP.sh

#!/bin/sh
first_Half_Path="/home/songbw/shell_example"
second_Half_Path="/trunk"
for ((index=1;index<=5;++index))
do
       echo "$first_Half_Path$index$second_Half_Path"
done  

执行结果:

[songbw@qndd shell_example]$ ./close_All_MP.sh
/home/songbw/shell_example1/trunk
/home/songbw/shell_example2/trunk
/home/songbw/shell_example3/trunk
/home/songbw/shell_example4/trunk
/home/songbw/shell_example5/trunk
[songbw@qndd shell_example]$

解析:
  循环20次打印路径
具体用法如下:
在这里插入图片描述
在这里插入图片描述
  值得注意的是,在”执行步阶”的设定上,如果每次增加1,则可以使用类似i++的方式,就是i每次循环都会增加一的意思。

引申-shell算术运算的几个例子
在这里插入图片描述

五、利用case…esac判断

在这里插入图片描述

例子 case_esac.sh

#!/bin/bash

case $1 in 
"hello")
        echo "hello, how are you!"
        ;;
"")
        echo "the first parameter cannot be empty" 
        ;;
*)
        echo "the first parameter is hello"
        ;;
esac  

执行结果:

[songbw@qndd shell_example]$ ./case_esac.sh hello
hello, how are you!
[songbw@qndd shell_example]$ ./case_esac.sh
the first parameter cannot be empty
[songbw@qndd shell_example]$ ./case_esac.sh xxx
the first parameter is hello
[songbw@qndd shell_example]$

说明:
在这里插入图片描述

六、综合条件判断循环事例

示例 generate_Relation.sh

#!/bin/sh
  
FileName="releation.csv"

if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ];then
        echo "you need three parameters,the first is Enterprise ID
        the second is initial value,and the third is the number of rows"
        exit 0
else
        startNumber=$2
        enterpriseID=$1
        rowNumber=$3
fi


for ((i=1;i<=rowNumber;i=i+1))
do
        startNumber=$((startNumber+1))
        cat >>$FileName <<EOF
$enterpriseID,$startNumber,$startNumber,$startNumber,$startNumber,$startNumber
EOF
done

程序执行及结果:

[songbw@qndd shell_example]$ ./generate_Relation.sh qnsoft 33 3
[songbw@qndd shell_example]$ cat releation.csv
qnsoft,34,34,34,34,34
qnsoft,35,35,35,35,35
qnsoft,36,36,36,36,36
[songbw@qndd shell_example]$

说明:
  代码其实很简单,只是这里面既有 if 判断,又有for循环

七、写 shell 用到

7.1 EOF

在shell编程中,经常会用到EOF,那么它的作用是什么呢?
  在shell脚本中,通常将EOF与 << 结合使用,表示后续的输入作为子命令或子Shell的输入,直到遇到EOF为止,再返回到主Shell。
  EOF只是一个分界符,当然也可以用abcde替换。当shell遇到<<时,它知道下一个词是一个分界符。在该分界符以后的内容都被当作输入,直到shell又看到该分界符(位于单独的一行)。
  此分界符可以是所定义的任何字符串,其实,不一定要用EOF,只要是“内容段”中没有出现的字符串,都可以用来替代EOF,完全可以换成abcde之类的字符串,只是一个起始和结束的标志罢了。
例子 detect_Time_xxx.sh

#!/bin/sh
date
mongo <<EOF >sucess.txt
use runoob
document={"agent_id":"90011057","status":101,"start_time":"1482570954","duration":23,"end_time":"1482570977","channel_type":0,"call_type":1,"session_id":"6726229658121366153","local_url":"","remote_url":"","skill_id":"1000000595","area_id":"","region_id":"0826","agent_dn":"SIP:90011057","agent_name":"90011057","reason":0,"campaign_id":"","skill_type":"","ent_id":"0101290011","state_num":159,"is_handled":0,"global_serial_num":1,"main_serviceid":""}
db.col.insert(document)
db.col.insert(document)
db.col.insert(document)
exit
EOF
date

说明:
  date和mongo都是主shell中的命令,当我在主shell中执行mongo命令时,就连上了mongo数据库的服务端,此时进入的是mongo的客户端,后来的先转到runoob数据库,然后定义一个document文档,接着在col集合中插入此文档,最后退出客户端都是在mongo客户端中进行的,如果不利用<<EOF分界,我们根本进不到mongo客户端,只有利用了<<EOF 开始和EOF结束,中间部分的命令才代表在mongo客户端中执行的命令,当出了EOF之后,后面的命令又是在主shell中了,所以我觉得
<<EOF
EOF
简直太强大了。
  sucess.txt 中存的是执行 mongo 的操作命令的结果

执行结果:

[songbw@hadoop detect_StorageTime_Of_Mongo]$ ./detect_Time_xxx.sh
2020年 12月 18日 星期五 16:42:30 CST
2020年 12月 18日 星期五 16:42:30 CST

查看 sucess.txt 中内容

[songbw@hadoop detect_StorageTime_Of_Mongo]$ cat sucess.txt
MongoDB shell version: 3.2.0
connecting to: test
switched to db runoob
{
“agent_id” : “90011057”,
“status” : 101,
“start_time” : “1482570954”,
“duration” : 23,
“end_time” : “1482570977”,
“channel_type” : 0,
“call_type” : 1,
“session_id” : “6726229658121366153”,
“local_url” : “”,
“remote_url” : “”,
“skill_id” : “1000000595”,
“area_id” : “”,
“region_id” : “0826”,
“agent_dn” : “SIP:90011057”,
“agent_name” : “90011057”,
“reason” : 0,
“campaign_id” : “”,
“skill_type” : “”,
“ent_id” : “0101290011”,
“state_num” : 159,
“is_handled” : 0,
“global_serial_num” : 1,
“main_serviceid” : “”
}
WriteResult({ “nInserted” : 1 })
WriteResult({ “nInserted” : 1 })
WriteResult({ “nInserted” : 1 })
bye
[songbw@hadoop detect_StorageTime_Of_Mongo]$

例子 generate_Insert_xxx.sh

#!/bin/sh
for ((i=1;i<=5;i=i+1))
do
cat >> xxxx.sh <<EOF
db.col.insert(document)
EOF
done  

说明:
外围是主shell中的一个循环,里面是

cat >> detect_Time.sh <<EOF
db.col.insert(document)
EOF

  因为cat有创建文件的功能,写的东西重定向到xxxx.sh中,但必须得有个
<<EOF
EOF
好让脚本中能进到子shell中,同时有了EOF标志之后也可以出来到子shell中,自己可以再 shell 中试一下就知道了

执行结果

[songbw@hadoop detect_StorageTime_Of_Mongo]$ ./generate_Insert_xxx.sh
[songbw@hadoop detect_StorageTime_Of_Mongo]$ cat xxxx.sh
db.col.insert(document)
db.col.insert(document)
db.col.insert(document)
db.col.insert(document)
db.col.insert(document)
[songbw@hadoop detect_StorageTime_Of_Mongo]$

引出一个问题:
子shell中能否用for循环呢?
  先给出答案,我自己觉得是不能的,因为尝试了许多,感觉不好使啊
  例子detect_Time_xxx.sh中我要测mongo插入那个document的速度,我是怎么做的呢,比如我要插入5000条document,我是在<<EOF和EOF中写5000条 db.col.insert(document),当然可以像例子generate_Insert_xxx.sh中那样实现,但是我要测的是document入mongo的速度,如果不断的连客户端,再退出,应该会浪费不少时间啊,如果能不退出客户端循环执行 db.col.insert(document),那简直太棒了
  而例子generate_Insert_xxx.sh并不考虑速度,所以在主shell中执行循环是没有问题的。 ‘
  还想到一种情况,比如 mysql 或者 oracle,你可以生成一个 .sql的文件,然后我直接执行这个文件就行了,记得 oralce 或者 mysql 是支持这样的

7.2 alias

  复杂的命令重命名,使得使用起来更简单
在这里插入图片描述

我的机器

(base) desktop-6rkqqec:data songbw$ alias
alias aoel=‘/Users/songbw/auto_login/login.sh’
alias ll=‘ls -l’
(base) desktop-6rkqqec:data songbw$

八、shell的追踪与调试

sh [-nvx] scripts.sh
-n: 不要执行脚本,仅查询语法问题
-v: 在执行脚本前,先将脚本的内容输出到屏幕上
-x: 将脚本的执行过程全部列出来(代码是什么,然后输出什么,写的相当清楚)

例子move_more_150.sh

#! /bin/sh  
  
filename=`ls -l /home/songbw/shell_example/|awk '$5>150{print $9}'`
for file in $filename
do
        echo $file
        cp $file /home/songbw/shell_example/10_topic
done  

说明:
脚本 move_more_150.sh 是放在 /home/songbw/shell_example/10_topic 下面

执行结果:

[songbw@qndd 10_topic]$ ./move_more_150.sh
case_esac.sh
cp: 无法获取’case_esac.sh’ 的文件状态(stat): 没有那个文件或目录
close_All_MP.sh
cp: 无法获取’close_All_MP.sh’ 的文件状态(stat): 没有那个文件或目录
generate_Relation.sh
cp: 无法获取’generate_Relation.sh’ 的文件状态(stat): 没有那个文件或目录

  这么看可能会有点懵逼,如果用如下调试的方法执行,会看的更清楚

[songbw@qndd 10_topic]$ sh -x move_more_150.sh 
++ ls -l /home/songbw/shell_example/
++ awk '$5>150{print $9}'
+ filename='case_esac.sh
close_All_MP.sh
generate_Relation.sh'
+ for file in $filename
+ echo case_esac.sh
case_esac.sh
+ cp case_esac.sh /home/songbw/shell_example/10_topic
cp: 无法获取'case_esac.sh' 的文件状态(stat): 没有那个文件或目录
+ for file in $filename
+ echo close_All_MP.sh
close_All_MP.sh
+ cp close_All_MP.sh /home/songbw/shell_example/10_topic
cp: 无法获取'close_All_MP.sh' 的文件状态(stat): 没有那个文件或目录
+ for file in $filename
+ echo generate_Relation.sh
generate_Relation.sh
+ cp generate_Relation.sh /home/songbw/shell_example/10_topic
cp: 无法获取'generate_Relation.sh' 的文件状态(stat): 没有那个文件或目录
[songbw@qndd 10_topic]$      

执行

cp case_esac.sh /home/songbw/shell_example/10_topic

出现

cp: 无法获取’case_esac.sh’ 的文件状态(stat): 没有那个文件或目录

  因为脚本是在 /home/songbw/shell_example/10_topic 下面,而 文件 case_esac.sh 不在这个目录下面,是在上一层目录,自然无法cp

参考文献:
鸟哥的私房菜

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值