shell基础

一、理解bash基础

默认的Linux shell------Bash(Bourne Again SHell)可以通过命令控制系统,执行文件操作,或者启动应用程序。它可以在命令行上交互式使用,或者你可以创建一个包含多个shell命令的文件,并像启动应用程序一样启动这个文件。

Bash的一些特性:

  • Bash命令行

    • 在命令行中输入的命令由命令和可选参数组成:

    在左侧是命令提示符,显示为tux@server1:~>。命令cp后面跟着参数,在这个例子中,是选项-a以及参数Photos和/tmp。按下回车键后,命令被执行。由于没有错误消息,说明命令执行成功。

    • 指示正常或预期行为的消息被写入标准输出(stdout,文件描述符1),在Bash的交互式使用中,标准输出与您输入命令的终端相连。

      在上面的命令行中,每个元素都被称为一个单词。单词(也称为标记)是由shell视为单个单位的字符序列。单词之间由空格、制表符或以下字符之一分隔:|、&、;、( )、<、>。

    • 错误信息被写入到标准错误(stderr 文件描述符 2),在交互式使用中,它也连接到您输入命令的终端上。

    • 任何对该进程的输入都来自标准输入(stdin,文件描述符 0),它通常与键盘相连。

    • 在Linux中,一个典型的程序在启动时会打开这三个文件描述符:标准输入(文件描述符0)、标准输出(文件描述符1)和标准错误(文件描述符2)。

    • 在终端内,标准输出(stdout)和标准错误(stderr)看起来是一样的,但它们实际上是不同的,这在你将它们重定向到文件时就能看出来。要将标准输出重定向到一个文件,使用 > 操作符(或者使用 >> 来追加到文件中)。

    • 还可以将标准错误重定向到文件中。如果有很多错误消息,或者在大量正常输出中有一些错误消息,这样做会特别有用。重定向标准错误允许你使用像 less 这样的分页器来查看消息。要重定向标准错误,你使用 2> 操作符。

      从以上两个例子中可以看到,当标准输出和标准错误都被重定向时,终端上不会写入任何输出。

    • 标准输入可以被重定向以从文件读取数据而不是从键盘读取,重定向标准输入的操作符是"<"。

    • 可以使用一条命令行将标准输出(stdout)和标准错误(stderr)重定向到不同的文件中。

    • • 也可以使用 2>&1 操作符将标准输出(stdout)和标准错误(stderr)重定向到同一个文件中,该操作符必须出现在命令行中标准输出重定向之后。

    • 如果想用一个命令来处理另一个命令的输出,你可以将第一个程序的输出写入一个文件,并将该文件作为第二个命令的输入。然而,你也可以使用管道操作符 | 将一个命令的输出直接作为另一个命令的输入,如下面邮件命令的例子所示。

    • 除了从文件中读取,还可以使用所谓的"here document"(内联文档),通过 << 重定向符,让shell从当前源读取,如下所示。

      当在一行中出现与开始标记相同的字符串(在上面的例子中是EOF),且该字符串后没有尾随空白字符时,cat << EOF 行之后的文本就会被打印出来。这种语法在脚本中经常被用来向屏幕输出多行文本。

    • 重定向

      • > 将stdout重定向到文件中。
      • >> 重定向stdout,附加到文件。
      • 2> 将stderr重定向到文件中。
      • 2>> 重定向stderr,附加到文件。
      • < 从文件中重定向 stdin。
      • | 将一个命令的stdout用作另一个命令的stdin。
      • tee 将stdout重定向到文件和终端。例如:<command> | tee <file>
  • Bash变量

    Shell 变量用于控制 shell 本身的行为。其中一些变量由 Bash 分配默认值,而另一些变量可以由 Bash 启动时读取的启动脚本(如 /etc/profile 或 ~/.bashrc)分配值。Bash变量有以下三种:

    • shell变量

      • Shell 变量用于控制 shell 本身的行为。

      • 其中一些变量由 Bash 分配默认值,而另一些变量则可以在 Bash 启动时,通过其读取的启动脚本来分配值:

        • IFS(Internal Field Separator,内部字段分隔符):这是一个字符列表,用于分隔字段,并确定单词(标记)的开始和结束。

        • PS1(Primary Prompt String,主提示符字符串):这个字符串决定了你在终端窗口中看到的正常提示符的外观。

        • BASH:这是执行当前 Bash 实例时使用的完整路径名。

        • HISTSIZE:这是历史列表中保留的命令的最大数量。

        要了解 shell 变量的完整说明,请查看 man bash 手册页,并搜索"Shell Variables"(Shell 变量)。

    • 位置参数

      • $0:命令本身
      • $1:第一个参数
      • $2:第二个参数
      • $3:第三个参数
      • $*:将所有的位置参数视为一个单词
      • $@:将所有的位置参数视为单独的单词
        当调用一个命令或脚本时,$0 参数会被赋予命令或脚本的名称。之后的第一个参数是 $1,第二个参数是 $2,以此类推。如果你想引用所有的位置参数,你可以使用 $*(将所有位置参数视为一个单词)或 $@(将所有位置参数视为单独的单词)。
        你可以在脚本内部引用这些位置参数。
        示例:
    • 环境变量

      • 环境变量可以用来调节 Bash 的行为。
      • 环境变量包括以下几类:
        • USER:调用 shell 的用户。
        • MANDIR:搜索手册页的目录。
        • LS_COLORS:用于 ls 命令输出的颜色。
          默认情况下,会设置各种环境变量。你可以使用 export 和 set 命令来查看它们及其内容。
  • 返回值

    • 每个命令都会向调用它的shell返回一个值,该值表示程序是正常终止(返回0)还是出现了错误(返回非0值)。

    • 使用echo命令,你可以查看$?变量的内容:

    • 通过使用返回值,你可以使第二个命令的执行依赖于第一个命令的结果。要使用的操作符是 &&(如果第一个命令返回 0,则执行第二个命令)或 ||(如果第一个命令返回的值不同于 0,则执行第二个命令)。在脚本中,如何继续执行通常基于命令的返回值。

    二、使用基本脚本元素

    脚本的基本元素分为以下两项:

    • shell脚本要素
    • debug选项
  • shell脚本要素

    • Shell脚本基本上是一个ASCII文本文件,其中包含要按顺序执行的命令。
    • 你可以使用类似以下的命令来运行脚本:
      bash script.sh
    • 要将其作为程序运行,重要的是为运行它的用户设置脚本文件的权限,使其具有r(可读)和x(可执行)权限。
    • 要赋予这种权限,请使用类似以下的命令:
      chmod +x script.sh
    • 然后,你可以这样运行它:
      ./script.sh
      新创建的文件默认不会授予执行权限。要赋予这种权限,需要使用如下命令:
      chmod +x script.sh
      也可以使用如下命令从另一个Shell环境中执行脚本:
      bash script.sh
      如果以这种方式运行,则无需使脚本具有可执行权限。
      在SUSE Linux Enterprise中,/bin/sh是指向/bin/bash的链接。当以sh script.sh的方式调用时,某些Bash功能将不可用,如果脚本依赖于这些功能中的某些功能,则可能无法按预期工作。
      如果想仅通过脚本的名称来运行它,那么包含该脚本的目录必须列在PATH变量中。在SUSELinuxEnterpriseServer12中,如果用户的主目录中存在bin目录,则该目录默认包含在PATH中。
      对于未列在$PATH中的目录中的Shell脚本,必须使用完整路径名或相对路径名(如./script.sh)来启动。
    • 在命名脚本文件时,给文件名添加一个.sh扩展名是一个好主意。虽然Linux系统并不强制要求这样做,但这可以确保系统管理员能够轻松地将该文件识别为Shell脚本。
    • 如果你没有添加这个后缀,那么你需要确保文件名不与现有的命令重名。例如,一个常见的错误是将脚本命名为"test",这会与命令行工具test产生冲突。
    • 在脚本中,空行和以#字符开头的行会被忽略。#字符用于在脚本中添加注释。通常,你应该在脚本开头添加一个注释,简要概述脚本的用途,并在整个脚本中添加注释来解释每一行或每一部分的作用。这样,当你或其他人在一段时间后回来修改脚本时,可以更容易地理解脚本的内容。
    • 脚本的第一行定义了用于执行脚本的Shell。这一行有时被称为she-bang行(#!行)。尽管它以#字符开头,但只有这一行会被解释器识别。它的语法如下:
      #!/路径/解释器
      例如:
      #!/bin/bash
    • 脚本的所有后续行要么是注释(以#字符开头),要么是空行,要么是实际的命令。
    • 脚本小示例:
      -通过rsync来同步备份主目录。

      用rsync作为备份的核心命令。rsync能够高效地将文件从一个目录复制到另一个目录,或者通过网络从一台机器复制到另一台机器。它的主要优势在于,在更新备份时,只复制文件之间的差异部分,而不是整个文件,从而显著加快了更新速度。
      rsync可以通过多种选项进行控制。因此,即使我们的脚本只包含一个命令,它也能节省一些输入,因为每次调用脚本时都不需要再次输入这些选项。该脚本的作用是将用户的家目录复制到/backup目录。
      你需要的元素包括she-bang行(即脚本的首行声明,指明解释器路径)、一个解释脚本功能的注释,以及rsync命令本身。脚本可能类似于上图所示。
      -a(archive,归档)选项确保权限得到保留,并且目录会被递归地复制。--no-whole-file选项确保只更新文件的更改部分,而不是复制整个文件。这在初次复制时并无差异,但能够加快更新速度。
  • debug选项

    如果脚本没有按照你的预期执行,你需要找到错误所在。

    有几种方法可以让shell输出更多信息,帮助你找到错误:

    • 在脚本的第一行添加-x选项:
      #!/bin/bash -x
      这样做会在脚本的每一行命令执行前输出该命令,从而帮助你跟踪脚本的执行过程。
    • 使用-x选项在单独的shell中启动脚本:
      bash -x script.sh
      这种方法的好处是你不需要修改脚本本身。它会在脚本执行时输出每一行命令及其参数。
    • 在当前shell中使用set -x命令:
      set -x
      这样做会为从这个shell启动的所有脚本开启额外的输出。你可以使用set +x命令再次关闭这个选项。这种方法同样不需要修改脚本本身。

三、编写脚本的常用方法

  • 变量和命令替换

  • 控制结构

    • if语句


      有时会看到 test 命令的不同语法。除了使用 test "?" -eq 0 外,还可以省略 test 命令,并将表达式放在方括号内,如 \[ "?" -eq 0 ]。请注意,方括号 [ 后面和方括号 ] 前面都要有空格。如果没有这些空格,执行脚本时会收到错误消息。

      例如:

      if语句中的test:

      test - 检查文件类型和比较值。

        • -help 显示此帮助信息并退出
        • -version
          输出版本信息并退出
      • 如果省略EXPRESSION,则默认为假。否则,EXPRESSION为真或假,并设置退出状态。它可以是以下之一:
        • ( EXPRESSION )
          EXPRESSION为真
        • ! EXPRESSION
          EXPRESSION为假
      • EXPRESSION1 -a EXPRESSION2
        EXPRESSION1和EXPRESSION2都为真(注意:建议使用'&&'代替'-a',以避免歧义)
      • EXPRESSION1 -o EXPRESSION2
        EXPRESSION1或EXPRESSION2为真(注意:建议使用'||'代替'-o',以避免歧义)
      • -n STRING
        STRING的长度非零
      • STRING 等价于 -n STRING
      • -z STRING
        STRING的长度为零
      • STRING1 = STRING2
        字符串相等
      • STRING1 != STRING2
        字符串不相等
      • INTEGER1 -eq INTEGER2
        INTEGER1等于INTEGER2
      • INTEGER1 -ge INTEGER2
        INTEGER1大于或等于INTEGER2
      • INTEGER1 -gt INTEGER2
        INTEGER1大于INTEGER2
      • INTEGER1 -le INTEGER2
        INTEGER1小于或等于INTEGER2
      • INTEGER1 -lt INTEGER2
        INTEGER1小于INTEGER2
      • INTEGER1 -ne INTEGER2
        INTEGER1不等于INTEGER2
      • FILE1 -ef FILE2
        FILE1和FILE2具有相同的设备和inode号
      • FILE1 -nt FILE2
        FILE1比FILE2新(修改日期)
      • FILE1 -ot FILE2
        FILE1比FILE2旧
      • -b FILE
        FILE存在并且是块特殊文件
      • -c FILE
        FILE存在并且是字符特殊文件
      • -d FILE
        FILE存在并且是目录
      • -e FILE
        FILE存在
      • -f FILE
        FILE存在并且是常规文件
      • -g FILE
        FILE存在并且设置了组ID
      • -G FILE
        FILE存在并且由有效组ID拥有
      • -h FILE
        FILE存在并且是符号链接(与-L相同)
      • -k FILE
        FILE存在并且设置了粘滞位
      • -L FILE
        FILE存在并且是符号链接(与-h相同)
      • -O FILE
        FILE存在并且由有效用户ID拥有
      • -p FILE
        FILE存在并且是命名管道
      • -r FILE
        FILE存在并且授予了读取权限
      • -s FILE
        FILE存在并且大小大于零
      • -S FILE
        FILE存在并且是套接字
      • -t FD
        文件描述符FD在终端上打开
      • -u FILE
        FILE存在并且设置了用户ID位
      • -w FILE
        FILE存在并且授予了写入权限
      • -x FILE
        FILE存在并且授予了执行(或搜索)权限
        除了-h和-L之外,所有与FILE相关的测试都会取消引用符号链接。请注意,对于shell,括号需要使用转义字符(例如,反斜杠)进行转义。INTEGER也可以是-l STRING,它评估为STRING的长度。
        注意 :二元运算符-a和-o本身具有歧义。建议使用'test EXPR1 && test EXPR2'或'test EXPR1 || test EXPR2'代替。
        注意 :[命令支持--help和--version选项,但test不支持。test将这些选项视为任何其他非空STRING一样处理。
        注意:您的shell可能拥有自己版本的test和/或[,这通常优于此处描述的版本。请参考您的shell文档,了解它支持的选项的详细信息。
    • case语句

    • for循环

      "in"后面定义的列表不一定是静态的。for循环经常用于遍历文件列表。要做到这一点,一个简单的方法是在"in"后面使用"*"。

      这个脚本会遍历当前工作目录下的所有文件,并且(在移除了用于测试脚本而不实际影响任何文件的mv命令前的echo语句后)将这些文件的名称从大写改为小写。"*"会被shell扩展为所有这些文件的列表。

      在循环的每一次迭代中,变量$i会包含一个文件名。该文件名会被转换为小写,并存储在变量lower中。然后,使用mv命令将原始文件重命名为小写形式。

      注意:这只是一个演示脚本。在实际使用的脚本中,需要添加一些代码来确保不会意外地覆盖现有的小写文件。

    • while和until循环

      两种循环类型都依赖于一个条件。在 while 循环中,只要条件为真,就会执行命令;而在 until 循环中,只要条件为假就会执行命令------直到条件变为真为止。

      可以在"do"和"done"之间嵌套一个if控制结构,并在满足某个条件时退出while循环。用于中断while循环处理的命令是break。

      也可以使用continue命令跳过循环的进一步处理,并进入下一次迭代。

  • 读取用户的输入

  • 使用数组

数组基本上是能够存储多个值的变量。为了标识数组中的值,我们使用数字索引。索引写在数组名称后的方括号中:

lines[0]="Hello World"

这行代码将字符串"Hello World"分配给名为lines的数组的第0个索引。

要访问数组中的值,您必须指定一个索引,并用大括号将数组名称括起来,如上所示。

数组对于存储列表数据非常有用,比如文件列表、名称列表或类似的数据。

  • 脚本中的time

    在$符后面的括号内,date命令有着非常丰富的格式,详情可参见man date或者其他朋友们的博客均有记载。

    也可以在命令中嵌套date命令来使用。注意括住date命令的是英文状态下esc键下方的`。
  • 算数运算符
    Shell脚本通常使用赋给变量的值进行计算。
    • Bash通常将变量视为文本,因此算术运算需要特殊的语法

    • Bash shell内置了对算术运算的支持

    • Bash的算术功能在以下方面受到限制:

      • 只能执行整数的运算。
      • 所有值都是有符号的64位值。可能的值范围从-263到+263-1。
    • 使用外部命令如bc进行浮点计算

    • 算术运算的可能方法和格式如下所列

    • 它们都是基于这个示例操作:

      B=5 A=B+10

    • 使用外部命令expr(与Bourne shell兼容),在使用Bash时,可能需要使用外部命令(如bc)来进行浮点计算。虽然使用外部命令的脚本总是会比依赖内置命令的脚本执行得慢一些,但外部命令也适用于传统的Bourne shell。

      A=$(expr $B + 10)

    • 使用Bash内置命令let, 在Bash中,可以使用let命令来执行算术表达式。

      let A="$B + 10"

    • 使用括号内的算术表达式

      A=$((B + 10))

    • 使用内置命令declare

      declare -i A

      declare -i B

      A=B+10

    • 算术表达式可以用双括号(( ))或方括号[ ]来由Bash进行扩展。((...))是可行的,但[...]已被认为过时,应避免使用。

      使用内置命令declare和选项-i可以声明一个变量为整数。

      如果计算中涉及的所有变量之前都已通过declare -i声明为整数,那么在给这些变量赋值时,它们的算术求值会自动进行。

      这意味着在上面的示例中,变量B在求值时不需要在其前面加上符号(但实际上,在赋值语句中通常不需要前缀,而在引用变量值时则需要)。不过,这里的表述可能有些混淆,因为在赋值操作中(如B=5或B=((A+10))),我们并不对变量进行"求值",而是在赋值表达式中可能包含算术运算。当使用((...))进行算术扩展时,括号内的表达式会被求值,并且结果可以赋值给变量。

    • 使用expr命令时,只有以下五个操作符可用:

      • *
      • \
      • % (模,一个除法的剩余部分)
  • shell Functions (函数)

    带有暂停功能函数:
  • 子脚本

注意:命令typeset -f可以显示当前shell中定义的所有函数。

  • shell处理的常用工具
    • grep

      grep用于以某种模式搜索文件,格式如下:

      grep 搜索模式 文件1 文件2 ......

      若要搜索更复杂的模式,请使用-E选项

      E选项所使用的正则表达式需要符合标准的regex语法

    • sed

      • sed 程序是一个流编辑器,它通常在命令行中使用,而不是以交互方式使用。sed 按行对文本进行转换。
      • 可以直接在命令行中指定 sed 命令,或者在一个特殊的命令脚本中指定,然后在程序执行时加载该脚本。
      • sed命令的格式:

        sed editing-command filename

      • 可用的编辑命令包括单字符参数,例如:
        d 删除
        s 替换
        p 将结果行再输出一遍
        n 结合p使用,仅输出结果行
        a 在后面追加
        #sed '/tux/a\ tux' passwd (在tux行后另起一行追加tux)
      • 以下是一些重要的sed命令行选项:
        • -n, --quiet, --silent
          默认情况下,sed 会在处理完所有行后将它们打印到标准输出。此选项会抑制输出,因此 sed 仅打印那些通过 p 编辑命令明确重新启用打印功能的行。
        • -e command1 -e command2 ...
          当指定两个或多个编辑命令时,此选项是必需的。它必须插入在每个附加的编辑命令之前。
        • -f filename
          使用此选项,您可以指定一个脚本文件,sed 应从中读取其编辑命令。
        • -i extension
          原地编辑,并可选地使用指定扩展名备份原始文件。
        • 每个 sed 命令前都必须有一个精确的地址或地址范围,指定编辑命令适用的行。最常用的地址标签之一是 $,它表示最后一行。
      • 以下是sed命令的两个示例:
        • 仅在stdout上打印第1行至第9行:
          #sed -n '1,9p' somefile
        • 删除从第10行到文件末尾的所有内容,并打印某文件的前9行。

          sed '10,$d' somefile

      • 可以使用正则表达式来为编辑命令定义地址或地址范围。正则表达式必须用正斜杠括起来。如果地址是用这样的表达式定义的,那么 sed 会处理包含给定模式的每一行。下面是一个使用正则表达式的例子:

        sed -n '/Murphy.*/p' somefile

        这个示例会打印所有包含模式 Murphy.* 的行。
      • 如果要让sed对同一个地址执行多个编辑命令,则需要用括号括住这些命令:
        #sed '1,10{command1 ; command2}'
      • 更多的信息查看man sed手册。
    • tr

      • tr命令翻译(替换)或删除字符。
      • 以下是tr的标准语法:
        #tr set1 set2
      • 以下是使用tr命令的示例:
        #cat text-file | tr a-z A-Z (将text-file文件中的所有字母全部大写并输出)
      • 可以使用tr从文件中删除所有set1的字符,输入以下内容:
        #tr -d set1
      • tr命令删除了变量VAR原始值中的百分号,并将结果作为新值赋给同一个变量。

        VAR=$(echo $VAR | tr -d %)

      • tr命令用于将连续的重复字符压缩为单个字符,并输出到标准输出。
      • 将所有连续的空格压缩为一个空格,并删除了多余的空格。
    • cat

      • 当与运算符(<<)结合使用时,cat命令是输出脚本中多行文本的一个好选择。在交互式使用中,该命令通常以一个文件名作为参数来运行,在这种情况下,cat会将文件内容打印到标准输出上。
    • cut

      • cut命令用于从文件中截取行的部分,以便仅将指定部分打印到标准输出上。该命令适用于文件中或标准输入中可用的每一行文本。您可以使用cut -f来截取文本字段。cut -c则用于截取指定的字符。
        可以指定单个部分(字符或字段)或多个部分。字段之间的默认分隔符是制表符,但可以使用-d选项来指定不同的字段分隔符。
      • 例如:
        • cut -c 字符位置 [选项] 文件名

    • date

      date命令允许你几乎以任何细节更改输出格式。使用-I选项(如上面的第一个示例所示),date会以ISO格式打印日期和时间(这与使用+%Y-%m-%d选项的效果相同)。

      要查看date命令所有可能的格式选项列表,请查阅man date手册页。应该能够自定义输出,以精确满足脚本要求。

    • test

      test命令既作为内置命令存在,也作为外部命令存在。它用于比较值以及检查文件及其属性(例如,文件是否存在,是否可执行等)。

      如果测试的条件为真,test会返回退出状态0;如果条件不为真,则退出状态为1。在shell脚本中,test主要用于声明条件,以影响循环、分支和其他语句的操作。

      • 格式:

        test 条件 or 条件

      • 可以使用测试命令执行以下操作:-测试文件是否存在。以下是一些可用选项:
          • d:测试目录
          • f: 测试普通文件
          • e:测试文件
          • x:测试执行文件
      • 比较两个文件。以下是一些可用的操作符:
        • -ef:指同一个inode,比如硬链接
        • -nt:比新
        • -ot:比旧
      • 比较两个整数,可用的运算符有:
        • -eq:等于
        • -ge:大于或等于
        • -gt:大于
        • -le:小于或等于
        • -lt:小于
        • -ne:不等于
      • 测试字符串。以下是一些可用的运算符:
        • test -z string :如果字符串长度为零(为空),则退出状态为0(真)
        • test string:如果字符串长度不为零(至少包含一个字符),则退出状态为0(真)
        • test -n string:如果字符串长度不为零(至少包含一个字符),则退出状态为0(真)
        • test string1 = string2 :如果字符串相等,退出状态为0(真)
        • test string1 != string2:如果字符串不相等,则退出状态为0(真)
      • 结合测试。以下是一些可用的操作:
        • test ! condition :如果条件不成立,退出状态为0(真)
        • test condition1 -a condition2:如果两个条件都为真,则退出状态为0(真)
        • test condition1 -o condition2: 如果任一条件为真,则退出状态为0(真)
      • 更多信息参考man test吧。
    • echo

      • 名称
        echo - 将一行文本输出到标准输出。
      • 概要
        echo [短选项]... [字符串]...
        echo 长选项
      • 描述
        将指定的字符串(STRING(s))输出到标准输出。
        -n 不输出尾随的换行符
        -e 启用反斜杠转义序列的解释
        -E 禁用反斜杠转义序列的解释(默认)
        --help 显示此帮助信息并退出
        --version
        输出版本信息并退出
      • 如果启用了-e选项,将识别以下序列:
        \ 反斜杠
        \a 警报(BEL)
        \b 退格
        \c 不产生进一步的输出
        \e 转义
        \f 换页
        \n 新行
        \r 回车
        \t 水平制表符
        \v 垂直制表符
        \0NNN 具有八进制值NNN(1到3位数字)的字节
        \xHH 具有十六进制值HH(1到2位数字)的字节
        注意:您的shell可能自带一个echo版本,该版本通常会覆盖这里描述的版本。有关它支持的选项的详细信息,请参阅您的shell的文档。
    • getopts

      #!/bin/bash

      初始化选项字符串

      options="a:b:c"

      初始化变量

      a_option=""
      b_option=""
      c_option="false"

      解析命令行参数

      while getopts "$options" opt; do
      case opt in a) a_option=OPTARG
      echo "Option a with argument: a_option" ;; b) b_option=OPTARG
      echo "Option b with argument: b_option" ;; c) c_option="true" echo "Option c without argument" ;; \?) echo "Invalid option: -OPTARG" >&2
      exit 1
      ;;
      :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
      esac
      done

      移动参数指针,跳过已解析的选项

      shift $((OPTIND - 1))

      处理剩余的参数

      remaining_args=("$@")
      echo "Remaining arguments: ${remaining_args[@]}"

    • shift
      在Shell脚本中,shift 命令用于对位置参数进行左移操作。位置参数(即传递给脚本的参数)在默认情况下被命名为 $1, $2, $3 等等。当你执行 shift 命令时,所有的位置参数都会向左移动一个位置,即 $2 的值会移动到 $1,$3 的值会移动到 $2,依此类推。而原来的 $1 的值会被丢弃。
      shift 命令通常用于循环处理位置参数,每次循环中处理第一个参数(即 $1),然后使用 shift 将所有参数左移,以便在下一次循环中处理下一个参数。
      shift 命令还可以接受一个可选的数字参数,表示左移的位数。例如,shift 2 会将 $3 的值移动到 $1,$4 的值移动到 $2,依此类推,而 $1 和 $2 的值会被丢弃。
      下面是一个简单的示例,展示了如何使用 shift 命令在循环中处理位置参数:

      #!/bin/bash

      检查是否提供了参数

      if [ $# -eq 0 ]; then
      echo "Usage: $0 arg1 arg2 arg3 ..."
      exit 1
      fi

      while [ $# -gt 0 ]; do
      echo "Processing $1"

      在这里可以对 $1 进行任何需要的处理

      例如,可以将其添加到一个数组或进行其他操作

      将所有参数左移一位

      shift
      done

在这个脚本中,while 循环会一直执行,直到没有位置参数为止($# 表示位置参数的数量)。在每次循环中,脚本都会处理 $1(即当前的第一个参数),然后使用 shift 将所有参数左移一位,以便在下一次循环中处理下一个参数。

相关推荐
运维&陈同学6 分钟前
【HAProxy05】企业级反向代理HAProxy调度算法之静态算法与动态算法
linux·运维·算法·nginx·云原生·负载均衡·lvs·haproxy
默闻革12 分钟前
Nginx报错unknown directive “ssl“
运维·nginx·ssl
关李屁氏13 分钟前
如何编写jenkins的流水线
运维·jenkins
Narutolxy17 分钟前
深入解析 CentOS 7 上 MySQL 8.0 的最佳实践20241112
linux·mysql·centos
BillKu18 分钟前
Linux(CentOS)项目总结(前后端分离)
java·linux·mysql·nginx·centos
angleboy820 分钟前
【全面系统性介绍】虚拟机VM中CentOS 7 安装和网络配置指南
linux·运维·centos
孤邑27 分钟前
【Linux】网络编程3
linux·服务器·网络·笔记·学习
夏天匆匆2过30 分钟前
linux性能提升之sendmmsg和recvmmsg
linux·c++·单片机·网络协议·udp·tcp
Diamond技术流33 分钟前
从0开始学习Linux——文件管理
linux·运维·学习·安全·文件·权限·极限编程
ziqibit1 小时前
debian 系统更新升级
运维·debian