shell编程笔记1–shell基础

  • Post author:
  • Post category:其他




shell编程笔记1–shell基础

shell是linux使用者必须掌握的一项技能,笔者使用了多年的linux一直以来没有来得及做一个简单的学习文档,最近抽空将常用的shell编程要点加以总结,以供后续查阅;后续也会在此基础上持续更新,并补充重要知识点和功能函数。



笔记1–变量

  • 变量定义与引用

    字符串类型,不解析任何字符。

     var1='var1'
    

    双引号内部会解析$和反斜杠特殊字符。

    var2="var2\var3"
    

    反引号或$()执行系统命令

    now_date=\`date\` 
    cur_dir=\$(pwd)
    

    使用变量直接加$即可,变量名外面的花括号是可选的,加花括号是为了帮助解释器识别变量的边界

    echo $var1 $var2 $now_date "current dir:\${cur_dir}"
    
  • 变量类型

    常见环境变量:

    PATH:系统路径;

    HOME:当前用户家目录;

    HISTSIZE:保存历史命令记录的条数;

    LOGNAME:当前用户登录名;

    HOATNAME:主机名称,若应用程序要用到主机名的话,一般是从这个环境变量中的取得的;

    SHELL:当前用户用的是哪种shell;

    LANG:和语言相关的环境变量,使用多种语言的用户可以修改此环境变量;

    env 可以查看所有环境变量

  • 预定义变量

    预定义变量为shell内部使用的变量,与不能重定义,常见如下:

    $@ 传入的所有参数

    $# 位置参数的数量

    $* 所有位置参数的内容

    $? 命令执行后返回的状态, 0表示成功,非0表示失败

    $$ 当前进程的进程号

    $! 后台运行的最后一个进程号,很少用

    $0 当前执行的进程名,即执行脚本

    $1-n 传入的1-n个参数

    测试:
     bash test.sh para1 para2 para3
     $@ para1 para2 para3
     $# 3
     $* para1 para2 para3
     $? 0
     $$ 18687
     $!
     $0 test.sh
     $1 para1
    
  • declare和typeset

    命令两者等价,都是用来定义变量类型的,相关参数说明如下:

    -r 将变量设为只读

    -i 将变量设为整数

    -a 将变量定义为数组

    -f 显示此脚本前定义过的所有函数及内容

    -F 进显示此脚本前定义过的函数名

    -x 将变量声明为环境变量,env中可以查看到定义的环境变量

    示例:

    $ num1=1
    $ num2=$num1+1
    $ echo $num2
    1+1
    $ declare -i num3
    $ num3=$num1+10
    $ echo $num3
    11
    
  • 注释

    单行注释方法为: # 注释内容

    多行注释方法:

    方法1:
    if false; then
    echo '1'
    echo '2'
    fi
    方法2:
    ((0))&&{
    echo '1'
    echo '2'
    }
    
  • 重定向

    cmd > file #重定向到file

    cmd >> file #追加的方式重定向到file

    cmd > /dev/null #重定向到空设备文件

    linux下输入、输出、错误描述符:

    标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据;

    标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据;

    标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

    cmd > file 2>&1 # 将标准输出和标准错误输出重定向到file文件。

    cmd > /dev/null 2>&1 # 屏蔽所有输出

  • 交互式输入read

    read可在shell中实现交互式输入, -p可实现带提示的交互式输入,示例如下:

    function interact(){
        echo 'input var1:'
        read var1
        echo 'var1 is ' $var1
        read -p 'input var2:' var2
        echo 'var2 is ' $var2
    }
    执行结果:
    input var1:
    001
    var1 is  001
    input var2 002
    var2 is  002
    



笔记2–运算、比较操作

  • 加减乘除三种表示方法

    1-let命令:

    let “sum=3+5”

    echo $sum

    2-expr命令:

    sum=`expr 2 – 5` #注意-左右有空格

    echo $sum

    注意:使用乘法 * 的时候需要转义\*否则会出错。

    3-使用(( … )) 的形式:

    sum=$((3+5))

    echo $sum

    4-使用bc进行浮点运算

    使用方法:variable=`echo “OPTIONS; OPERATIONS” | bc`

    例如:n=`echo “scale=3; 13 / 2” | bc`

    echo $n 结果为6.500,其中scale=3表示小数点位数。
  • 常见比较操作
  1. test整数比较方法

    大于 -gt

    小于 -lt

    大于等于 -ge

    小于等于 -le

    等于 -eq

    不等于 -ne

    示例:
    $ a=100
    $ b=200
    $ test $a -lt $b
    $ echo $?
    0
    $ test $a -eq $b
    $ echo $?
    1
    
  2. test字符串比较

    测试空字符串-z

    测试字符串的长度为非零-n

    等于某一个字符串=不等于某一个字符串!=
  3. test逻辑与非

    -a 逻辑与

    -o 逻辑非

    $ a=100
    $ b=200
    $ test $a -eq 100 -a $b -eq 200
    $ echo $?
    0
    $ test $a -eq 101 -o $b -eq 200
    $ echo $?
    0
    $ test $a -eq 101 -o $b -eq 201
    $ echo $?
    1
    
  4. 文件比较

    文件类型说明:

    常规文件 –

    目录文件 d

    字符设备 c

    块设备 b

    套接字 s

    链接 l

    test -f $filename #文件存在

    test -d $filename #目录存在

    test -r $filename #文件是否可读

    test -w $filename #文件是否可写

    test -x $filename #文件是否可执行



笔记3–字符串 数组



3.1 字符串

  • 字符串分割方法:

    假设有变量 var=http://www.aaa.com/123.htm.

    1. #号截取,删除左边字符,保留右边字符。

      echo ${var#

      //}

      其中 var 是变量名,# 号是运算符,

      // 表示从左边开始删除第一个 // 号及左边的所有字符

      即删除 http://

      结果是 :www.aaa.com/123.htm
    2. # 号截取,删除左边字符,保留右边字符。

      echo ${var##

      /}

      ##

      / 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符

      即删除 http://www.aaa.com/

      结果是 123.htm
    3. %号截取,删除右边字符,保留左边字符

      echo ${var%/

      }

      %/

      表示从右边开始,删除第一个 / 号及右边的字符

      结果是:http://www.aaa.com
    4. %% 号截取,删除右边字符,保留左边字符:

      echo ${var%%/

      }

      %%/

      表示从右边开始,删除最后(最左边)一个 / 号及右边的字符

      结果是:http:
    5. 从左边第几个字符开始,及字符的个数

      复制代码 代码如下:

      echo ${var:0:5}

      其中的 0 表示左边第一个字符开始,5 表示字符的总个数。

      结果是:http:
    6. 从左边第几个字符开始,一直到结束。

      echo ${var:7}

      其中的 7 表示左边第8个字符开始,一直到结束。

      结果是 :www.aaa.com/123.htm
    7. 从右边第几个字符开始,及字符的个数

      echo ${var:0-7:3}

      其中的 0-7 表示右边算起第七个字符开始,3 表示字符的个数。

      结果是:123
    8. 从右边第几个字符开始,一直到结束。

      echo ${var:0-7}

      表示从右边第七个字符开始,一直到结束。

      结果是:123.htm

      注:左边的第一个字符是用 0 表示,右边的第一个字符用 0-1 表示
  • expr字符串操作

    1. 字符串长度

       $ str1="abcde12345"
       $ echo ${#str1}
       10
       $ expr length $str1 
       10
      
    2. 取子字符串

      1)expr substr $string $position $length   #注意位置编号从1开始
      2)echo ${string:\$pos:$length}  # 注意位置编号从0开始
      $ string="abcde1234567890"
      $ expr substr $string  1 3   
      abc
      $ echo ${string:0:3}
      abc
      
  • 字符串连接

    $ str1=abc
    $ str2=def
    $ str3=\$str1\$str2
    $ echo $str3
    abcdef
    $ str3="\${str1}-${str2}" # 通过{}可以自定义分隔符
    $ echo $str3
    abc-def
    
  • 字符串替换

    $ string="you and you and zhangsan"  
    $ echo ${string/you/YOU}    # 只替换一次
    YOU and you and zhangsan
    $ echo ${string//you/YOU}   # 全部替换
    YOU and YOU and zhangsan
    



3.2 数组

数组常见操作如下:

1)定义一个数组:        myarray=(1 2 3 4 5)
2)读取数组的某一个元素 : echo ${myarray[下标值]}    # 注意数组名称必须使用{} 括起来, 下标值从0开始编号
3)数组元素的赋值:       myarray[下标值] = xxx
4)显示数组的所有元素:   echo ${myarray[*]}
5)获得数组的长度:  echo    ${#array[@]}  或者    echo ${#array[*]}
6)删除一个数组元素:     unset myarray[下标值]

案例:for循环输出数组

function printarray(){
    arr=(0 1 2 3 4 5 6)
    len=${#arr[@]}
    for (( i=0; i<$len; i++ ))
    do 
        echo ${arr[$i]}
    done
}

注意:for循环中 (( 与 ))和其它变量之间有空格



笔记4–顺序 选择 循环结构



4.1 顺序

shell默认采用顺序结构执行各条命令



4.2 选择

  1. if else选择

    if elif else是最简单的选择结构,具体示例如下:

    function testslect(){
        read -p "input num1: " num1
        read -p "input num2: " num2
        if [ $num1 -gt $num2 ];then
            echo $num1 '>' $num2
        elif [ $num1 -lt $num2 ];then
            echo $num1 '<' $num2
        else
            echo $num1 '=' $num2
        fi
    }
    输出结果:
    input num1: 1
    input num2: 2
    1 < 2
    
  2. case 选择

    case是常用的一种选择结构,具体示例如下:

    #!/bin/bash
    
    usage()
    {
       	echo "this is help!"
    }
    client()
    {
       	echo "this is client!"
    }
    master()
    {
       	echo "this is master!"
    }
    case "$1" in
        -h)
            usage
            ;;
        --help)
            usage
            ;;
        client)
            client
            ;;
        master)
            master $2
            ;;
        *)
            echo "Unknown command: $1"
            usage
            exit 1
            ;;
    esac
    
    #输出结果:
    $ bash case_test.sh  -h
    this is help!
    $ bash case_test.sh  client
    this is client!
    



4.3 循环

  1. for 循环

    shell中通过for实现循环,示例如下:

    function printarray(){
        arr=(0 1 2 3 4 5 6)
        len=${#arr[@]}
        for (( i=0; i<$len; i++ ))
        do 
            echo ${arr[$i]}
        done
    }
    

    shell 中通过 for 遍历文件夹中的目录

     for file in `ls`; do echo $file; done
    
  2. while 循环

    shell中通过while实现循环,示例如下:

    #!/bin/bash
    sum=0
    i=0
    while ((i<100))
    do
    ((sum=sum+i))
    ((i++))
    done
    echo $i
    echo $sum
    案例2: 每隔一小添加用户,防止用户ssh被系统刷掉
    #!/bin/bash
    sum=0
    while [ $sum -lt 864000 ]
    do
        cd /root
        bash adduser.sh lifeng06
        sleep 3600
        let sum=sum+3600
    done
    exit 
    



笔记5–函数



5.1 函数使用方法

shell中函数定义形式如下:

function functionName(){
cmd1
cmd2
}

其中,function可以省略;执行的时候直接functionName $para1 $para2即可。



5.2 shell常见函数

  • 时间日期相关函数

     1. $ date 
         2020年 01月 20日 星期一 11:13:45 CST
     2. $ date +"%Y%m%d" 
         20200120
     3. $ date +"%Y-%m-%d %H:%M:%S"
         2020-01-20 11:17:09
     4. date +"%Y%m%d" -d "+n year|month|day" 今天的后n年||日 日期
     5. date +"%Y%m%d" -d "-n year|month|day"  今天的前n年||日 日期
     6. date +"%Y-%m-%d %H:%M:%S" -d "+n hour|minute|second" 今天的后 n时||秒 日期
     7. date +"%Y-%m-%d %H:%M:%S" -d "-n hour|minute|second" 今天的前 n时||秒 日期
    
  • to add …



笔记6–常见shell小功能



6.1 批量检测 ip 是否ping通

#!/bin/bash

abspath=$(cd "$(dirname "$0")"; pwd)
logs=$abspath'/test_ping.log'

iplist=(192.168.1.1 192.168.1.2 192.168.1.22)
len=${#iplist[@]}
for (( i=0; $i<$len; i++ ))
do
    ping  -c 2  ${iplist[$i]} &>/dev/null
    if [ $? -eq 0 ]; then
        echo -e  "$(date) ${iplist[$i]} is up\t" >> $logs
    else
        echo -e "$(date) ${iplist[$i]} is down\t" >> $logs
    fi
done



6.2 批量检测用户是否有 ssh 登录权限

function test_ssh(){
    abspath=$(cd "$(dirname "$0")"; pwd)
    logs=$abspath'/test_ssh.log'
    
    iplist=$(cat ip.txt|tr -d '\r')
    for i in $iplist
    do
        ping  -c 2  $i &>/dev/null
        if [ $? -eq 0 ]; then
            ssh -o "ConnectTimeout=5" -o "StrictHostKeyChecking=no" -p yourport -l yourname $i exit
            if [ $? -eq 0 ]; then
                echo -e  "[$(date)] [$i is connnected], [\$? $?]\t" >> $logs
            else
                echo -e "[$(date)] [$i is disconnected] [\$? $?]\t" >> $logs
            fi
        else
            echo -e "[$(date)] [$i is down] [\$? $?]\t" >> $logs
        fi
    done
}
测试结果:
[Sun 10 Nov 2019 11:14:04 AM CST] [xx is connnected], [$? 0]	
[Sun 10 Nov 2019 11:14:15 AM CST] [xx is down] [$? 0]	
[Sun 10 Nov 2019 11:14:26 AM CST] [xx is down] [$? 0]	
[Sun 10 Nov 2019 11:14:32 AM CST] [xx is disconnected] [$? 0]	

注意:需要使用ConnectTimeout、StrictHostKeyChecking、exit三个参数,否则会在相应界面等待输入或占用太长时间。ip.txt中存放所有ip,需按照行存放。tr -d ‘\r’主要为了删除换行符,否则在有些bash里面执行ssh的时候会出现错误。



6.3 help 菜单实现方法

1)直接使用echo

  function usage()
  {
   echo 'usage:'
   echo '		bash dh.sh -h|--help|start|stop'
   }

2)使用EOF标识

 function usage() {
 cat <<_EOF
 	usage:
 		bash dh.sh -h|--help|start|stop
 _EOF
 } 

3)使用EOF将多行文件写入到指定文件

当系统中没有编辑工具的时候,我可以通过cat 和EOF将多行文件重定向到指定文件

 可以使用_EOF,也可以使用EOF
 cat <<EOF >target.txt
 line one  
 line two
 EOF



6.4 拷贝当前目录指定文件夹到备份文件夹下面

########功能说明###########
# 获取当前目录下所有的_out结尾的文件夹,并在backupfiles目录创建对应的_out文件;
# 根据 file_time 格式,将_out 中指定年月的文件移动到 backupfiles目录对应的_out文件中;
# 使用方式: 将 mvfile.sh 文件和_out 文件夹放在同一级目录下面即可;
##########################
# vim mvfile.sh 
#file_time='202007,202008,202009'
file_time='202010'
mkdir -p backupfiles
cd backupfiles
ls ../ |grep _out|xargs mkdir -p
cd ..
for dir_out in `ls|grep _out`
do
  pwd
  # echo 'aaa'$dir_out
  cd $dir_out
  for file in `ls|grep $file_time`
  do 
    mv $file ../backupfiles/$dir_out
  done
  cd ..
done



参考文献

1

shell 输入/输出重定向


2

shell 变量及输入输出



版权声明:本文为u011127242原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。