Shell

  • 推荐网站:Advanced Bash-Scripting Guide

创建shell

  • mkdir shellscript&&cd shellscript
  • vim hello
  • 开头以#! /bin/bash来标识文件使用bash来运行的
  • 输入 echo ‘hello world’
  • ll 显示没有user的执行权限
  • chmod u+x myown 改变权限,文件会变颜色,后面加上一个可执行文件的星号
  • ./hello 就可以执行脚本了

/add 一个加法的脚本

    #!/bin/bash
    echo "Please type first number:"
    read param1
    echo "Please type second number"
    read param2
    result=$[$param1+$param2]
    echo "The result is :"$result

或者

    #!/bin/bash
    echo "first: $1"  # 大于10的参数用 ${10} 来表示
    echo "second: $2"
    sum=$[$1+$2]
    echo "result: $sum"
    echo "the script name is $0"  # $0是程序名,包含路径
    echo 'basename $0'  # basename输出的不包含路径
    # 输入./add 3 5 自动定位到变量,

条件判断

文件表达式

  • if [ -f file ] 如果文件存在
  • if [ -d … ] 如果目录存在
  • if [ -s file ] 如果文件存在且非空
  • if [ -r file ] 如果文件存在且可读
  • if [ -w file ] 如果文件存在且可写
  • if [ -x file ] 如果文件存在且可执行

整数变量表达式

  • if [ int1 -eq int2 ] 如果int1等于int2
  • if [ int1 -ne int2 ] 如果不等于
  • if [ int1 -ge int2 ] 如果>=
  • if [ int1 -gt int2 ] 如果>
  • if [ int1 -le int2 ] 如果<=
  • if [ int1 -lt int2 ] 如果<

字符串变量表达式

  • if [ a= b ] 如果string1等于string2 字符串允许使用赋值号做等号
  • if [ string1!= string2 ] 如果string1不等于string2
  • if [ -n $string ] 如果string 非空(非0),返回0(true)
  • if [ -z $string ] 如果string 为空
  • if [ $sting ] 如果string 非空,返回0 (和-n类似)

if的格式

    if condition1
    then
        command1
    elif condition2
        command2
    else
        commandN
    fi
    # 下面是实例
    num1=$[2*3]
    num2=$[1+5]
    if test $[num1] -eq $[num2]
    then
        echo '两个数字相等!'
    else
        echo '两个数字不相等!'
    fi

函数

/test1 两种定义函数格式,return,反引号

    #! /bin/bash
    function func1 {
        echo 'this my first function'
    }
    func1  # 定义之后才能调用
    func2() {
        echo 'This is another function'
        return 20
    }
    func2
    echo "$?"  # 必须在函数执行后立即执行,对退出状态码进行访问,必须在0-255,没有return时默认正确是0,不成功是其他
    value=`func1`  # 将func1的输出赋值给value,这里是用的反引号,命令替换是指shell能够将一个命令的标准输出插在一个命令行中任何位置
    echo "$value"

/test2 函数变量参数的使用

    #! /bin/bash
    add() {
        if [ $# -eq 2 ];then
            result=$[ $1+$2 ]
            echo $result
        else
            echo "Please input 2 params"
            return 1
        fi
    }
    value=`add $1 $2`  # 函数的echo会返回到这里
    if [ $? -eq 0 ]; then  # 判断退出码
        echo $value
    else
        echo "Err: $value"
    fi

/test3 数组参数的处理 局部定义域local

    #! /bin/bash
    testarray(){
        echo "$@"
        echo "$1"
        echo "$2"
        echo "$#"
    }
    sum(){
        local result=0  # 不加local是全局变量
        for var in $@
        do
            result=$[ $result + $var ]
        done
        echo $result
        local newarray=(`echo "$@"`)
        echo ${newarray[*]}  # 返回数组
    }
    array=(1 2 3 4 5 6 7)
    testary $array  # 这里只传进去了第一个参数
    testary ${array[*]}  # 将数组当成了7个参数传了进去
    sum ${array[*]}
    echo "This is outside function: $result"  # 此时访问不到了

函数库:包含了一些列函数的封装,包含可重用函数的脚本文件

    # /calc.sh 函数库文件,加.sh是为了更方便的查找脚本,可以不加
    #! /bin/bash
    add() {
        local result=0
        if [ $# -eq 2 ]; then
            result=$[ $1 + $2 ]
            echo $result
        else
            echo 'Need 2 number params'
            return 1
        fi
    }
    sum(){
        local result=0
        for var in $@
        do
            result=$[ $result + $var ]
        done
        echo $result
    }
    # /test4 测试文件
    #! /bin/bash
    source ./calc.sh  # 将已知文件引入到当前环境
    echo `add 12 23`

bash启动时,自动引入函数库

  • vim ~/.bashrc
  • 在最后添加 . ~/shellscript/calc.sh

变量运算 expr 详细内容,请看谷歌

  • expr length “hello” 字符串长度
  • expr length “$str” 参数是变量
  • expr index “$str” ‘h’ 查找h在str中的位置,返回第一个匹配的位置

bc工具的使用 脚本中引用, 内联输入重定向

  • bc 进入bc工具
  • bc -q 不显示欢迎条
  • scale=4
  • 100/3 结果是33.3333,不设置scale会是33
  • quit 退出 scale会失效,每次都要设置
  • num2=3;num3=17 # 自定以变量,要以分号间隔
  • var=echo "options;expression" | bc # 反引号 在脚本中引用bc
  • var=’echo “scale=4;3.44/5” | bc’
    var1=10.46
    var2=43.67
    var3=33.2
    var4=71
    result=`bc << EOF  # << 内联输入重定向,EOF开始和结束的标识符,可以自定义
    scale=4
    a1 = $var1 * $var2
    b1 = $var3 * $var4
    a1+b1
    EOF
    `  # 反引号 EOF 进行重定向
    echo result

循环

简单for的一个例子,数据四种来源,默认分隔符是空格,换行,制表符 IFS修改分隔符

    #! /bin/bash
    for val in Jan Feb Mar Apr May  # 列表循环
    do
        echo "Month name is $val"
    done
    list="Jan Feb Mar Apr May"
    for val in $list  # 使用变量实现循环
    do
        echo "Month name is $val in list"
    done
    IFS=$";"  # 修改分隔符为;且只有;
    for var in `cat datefile`  # 文件读入,默认分隔符是空格,换行,制表符
    do
        echo "Month name is $val in file"
    done
    for var in ~/shellscript/*  # 也可以是c* # 可以直接读取文件列表,一定要加通配符
    do  # 等同于 for var in `ls ~/shellscript/*`
        echo "$var"
    done
    for((i=1,j=10;i<=10;i++,j--))  # 类C的结构
    do
        echo "test number is $i $j"
    done

while test command,until test command语句

var=1
while [ $var -lt 10 ]  # command写成[]的形式
do
    echo "$var"
    var=$[ $var+1 ]  # expression 写成$[]的形式
done
var=1
while [ $var -en 10 ]  # command写成[]的形式
do
    echo "$var"
    var=$[ $var+1 ]  # expression 写成$[]的形式
done

输入输出

永久重定向 exec 文件描述符>filepath

    #! /bin/bash
    exec 1>testoupt  # 这里就把整个脚本的输出都定向了
    exec 2>errlog
    echo "test error" >&2  # 错误信息还需要临时重定向
    echo "normal output 1"
    echo "normal output 2"
    #! /bin/bash
    exec 0< errlog
    count=1
    while raed line
    do
        echo "line #$count : $line"
        count=$[ $count+1 ]
    done

用户输入处理

/calc 根据输入的命令来判断运算规则,参数数量验证,软连接

    #! /bin/bash
    if [ $# -lt 2 ]  # $#参数计数, -lt 小于
    then
        echo 'Please input at least 2 param'
        exit
    fi
    name=`basename $0`
    if [ $name = "add" ]
    then
        result=$[ $1 + $2 ]
    elif [ $name = "minus" ]
    then
        result=$[ $1 - $2 ]
    fi
    echo "The $name result is $result "
    # ln -s calc add&&ln -s calc minus 进行软连接

测试$#, $*, $@

    #! /bin/bash
    echo $#  # 统计参数个数
    echo $*  # 所有变量作为一个参数
    echo $@  # 变量存进列表
    for var in "$*"
    do
        echo "\$* Param = $var"
    done
    for var in "$@"
    do
        echo "\$@ Param = $var"
    done

/sum 计算多个参数的和,移动变量shift

    #! /bin/bash
    result=0
    while [ -n "$1" ]  # -n表示非空
    do
        result=$[ $result + $1 ]
        shift  # 位置参数的值会减1,都向前移动一位
    done
    echo "SUM of numbers is : $result"

/test2 处理带值选项, 带有合并的选项 getopt ab:cd -b value -acd param1 param2

  • 上面的命令会输出:-b value -a -c -d – param1 param2
  • getopt不能解决value中带有空格的,要用getopts
    # 输入 ./test2 -ac -b value param1 param2
    #! /bin/bash
    set -- `getopt -q ab:c "$@"`  # set -- 是将getopt的结果重新付给命令行参数 -q 不会输入遇到的错误
    while [ -n "$1" ]
    do
        case "$1" in
        -a) echo 'Option -a' ;;
        -b) value="$2"
            echo "Option -b ,Value is : $value"  # $要写到双引号里
                shift;;  # 因为读了一次值,要shift一次
        -c) echo 'Option -c' ;;
        --) shift  # 当不是选项,是参数是执行,下面可以写函数体
            break;;
        esac
        shift
    done
    echo "Params are : $*"

/test3 处理value中带空格的命令getopts

    # 输入 ./test3 -ac -b "value 123" "param1 123" param2
    #! /bin/bash
    while getopts ab:c opt  # 会把所有解析出来的参数放到opt中
    do
        case "$opt" in
        a) echo 'Option -a' ;;
        b) echo "Option -b ,Value is : $OPTARG" ;;  # 选项的值会放到$OPTARG中
        c) echo 'Option -c' ;;
        *) echo "Unknown option: $opt" ;;
        esac
    done
    shift $[ $OPTIND -1 ]  # $OPTIND存放解析的选项的数量,后面就只剩参数部分了
    count=1
    for param in "$@"
    do
        echo "Param #$count is: $param"
        count=$[ $count + 1  ]
    done

/read_test1 处理read超时

    #! /bin/bash
    if read -t 5 -p "Please type your input:" input  # -t 5秒为超时,-p 输入提示
    then
        echo "You typed: $input"
    else
        echo "Timeout"
    fi

/read_test2 处理密码隐藏

    #! /bin/bash
    echo "Please input a password:"
    read -s passwd
    echo
    echo "Your password is : $passwd"

/read_test3 从文件中读取

    #! /bin/bash
    exec 0< read_test1
    count=1
    while read line
    do
        echo "#$count: $line"
        count=$[ $count + 1 ]
    done
  • 利用管道来实现
    #! /bin/bash
    count=1
    cat read_test1 | while read line
    do
        echo "#$count: $line"
        count=$[ $count + 1 ]
    done

运行控制

信号机制 man 7 signal 进行查看 ps kill

  • 组合键产生信号
  • Ctrl + C 终止进程 SIGINT
  • Ctrl + Z 暂停进程 SIGSTP
  • ping www.baidu.com
  • Ctrl + Z
  • ps -ef | grep ping # 可以看到ping命令还在执行中
  • 命令产生信号 kill、killall
  • ps # 查看当前的进程
  • kill -9 25949 # -9是无条件终止,25949是ps命令查出来的PID
  • ps # 刚放的进程已经被终止

脚本中处理信号捕捉 trap

    #!/bin/bash
    trap "echo 'Signal traped SIGINT for Ctrl+C'" SIGINT  # 改变Ctrl + C为 输出文本
    trap "echo 'quit script'" EXIT  # 捕捉退出命令,并修改
    count=1
    while [ $count -le 10 ]
    do
        echo "Loop # $count"
        sleep 1  # 1秒钟的休眠
        count=$[ $count + 1 ]
    done
    trap - EXIT  # 移除添加的trap,不会再输出quit script
    echo 'Loop ended'

/bgtest 后台运行脚本 可以同时执行多个进程 & jobs fg bg nice renice ps al nohup

    # 只需要输入 ./bgtest &
    # nice -n 10 ./bgtest > temp & 后台运行脚本,优先级10(最高-20,最低19)
    # renice 10 -p 19863 通过PID来修改进程的优先级
    # ps al 可以查看进程优先级,普通用户只能最高设置到0
    # nohup ./bgtest & 使脚本的运行与bash无关,可以关掉bash,默认输出到nohup.out
    #!/bin/bash
    trap "echo 'quit script'" EXIT
    count=1
    while [ $count -le 10 ]
    do
        echo "Loop # $count"
        sleep 5
        count=$[ $count + 1 ]
    done
    trap - EXIT
    echo 'Loop ended'

/attest 定时运行脚本 at atq atrm

  • Ubuntu默认没有at命令,sudo apt-get install at
  • at 默认是以邮件的形式发送到指定的邮箱,所以脚本开头有一个标准输出重定向
    # at -M -f ./attest 18:18 M参数指的是不用邮箱,f参数是运行脚本
    # at -M -f ./attest now+2 min
    # atq 会看到at的运行队列
    # atrm 12 通过PID来删除at作业
    #!/bin/bash
    exec 1>>atresult
    echo "script run at `date`"
    echo "end scritp"

启动时运行脚本

系统启动时运行
  • 不同系统启动运行不同,主要有一下两种
  • System V init
  • Upstart init
  • 自定义开机运行脚本
  • debian /etc/init.d/rc.local
  • Ubuntu /etc/rc.local
  • openSUSE /etc/init.d/boot.local
  • CentOS /etc/rc.d/rc.local
  • 在Ubuntu中
  • vim /etc/rc.local 最开始添加调试功能
  • exec 1>>/home/helloworld/logs/startup/logs
  • exec 2>>/home/helloworld/logs/startup/error
  • 后面输入脚本的路径就行了,环境变量要放到另外的脚本,然后引入
shell启动时运行
  • 启动shell的三种方式
  • 启动bash
  • 通过ssh登陆
  • 通过ssh执行命令 直接执行,不会login shell,在man ssh中可以查到
  • 因此,使用ssh执行命令,不会调用/etc/profile指定的设置,只调用/etc/.bashrc这个文件
  • 下面是各种启动shell时调用的文件
  • 启动bash /etc/profile /etc/.bashrc
  • 通过ssh登陆 /etc/profile /etc/.bashrc
  • 通过ssh执行命令 /etc/.bashrc

周期运行脚本 cron crontab anacron

  • vim /etc/crontab 查看cron时效表格式
  • ls /etc/cron* 查看更多的信息,可以添加自己的脚本到指定的文件夹下
  • crontab -l 显示当前用户的时间表
  • crontab -e 修改用户时间表
  • 缺点是默认电脑是7*24小时都开机的,可以用anacron
  • vim /etc/anacrontab 查看格式,最小配置是1天1次

免密码登录

  • SSH免密码登录
  • 实现ssh自动登录的4种方法

SSH Key 无密码登录

  • A为本地主机(即用于控制其他主机的机器),B为远程主机(即被控制的机器Server), 假如ip为192.168.56.101,A和B的系统都是Linux
  • 在A上的命令:
    ssh-keygen -t rsa (连续三次回车,即在本地生成了公钥和私钥,不设置密码)
    ssh root@192.168.56.101 "mkdir .ssh;chmod 0700 .ssh" (需要输入服务器root密码, 注:必须将.ssh的权限设为700)
    scp ~/.ssh/id_rsa.pub root@192.168.56.101:.ssh/id_rsa.pub (需要输入服务器root密码)
  • 在B上的命令:
    touch /root/.ssh/authorized_keys (如果已经存在这个文件, 跳过这条)
    chmod 600 ~/.ssh/authorized_keys  (# 注意: 必须将~/.ssh/authorized_keys的权限改为600, 该文件用于保存ssh客户端生成的公钥,可以修改服务器的ssh服务端配置文件/etc/ssh/sshd_config来指定其他文件名)
    cat /root/.ssh/id_rsa.pub  >> /root/.ssh/authorized_keys (将id_rsa.pub的内容追加到 authorized_keys 中, 注意不要用 > ,否则会清空原有的内容,使其他人无法使用原有的密钥登录)
  • 也可以把Key放到其他的用户的文件里实现无密码登录,如:ssh [email protected] “mkdir .ssh;chmod 0700 .ssh”
  • 回到A机器: ssh [email protected] (不需要密码, 登录成功)
  • 假如在生成密钥对的时候指定了其他文件名(或者需要控制N台机器,此时你会生成多对密钥),则需要使用参数-i指定私钥文件
  • ssh [email protected] -i /path/to/your_id_rsa
  • scp也是一样,如:
  • scp -i /root/.ssh/id_rsa ./xxx 192.168.102.158:/home/wwy/bak
  • 因为默认情况下ssh命令会使用~/.ssh/id_rsa作为私钥文件进行登录,如果需要连接多台服务器而又不希望每次使用ssh命令时指定私钥文件,可以在ssh的客户端全局配置文件/etc/ssh/ssh_config(或本地配置文件~/.ssh/config, 如果该文件不存在则建立一份)中增加如下配置
  • IdentityFile /path/to/your_id_rsa.
  • 也可以为每个服务器指定一个Host配置:
  • Host 192.168.56.101
  • IdentityFile /path/to/your_id_rsa
  • 如果连接时出现如下的错误:Agent admitted failure to sign using the key
  • 则使用 ssh-add 指令將私鑰 加進來 (根据个人的密匙命名不同更改 id_rsa):
  • ssh-add ~/.ssh/id_rsa

安装sshpass指定明文密码

  • sudo apt-get install sshpass
  • 安装完成后使用sshpass允许你用 -p 参数指定明文密码,然后直接登录远程服务器。例如:
  • sshpass -p ‘你的密码’ ssh 用户名@服务器ip地址
  • 用 ‘-p’ 指定了密码后,还需要在后面跟上标准的 ssh 连接命令。

设置ssd_config文件,免密码登录

  • sshd的配置文件/etc/ssh/ssd_config
  • 可以设置为免密码登录,不推荐

你可能感兴趣的:(Shell)