zoukankan      html  css  js  c++  java
  • 编写shell脚本

    为了进一步提高效率,逐步用 linux 替代 windows,如果不会编写 shell 脚本则无法发挥命令行的优势。

    1 Shell脚本

    • Shell 有些独特,因为它不仅是一个功能强大的命令行接口,也是一个脚本语言解释器。
    • 一个 shell 脚本就是一个包含一系列命令的文件。shell 读取这个文件,然后执行 文件中的所有命令,就好像这些命令已经直接被输入到了命令行中一样。

    2 基本步骤

    为了成功地创建和运行一个 shell 脚本,我们需要做三件事情:

    1. 编写一个脚本hello_world。Linux并不根据后缀名判断文件类型,如果命名为hello_world.sh,执行的时候要输入完整的文件名。基本格式如下:
    #!/bin/bash
    # This is our first script.
    echo 'Hello World!'
    

    这个#!字符序列是一种特殊的结构,被用来告诉操作系统将执行此脚本所用的解释器的名字。每个 shell 脚本都应该把这一文本行 作为它的第一行。

    1. 使脚本文件可执行。 使用 chmod 命令,对于脚本文件,有两个常见的权限设置;权限为755的脚本,则每个人都能执行,和权限为700的 脚本,只有文件所有者能够执行。注意为了能够执行脚本,脚本必须是可读的。

    2. 把脚本放置到 shell 能够找到的地方。为了能够运行此脚本,我们必须指定脚本文件明确的路径,如下:

    [me@linuxbox ~]$ ./hello_world
    Hello World!
    

    如果没有给出可执行程序的明确路径名,那么系统每次都会搜索一系列的目录,来查找此可执行程序。这个目录列表被存储在一个名为 PATH 的环境变量中。这个 PATH 变量包含一个由冒号分隔开的目录列表。

    • 如果我们的脚本位于此列表中任意目录下,那么不明确指定脚本文件路径也可以运行。
    • 如果这个 PATH 变量不包含这个目录,我们能够手动添加。比如,在.bashrc 文件中包含下面这一行文本将~/bin加入到PATH变量:
    export PATH=~/bin:"$PATH"
    

    注意这个命令是向PATH中增加新的目录项,等号左侧是PATH变量,右侧是新的值,其中$PATH是对PATH的展开(后面会讲)。
    当做了这个修改之后,它会在每个新的终端会话中生效。为了把这个修改应用到当前的终端会话中, 我们必须让 shell 重新读取这个 .bashrc 文件。

    [me@linuxbox ~]$ . .bashrc
    

    这个点(.)命令是 source 命令的同义词,一个 shell 内建命令,用来读取一个指定的 shell 命令文件, 并把它看作是从键盘中输入的一样。

    3 保存位置

    ~/bin 目录是存放为个人所用脚本的好地方。
    如果我们编写了一个脚本,系统中的每个用户都可以使用它,那么这个脚本的传统位置是 /usr/local/bin
    系统管理员使用的脚本经常放到 /usr/local/sbin 目录下。
    大多数情况下,本地支持的软件,不管是脚本还是编译过的程序,都应该放到 /usr/local 目录下, 而不是在 /bin/usr/bin 目录下。

    4 格式技巧

    • 为了减少输入,当在命令行中输入选项的时候,短选项更受欢迎,但是当书写脚本的时候, 长选项能提供可读性。
    • 当使用长命令的时候,通过把命令在几个文本行中展开,可以提高命令的可读性。
    find playground 
        ( 
            -type f 
            -not -perm 0600 
            -exec chmod 0600 ‘{}’ ‘;’ 
        ) 
        -or 
        ( 
            -type d 
            -not -perm 0711 
            -exec chmod 0711 ‘{}’ ‘;’ 
        )
    

    5 变量

    • 当 shell 碰到一个变量的时候,它会自动地创建它。这不同于许多编程语言,它们中的变量在使用之前,必须显式的声明或是定义。
    • shell 不会在乎变量值的类型;它把它们都看作是字符串。

    6 展开

    6.1 命令展开

    把一个命令的输出作为一个展开模式来使用:

    [me@linuxbox ~]$ ls -l $(which cp)
    -rwxr-xr-x 1 root root 71516 2007-12-05 08:58 /bin/cp
    

    6.2 字符串展开

    • 基本用法
      所有的变量存储的都是字符串,我们在使用这些字符串时有时候需要进行简单的处理,比如只取一部分、去掉后缀名等等。下面的命令就是按照特定要求展开字符串:
    var = /home/me/video/maopian.avi
    
    命令格式 功能
    ${var} 完全展开字符串。
    ${#var} 展开为字符串var的长度。
    ${var:offset} 提取字符串var中第 offset 个字符到末尾的部分。
    ${var:offset:length} 提取字符串var中第 offset 个字符起 length 个字符。
    • 开头/末尾字符清除
    命令格式 功能
    ${var#pattern} 从 var 字符串中清除开头一部分匹配 pattern的文本。清除最短的匹配结果
    ${var##pattern} 清除最长的匹配结果
    ${var%pattern} 清除的文本从 var 所包含字符串的末尾开始
    ${var%%pattern} 清除的文本从 var所包含字符串的末尾开始
    • 查找替换。对 var 的内容执行查找和替换操作。如果找到了匹配通配符 pattern 的文本, 则用 string 的内容替换它。/string 可能会省略掉,这样会导致删除匹配的文本。
    命令格式 功能
    ${var/pattern/string} 只有第一个匹配项会被替换掉。
    ${var//pattern/string} 所有的匹配项都会被替换掉。
    ${var/#pattern/string} 要求匹配项出现在字符串的开头
    ${var/%pattern/string} 要求匹配项出现在字符串的末尾

    引用

    有时候我们的字符串中包含了可展开的形式,但我们本意不想让这部分展开。通过引用,我们可以禁止一部分字符串的展开。
    随着引用程度加强,越来越多的展开被禁止。如果需要禁止所有的展开,我们要使用单引号。
    以下例子是无引用,双引号,和单引号的比较结果:

    [me@linuxbox ~]$ echo text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
    text /home/me/ls-output.txt a b foo 4 me
    
    [me@linuxbox ~]$ echo "text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER"
    text ~/*.txt   {a,b} foo 4 me
    
    [me@linuxbox ~]$ echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER'
    text ~/*.txt  {a,b} $(echo foo) $((2+2)) $USER
    

    7 函数

    Shell 函数有两种语法形式:

    function name {
        commands
        return
    }
    

    name () {
        commands
        return
    }
    

    通过在变量名之前加上单词 local,来定义局部变量。这就创建了一个只对其所在的 shell 函数起作用的变量。在这个 shell 函数之外,这个变量不再存在。

    8 位置参数

    shell 提供了一个称为位置参数的变量集合,这个集合包含了命令行中所有独立的单词。

    #!/bin/bash
    # posit-param: script to view command line parameters
    echo "
    Number of arguments: $#
    $0 = $0
    $1 = $1
    $2 = $2
    $3 = $3
    $4 = $4
    "
    

    输出结果:

    [me@linuxbox ~]$ posit-param a b c d
    Number of arguments: 4
    $0 = /home/me/bin/posit-param
    $1 = a
    $2 = b
    $3 = c
    $4 = d
    

    即使不带命令行参数,位置参数 $0 总会包含命令行中出现的第一个单词,也就是已执行程序的路径名。
    实际上通过参数展开方式你可以访问的参数个数多于9个。只要指定一个大于9的数字,用花括号把该数字括起来就可以。 例如 ${10}、 ${55}、 ${211}等等。
    shell 还提供了一个名为 $#,可以得到命令行参数个数。
    正如位置参数被用来给 shell 脚本传递参数一样,它们也能够被用来给 shell 函数传递参数。
    shell 提供了两种特殊的参数。他们二者都能扩展成完整的位置参数列表.

    参数 描述
    $* 展开成一个从1开始的位置参数列表。当它被用双引号引起来的时候,展开成一个由双引号引起来 的字符串,包含了所有的位置参数,每个位置参数由 shell 变量 IFS 的第一个字符(默认为一个空格)分隔开。
    $@ 展开成一个从1开始的位置参数列表。当它被用双引号引起来的时候, 它把每一个位置参数展开成一个由双引号引起来的分开的字符串。

    “$@” 在大多数情况下是最有用的方法,因为它保留了每一个位置参数的完整性。

    9 条件判断

    if commands; then
         commands
    [elif commands; then
         commands...]
    [else
         commands]
    fi
    

    经常与 if 一块使用的命令是 test。这个 test 命令执行各种各样的检查与比较。 它有两种等价模式:

    test expression
    

    [ expression ]
    

    目前的 bash 版本包括一个复合命令,作为加强的 test 命令替代物。它使用以下语法:

    [[ expression ]]
    

    这个[[ ]]命令非常 相似于 test 命令(它支持所有的表达式),但是增加了一个重要的新的字符串表达式:

    string1 =~ regex
    

    如果 string1匹配扩展的正则表达式 regex,其返回值为真。

    10 case语句

    #!/bin/bash
    # case4-2: test a character
    read -n 1 -p "Type a character > "
    echo
    case $REPLY in
        [[:upper:]])    echo "'$REPLY' is upper case." ;;&
        [[:lower:]])    echo "'$REPLY' is lower case." ;;&
        [[:alpha:]])    echo "'$REPLY' is alphabetic." ;;&
        [[:digit:]])    echo "'$REPLY' is a digit." ;;&
        [[:graph:]])    echo "'$REPLY' is a visible character." ;;&
        [[:punct:]])    echo "'$REPLY' is a punctuation symbol." ;;&
        [[:space:]])    echo "'$REPLY' is a whitespace character." ;;&
        [[:xdigit:]])   echo "'$REPLY' is a hexadecimal digit." ;;&
    esac
    
    • case 语句使用的模式和路径展开中使用的那些是一样的。模式以一个 “)” 为终止符。
    • 还可以使用竖线字符作为分隔符,把多个模式结合起来。这就创建了一个 “或” 条件模式。
    • 添加的 “;;&” 的语法允许 case 语句继续执行下一条测试,而不是简单地终止运行。

    11 循环

    • while 语法举例
    #!/bin/bash
    # while-count: display a series of numbers
    count=1
    while [ $count -le 5 ]; do
        echo $count
        count=$((count + 1))
    done
    echo "Finished."
    
    • util 语法举例
    #!/bin/bash
    # until-count: display a series of numbers
    count=1
    until [ $count -gt 5 ]; do
        echo $count
        count=$((count + 1))
    done
    echo "Finished."
    
    • for 语法举例

    第一种形式:

    #!/bin/bash
    for i in A B C D; do 
        echo $i; 
    done
    

    第二种形式:

    #!/bin/bash
    # simple_counter : demo of C style for command
    for (( i=0; i<5; i=i+1 )); do
        echo $i
    done
    
  • 相关阅读:
    苹果快速的修复了Mac OS High Sierra 上出现了root的漏洞
    Codeforces Round #525 (Div. 2) C. Ehab and a 2-operation task
    2018CHD-ACM新生赛(正式赛)E.解救迷茫的草滩小王子
    2018CHD-ACM新生赛(正式赛)D.刀塔大师lwq I
    2018CHD-ACM新生赛(正式赛)C.绝望のRevue
    最小生成树——克鲁斯克算法+一道例题
    求连通分量个数+判定二分图
    动态规划——滚动数组(省内存)
    [BZOJ 1491] [NOI 2007] 社交网络
    SPOJ 8222 Substrings 后缀自动机
  • 原文地址:https://www.cnblogs.com/tofengz/p/12614470.html
Copyright © 2011-2022 走看看