bash/shell编程学习(2)

先来复习上节重定向的用法:

1.快速清空文件

cat demo.txt < /dev/null

注:linux中有一个经典名言【一切皆文件】,/dev/null可以认为是一个特殊的空文件,更形象点,可以理解为科幻片中的黑洞,任何信息重向定输出到它后,便有去无回,当然黑洞里也没有信息能出来。

综合来讲,上面的意思就是利用<将黑洞做为demo.txt的标准输入,黑洞里没任何内容,任何文件里的内容被它吞噬了,自然也没就没东西能剩下了,所以最终就是demo.txt被黑洞洗空了。

/dev/null 还有其它用法,比如用它可以让nohup不生成nohup.out文件,见:http://www.cnblogs.com/yjmyzz/p/4831182.html 

 

2.执行时输出源码

#!/bin/bash -v
printf '%0.2f\n' 12.12334

执行结果如下:

#!/bin/bash -v
printf '%0.2f\n' 12.12334
12.12

注意:第3行输出结果之前,把源码也打印出来了,秘密在于第1行最后的 -v 参数 

 

3.调试模式

#!/bin/bash -x
printf '%0.2f\n' 12.12334
echo 'hello'

执行结果如下:

+ printf '%0.2f\n' 12.12334
12.12
+ echo hello
hello

注意:第一行后面的参数变成了-x,加上这个后,执行时,每一行代码在执行前,会先输出对应的源码,并且以+开头,十分方便调试。  

 

4. if与test及[]

4.1 数字判断

#!/bin/bash -x
i=$1 #变量i的值取第1个参数的值
if test $i -gt 89; then #如果i>89
  echo 'A'
elif test $i -gt 79; then #如果i>79
  echo 'B'
elif test $i -eq 60 -o $i -gt 60;then #如果i=60或i>60(即:i>=60)
  echo 'C'
elif test $i -gt 0;then #如果i>0
  echo 'D'
elif test $i -lt 0;then #如果i<0
  echo 'invalid'
else #i==0的情况
  echo 'zero'
fi

注:if test 条件; then 语句 fi 这是基本格式,注意条件后的;不可省略,另外结束符号是fi(即:把if倒过来,有点回文的理念),另外要记住一堆缩写

-lt 即-Less Than的缩写,表示小于

-gt 即-Greater Than的缩写,表示大于

-eq 即-equal的缩写,表示等于,此外还有

-ne 即-Not Equal的缩写,表示不等于

-o 即-or,表示前后二个逻辑判断是『或』的关系,类似的

-a 即-and,表示前后二个逻辑判断是『与』的关系

elif 即else if的缩写 

上面的示例运行结果:

./demo.sh 90
+ i=90
+ test 90 -gt 89
+ echo A
A

test语句还有一个简化的写法,即把"test 条件"变成" [ 条件 ] ",注意二端的方括号左右都要加一个空格,所以上面的写法可以改成:

i=$1
if [ $i -gt 89 ]; then
  echo 'A'
elif [ $i -gt 79 ]; then
  echo 'B'
elif [ $i -eq 60 -o $i -gt 60 ]; then
  echo 'C'
elif [ $i -gt 0 ]; then
  echo 'D'
elif [ $i -lt 0 ]; then
  echo 'invalid'
else
  echo 'zero'
fi

这样看起来就美观多了,如果不喜欢-o这种逻辑或的写法,第6行也可以换成这样

elif [ $i -eq 60 ] || [ $i -gt 60 ]; then

但是执行的细节略有区别,在调试模式下可以对比下,用||写法的输入(测试用例:61)

./demo2.sh 61
+ i=61
+ '[' 61 -gt 89 ']'
+ '[' 61 -gt 79 ']'
+ '[' 61 -eq 60 ']'
+ '[' 61 -gt 60 ']'
+ echo C
C

而用-o写法的输出:

./demo2.sh 61
+ i=61
+ '[' 61 -gt 89 ']'
+ '[' 61 -gt 79 ']'
+ '[' 61 -eq 60 -o 61 -gt 60 ']'
+ echo C
C

对比下5-6行可以发现,区别在于判断一次,还是判断二次 

4.2 字符串判断

#!/bin/bash -x
str1="abc"
if [ -z "$str1" ]; then
  echo 'str1 is empty'
else
  echo 'str1 is not empty'
fi

printf "\n"

str2=""
if [ -n "$str2" ]; then
  echo 'str2 is not empty'
else
  echo 'str2 is empty'
fi

printf "\n"

if [ "$str1" = "$str2" ]; then
  echo 'str1 = str2'
else
  echo 'str1 <> str2'
fi

注: -n即-not empty判断字符串非空,-z即-zero判断字符串为空,=判断字符串相同(判断字符串时,记得要加双引号)

运行结果:

+ str1=abc
+ '[' -z abc ']'
+ echo 'str1 is not empty'
str1 is not empty
+ printf '\n'

+ str2=
+ '[' -n '' ']'
+ echo 'str2 is empty'
str2 is empty
+ printf '\n'

+ '[' abc = '' ']'
+ echo 'str1 <> str2'
str1 <> str2

4.3 文件及目录判断

#!/bin/bash -x
if [ -f ~/.bash_profile ]; then
  echo '~/.bash_profile is a file'
else
  echo '~/.bash_profile is not a file'
fi

printf '\n'

if [ -d ~/ ]; then
  echo '~/ is a directory'
else
  echo '~/ is not a directory'
fi

-f即判断是否为file, -d即判断是否为directory, 输出结果:

+ '[' -f /Users/yjmyzz/.bash_profile ']'
+ echo '~/.bash_profile is a file'
~/.bash_profile is a file
+ printf '\n'

+ '[' -d /Users/yjmyzz/ ']'
+ echo '~/ is a directory'
~/ is a directory

 

5.命令列表

命令1 && 命令2

解释:如果命令1返回成功,则命令2会执行,示例:

#!/bin/bash
ping -c 4 $1 && printf '\n==== %s connected ====\n' $1

将上面这段保存成testurl.sh,然后chmod +x testurl.sh,执行效果如下:

./testurl.sh www.baidu.com
PING www.a.shifen.com (115.239.211.112): 56 data bytes
64 bytes from 115.239.211.112: icmp_seq=0 ttl=50 time=9.950 ms
64 bytes from 115.239.211.112: icmp_seq=1 ttl=50 time=23.994 ms
64 bytes from 115.239.211.112: icmp_seq=2 ttl=50 time=12.272 ms
64 bytes from 115.239.211.112: icmp_seq=3 ttl=50 time=19.717 ms

--- www.a.shifen.com ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 9.950/16.483/23.994/5.641 ms

==== www.baidu.com connected ====

如果把后面的参数 ,换成某个不能访问的网址,比如在伟大的墙内,可以试下:

./testurl.sh www.google.com
PING www.google.com (216.58.197.100): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
Request timeout for icmp_seq 2

--- www.google.com ping statistics ---
4 packets transmitted, 0 packets received, 100.0% packet loss

命令1 || 命令2

解释:这个正好跟&&相反,如果命令1返回失败,则执行命令2

#!/bin/bash
ping -c 4 $1 || printf '\n==== %s connect fail ====\n' $1

把这个保存成testurl2.sh ,然后重复刚才的测试

./testurl2.sh www.google.com
PING www.google.com (216.58.199.4): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
Request timeout for icmp_seq 2

--- www.google.com ping statistics ---
4 packets transmitted, 0 packets received, 100.0% packet loss

==== www.google.com connect fail ====

通过刚才的测试,相信大家已经掌握&&与||的用法了,那么问题来了,如何判断前一个命令的执行结果是【成功】还是【失败】呢?

先回忆一下,大学里《C程序设计》里老师讲的内容,C程序里main函数,如果运行成功,最后一般会约定返回return 0,没错bash里就是这么判断的

(再提一个问题:为什么要跟C扯上关系?因为linux里的很多bash命令,就是拿C/C++来开发的),我们可以来验证下:

#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv){
    printf("hello world and this function will return 0\n");
    return 0;
}

这是一段c语言的代码,保存成hello1.c,然后输入gcc -o hello1 hello1.c (mac本上只要安装了xcode,就已经自带了gcc编译器),然后会在当前目录下生成hello1的可执行文件,做为对比,再来一个hello2.c

#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv){
    printf("hello world and this function will return 1\n");
    return 1;
}

同样编译成hello2,然后测试:

./hello1 && echo 'hello1 is ok'
hello world and this function will return 0
hello1 is ok

再来一个

./hello2 && echo 'you can not see this'
hello world and this function will return 1

小结:这跟很多语言里约定1代表true, 0代表false正好是反的,在bash里,如果一个命令执行后返回0,表示成功,返回1表示失败。  

 

6. 检测参数个数及类型

最后结合前面学到的知识,做一个小小的综合练习:

#!/bin/bash

echo 'param count: ' $#
echo 'first param: ' $1
if [ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null); then
   echo 'param check pass!'
else
   echo 'only one integer parameter is accepted!'
fi

上面这段代码的意思是仅接收1个整型的参数,将这段代码保存成check1.sh,然后试着运行下:

./check1.sh a b 2
param count:  3
first param:  a
only one integer parameter is accepted!

再试下:

./check1.sh 123
param count:  1
first param:  123
param check pass!

第5行的那个长长的if判断,初次看估计比较晕,我们来分解一下:

第一部分

[ $# -eq 1]

其中$#表示参数的个数,-eq 1 要求参数个数必须等于1

第二部分

(echo $1 | grep ^[0-9]*$ >/dev/null)

仍然有点复杂,再细分一下,先不管最后的>/dev/null,将其去掉,然后简化一下:

grep 用于字符查找及过滤,见下面的图:

  

who用于显示本机有哪些用户登录了,以及登录的终端信息,加上管道符|,将输出结果传递给grep 001 ,最后就从who的一堆结果中,过滤出包含001的信息了。

再回过头,看下这个:

echo 123 | grep ^[0-9]*$

会输出123(注:如果mac上将终端改成了zsh,直接运行会报错zsh: no matches found: ^[0-9]*$,解决办法:新建一个.sh脚本文件,写在脚本文件里就能运行了),grep后的部分是一个正则表达式,匹配0-9中的1个或多个,最后再来看:

(echo $1 | grep ^[0-9]*$ >/dev/null)

现在应该能看懂了吧,将1个参数输出,然后做为grep的输入,正常情况下,如果第1个参数为数字,则会输出,但是我们的本意是放在if条件判断中,并不希望将其输出,所以最后重定向到黑洞。

结合前面的命令列表&&,可以将这段if简化成终极版本:

#!/bin/bash -x

! ([ $# -eq 1 ] && (echo $1 | grep ^[0-9]*$ >/dev/null)) && echo 'only one integer parameter is accecpted ' &&  exit 1
echo 'param check pass!'

就不解释了,大家自己体会吧。  

  

时间: 2024-11-06 03:31:47

bash/shell编程学习(2)的相关文章

bash/shell编程学习(1)

1)定义变量 myvar=abc #注:等号前后不能加空格 #或 myvar="abc" #或 myvar='abc' #注:如果变量后面的值中间本身没有空格,加不加引号都无所谓,但值本身如果有空格,则引号是必须的,比如: myvar='abc 123' 2) 使用变量echo $myvar 完整示例: #!/usr/bin/env bash myvar1='ddddddd 111' echo 'myvar1='$myvar1 echo 'myvar1=$myvar1' #注意这一行跟

bash/shell编程学习(3)

接上节继续, 1. 从键盘读取输入内容 #!/bin/bash read -p 'please input something:' input echo 'your input:' $input 运行效果: ./read1.sh please input something:123 your input: 123   2. while循环及case分支 #!/bin/bash printf '\nplease input a number or character, press "q"

Linux系统中bash shell编程的10个基础问题讲解_linux shell

第1问:为何叫做shell?在介绍 shell 是什么东西之前,不妨让我们重新审视使用者与电脑的关系.我们知道电脑的运作不能离开硬件,但使用者却无法直接对硬件作驱动,硬件的驱动只能透过一个称为"操作系统(Operating System)"的软件来控管,事实上,我们每天所谈的linux,严格来说只是一个操作系统,我们称之为"核心(kernel)".然而,从使用者的角度来说,使用者也没办法直接操作kernel,而是透过kernel的"外壳"程序,也

Linux服务器Shell编程学习笔记

Shell脚本编程学习入门是本文要介绍的内容,我们可以使用任意一种文字编辑器,比如gedit.kedit.emacs.vi等来编写shell脚本,它必须以如下行开始(必须放在文件的第一行):  代码如下 复制代码 #!/bin/sh ...注意:最好使用"!/bin/bash"而不是"!/bin/sh",如果使用tc shell改为tcsh,其他类似. 符号#!用来告诉系统执行该sell脚本的程序,本例使用/bin/sh.编辑结束并保存后,如果要执行该shell脚本

Shell编程学习:Shell子程序结构和函数

函数可以简化代码,实现脚本代码重用.一次定义可以多次调用.结构化编程,增强可读性,可以将功能定义为多个函数,然后保存在一个文件中,然后在~/bashrc或者在命令行使用source(.)调用这个文件.加快运行速度 1.定义函数的方法(传统风格) function name{ } C语言风格: name(){ } 推荐使用C语言风格,兼容性好,可以在csh/tsch中使用 函数中return和exit的区别,return退出执行,返回到主程序函数之后继续执行 exit直接退出当前脚本. shell

Bash Shell脚本学习小结_linux shell

1.字符截断: 如果是一般路径的字符截断可以用basename和dirname这两个工具:basename可以从一个文件路径中截一个文件名 例如: 复制代码 代码如下: $ basename /home/file.tarfile.tar dirname可以从一个文件路径中截到一个目录路径例如: 复制代码 代码如下: $ dirname /home/file.tar/home 不使用外部工具进行字符截断bash有自带的功能来对变量进行字符截断,一般使用"##", "#"

Shell编程学习:分支结构和循环结构

一.if结构 1.单分支结构 if condition then statement1 statement2 ... fi 2.双分支结构 if condition then statement1 statement2 ... else statement3 statement4 fi 3.多分支结构 if condition then statement elif condition2 then statement2 fi 二:case结构 case condition in pattern1

shell编程学习:shell基础和文件

1shell就是用户和内核进行交互操作的一种接口, 2shell可以运行的命令包括linux命令,内置命令,实用程序,用户程序,shell脚本 3shell元字符: 4.获取命令帮助:man,info.--help 5.文件类型有普通文件(-),目录(d),符号链接文件(l),字符设备文件(c),块设备文件(b),套接字(s),命名管道(p),块设备文件就是计算机外围设备文件,让用户可以操作设备的时候和操作普通文件一样. 6,常用文件和目录操作命令: ls ,ls -al,ls -R递归列出所有

SHELL编程基础之BASH入门

1.了解SHELL 只要能够操作应用程序的接口都能够称shell.shell也是一个应用程序,工作在用户模式,运行为进程.shell进程主要用于提供命令行界面,提供一系列的工作特性,有些特性可以自行的设定.当用户通过多种方式登录到Linux操作系统时,我们就可以认为该用户打开了一个SHELL,从而和系统进行交互. shell在linux系统下的角色如下: 2.为什么要学习shell 命令行界面的shell是很不好的,但是学完之后好处却很多.例如图形化界面像windows xp.win 7每个版本