shell 学习(1)

花了大概一天半的时间比较全面的接触了一下shell脚本,对于这种语言我对自己的要求很简单,不求深度精通,能写工具类脚本,能阅读别人的脚本,能在在相应场合无障碍沟通就好。这里整理一下这个阶段的知识结构,准备再花一到两天时间接触一下一些高级玩法,到时候Shell就算完篇了。

Shell 也是一门编程语言,学习编程语言我觉得需要关注这么一些点:

  • 数据类型

  • 语法/ 结构控制

  • 内置函数和变量

只要相应的关注整理一下,入门还是很快的,我相信读完这篇文章以后,你基本上可以认为自己能独立读写一些简单的Shell脚本,可以有底气的说自己是一个初级的Sheller了。

变量声明

首先要了解一下Shell的变量声明,引用等语法规则。有两个command(declare,local)与变量的声明有关:

declare  a=1  #声明一个全局变量
local  b="hello~~"   #声明一个局部变量,一般在函数体中使用
c=1     #实际情况是,这两个command可用可不用,不使用同样可以声明变量

变量的声明特别要注意赋值等号(=)两边不能有空格。

a = 2  #这个声明就会失败!!!

变量声明了以后还有一个变量读取,Shell读取变量的语法如下:

${var} 或者 $var #作为一个资深Coder,当然知道前者更专业,可以避免在字符串中解析出错 

数据类型

shell 不像那些严谨的编程语言有完整的数据体系,我现在接触到的就这么几种:

  1. 字符串
  2. 数值(整形/浮点)
  3. 数组(简单数组/关联数组)

字符串

这个是最常用也是最简单的数据类型了,首先来感受一下

echo "hello,shell~~" #输出字符串到控制台
echo "hello, shell " > a.txt  #将字符串输入到文件a.txt中,输入之前会清空文件
echo "hello,shell" >> a.txt #将字符串输入到文件a.txt中,输入之前不清空文件

和其他的编程语言一样,字符串也有相应的一些操作,整理下来基本上就是这么一些:

取字符串长度 :  ${#string}

子串截取:  
${string:position:length}  从起始位置截取指定长度
${string:position}  从起始位置截取到末尾

子串替换:
${string/substring/replacement} 替换第一个匹配的子串
${string//substring/replacement} 替换所有子串

子串删除
${string%substring} 从变量$string的结尾, 删除最短匹配$substring的子串
${string%%substring} 从变量$string的结尾, 删除最长匹配$substring的子串
.....

数值

数值分为整形和浮点型,他们的操作完全不一样,所以得分开说。
数值类型的话关注点就在运算了,Shell对于整形的运算有三种方式,如下:
** let **

a=1;
b=2;
let c=a+b;
echo $c;

** [ ] 中括号**

a=1;
b=2;
c=$[a+b];
echo $c;

** (( )) 双小括号 **

a=1;
b=2;
c=$(( $a+$b));
echo $c;

***这里总结一下, 这三种方式还是有一些细微的差别的,let 方式最为简单。[ ]需要注意在外面要用$对返回值进行引用。 (( ))最为复杂容易出错,首先在括号内变量的必须要加$引用,其次左括号必须和变量间有空格。 ***

bash 不支持浮点运算,如果需要进行浮点运算,需要借助bc,awk 处理。

c=$(echo "5.01-4*2.0"|bc)
c=$(awk 'BEGIN{print 7.01*5-4.01 }')

bc,awk我暂时也还没有接触到,先放到这吧。todo

数组

数组的定义(初始化)比较简单:

arr=(0 1 2 3 4 5 6 7 8 9);
或者
arr[0]=0;
arr[1]=1;
arr[2]=2;

下面就是关于数组的一些操作了

#循环打印数组元素
for  i  in ${arr[@]} ; do
     echo $arr[i]
done;
#这里牵涉到了两个知识点:
#(1)${arr[@]}  表示数组中的所有元素
#(1)$arr[i]  获取数组元素的形式,其中i为索引

#顺便说了for结构,对于for结构也可以这样:
for i in 1 2 3 4 5 ;do
    echo $i
done;

# 还可以这样:
for((i=0;i<=10;i++));do
     echo $i;
done;

#回到数组,获取数组的长度:
${#arr[@]}

数组的基本操作就是这些了,这里所说的数组都是简单数组,它的索引只能是数值,关联数组的不同之处在于索引可以是其他类型如字符串。需要bash4.0以上才支持

declare -A phone
phone=([jim]=135 [tom]=136 [lucy]=158)

数据类型基本上就是这些了,接下来看看一些常见的结构控制及语法。我们都知道程序的结构有三种:

  • 顺序结构

  • 选择结构

  • 循环结构

就从这个结构来入手语法吧。

语法

顺序结构就没什么好说的了,重点是后面两种。

选择结构

选择结构说白了就是会存在逻辑判断,Shell里面也提供两种大结构来支持判断。

# ------数字比较--------
if test 100 -gt 50 ;
then
      ehco " 100 大于50";
else
     ehco "100 小于50"
fi
# ------字符串比较--------
if test a \> b ;
then
      ehco " a 大于b";
else
     ehco "a 小于b"
fi

#------case 子句----------
case "$Keypress" in  
  [[:lower:]]   ) echo "Lowercase letter";;  
  [[:upper:]]   ) echo "Uppercase letter";;  
  [0-9]         ) echo "Digit";;  
  *             ) echo "Punctuation, whitespace, or other";;  
esac  

上面只是简单的示例,现在来说说具体的语法。对于if结构,后面的比较操作有三种写法:

# 1. test
if test 100 -gt 50 ;
then
      ehco " 100 大于50";
else
     ehco "100 小于50"
fi

# 2. [ ]中括号
if [ a \> a  ]
    then
    echo  "a>a"
else
    echo "a<a"
fi;

# 3. [[ ]]  双中括号
if [[ a == a && 2 > 3 ]] 
    then
        echo " all true";
    else
        echo " not all true";
fi;

总结一下,这三种写法中 test, [ ] 最为复杂,对于<,>等操作符必须要要转义\>, \<,而且不支持多条件判断,所以一般情况下就用双中括号[[ ]]来得简单。

shell中提供的支持判断的操作符比较别扭,分别正对字符串,数值比较各有一套:
字符串比较

> , >=  : 大于,大于等于
<, <=  : 小于,小于等于
==  :  等于
!= : 不等于
-z  : 字符串为空
-n  : 字符串非空

数值比较

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

文件比较

-e  文件是否存在      
-f  是一个标准文件          
-d  是一个目录

个人感觉这个定义好别扭,字符串和数值完全没有必要弄两套比较体系,而且数值比较很直观的东西变得很复杂。接下来,最蛋痛的事情到了,上面说到if后面的写法有三种:test , [] , [[ ]]。其中test, [] 是严格按照比较操作符的定义来的,如果是字符串比较,只能用符号比较>,<等,如果是数值比较,只能用文字比较符 -gt ,-lt等,但是对于[[ ]] 无论是文字比较还是数值比较,两种比较符都可以任意使用。所以一般建议只用[[]],你以为这就完了?不,还有一种情况,三目运算:

echo $(( 3 > 2?100:999));
echo $(( a > b?101:991));

三目运算表达式只能用符号比较符 > ,< 等!!!
在这一段的学习过程中我是感觉非常混乱,绕了好久才把这些点给理清楚,现在统一总结一下:

  1. test , [] , [[ ]]三种结构中,test ,[] 需要对<,>等转义。而且两种比较符各司其职,不能跨界操作。而 [[]]不需要转义,比较符号也可以跨界操作,非常方便。所以一般就只用[[]].
  2. 三目运算表达式中的比较只能用符号比较符 >, <.

Case子句的写法就没这么纠结了,当然在我看来,语法还是很蛋痛的。来记录一下它的语法规则:

case "$Keypress" in  
  [[:lower:]]   ) echo "Lowercase letter";;  
  [[:upper:]]   ) echo "Uppercase letter";;  
  [0-9]         ) echo "Digit";;  
  *             ) echo "Punctuation, whitespace, or other";;  
esac
  1. 条件判断分支必须用右括号 )结束。
  2. 条件子句必须用两个分号;;结束。

循环结构

Shell的循环结构比较简单,就是for, while。都来看看吧

#1. for 指定数组
for loop in 1 2 3 4 5 6
do
  echo $loop
done

#2. for 变量自增长
for i in `seq 100`
do
    echo $i;
done


#3. for 循环已定义数组
arr=(1 2 3  4 5 6 7 8 9 0)
for i in ${arr[@]} ;do
    echo $i;
done;

#4.  指定范围
for i in {1..10} ;do
    echo $i;
done;

#45. while (( ))
COUNTER=0
while(($COUNTER<=5))
do
        echo $COUNTER
        let "COUNTER++";
done

#5. while [[ ]]
while [[ $num != 4 ]]
do
    echo $num;
    let "num++";
done;

循环结构远没有选择结构那么复杂,基本上看看这些示例就OK了。

函数

语法部分还有一个比较重要的方面就是函数了。函数的定义和其他语言一样:

function myfun(){
    echo "共传入了:$# 个参数,分别是:" 
    for i in $*; do 
        echo $i
    done
}
# 1.   $# 参数个数
# 2.  $*  所有参数的数值
#3.  function 可写可不写

Shell似乎不支持带参函数,所以如何在函数内获取参数呢?

function myfun(){
     echo ${1};
         echo ${2};
         echo ${3};
}
#按照索引来获取参数就好了。注意,索引从1开始,${0}获取的是脚本名。

函数的执行也和其它程序不一样,来看一个例子:

sum=0
function myadd(){
    for i in $*; do 
        let sum+=$i
    done
}
myadd 1 2 3 4  # 执行函数
echo $sum;

如果函数有返回值,则可以直接赋值:

function myadd(){
    for i in $*; do 
        let sum+=$i
    done
     return $sum;
}
$(myadd 1 2 3 4)  # 执行函数
num=$?  #获取返回值

到此为止,基本上语法就都走了一遍,接下来要了解一些shell内置的一些变量,命令等,这个是真正能玩转起来的重要因素,看看有哪些东西是需要关注的吧。

内置变量及命令

首先来了解一下一些特殊的内置变量

$0  脚 本名字
$1- $9  位置参数 #1 - #9
${10}   位置参数 #10
$#     位置参数的个数
"$*"    所有的位置参数(作为单个字符串) *
"$@"    所有的位置参数(每个都作为独立的字符串)
${#*}   传递到脚本中的命令行参数的个数
${#@}   传递到脚本中的命令行参数的个数
$?  返回值
$$  脚本的进程ID(PID)
$-  传递到脚本中的标志(使用set)
$_  之前命令的最后一个参数
$!  运行在后台的最后一个作业的进程ID(PID)

对于初次接触者,可能这么几个要重点关注下

# 1. 位置参数,在上面的示例中已经用过,函数内部使用传递参数的写法
$1 - $9 位置参数 #1 - #9
${10}   位置参数 #10

# 2.   参数的数量 
$#     位置参数的个数

#3.  参数的集合,在一些不关心具体参数值的函数中特别有用,例如 add
$@  所有的位置参数(每个都作为独立的字符串)

#4.  函数的返回值, 不能直接将函数执行后复制给变量,必须通过这个返回值变量
$?  返回值

命令的话太多了,这里就不纠结这个话题,通篇下来,shell的初步接触应该比较完备了,后续再研究下高端玩法。

    原文作者:UncleYee
    原文地址: https://www.jianshu.com/p/478940a7adf9
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞