在bash脚本(for循环)中使用正则表达式

4

我想使用for循环解析传递给Shell脚本的参数。假设现在有3个参数,类似于 for i in $1 $2 $3 应该可以胜任,但我无法预测参数数量,因此我希望使用一个正则表达式来获取范围,并使用$#作为最后一个参数的编号。我不知道如何在for循环中使用这些正则表达式,我尝试了以下代码: for i in $[1-$#] 它并不能起到作用。循环只运行1次,而且1-$#被计算,而不是作为正则表达式使用。

3个回答

7

基础

如果您没有指定 in 子句,for 循环默认会循环遍历命令行参数:

for arg; do
    echo "$arg"
done

如果您想明确地获取所有参数,则可以使用"$@"。上述循环等同于:

for arg in "$@"; do
    echo "$arg"
done

从bash手册中得知:

特殊参数

$@ — 扩展为位置参数,从一开始。当扩展发生在双引号内时,每个参数都会扩展为一个单独的单词。也就是说,"$@" 等价于 "$1" "$2" ...。如果双引号扩展发生在一个单词内部,则第一个参数的扩展将与原始单词的起始部分连接,最后一个参数的扩展将与原始单词的最后部分连接。当没有位置参数时,"$@"$@ 扩展为空(即它们被删除)。

高级

对于重度参数处理,getopt + shift 是正确的方式。 getopt 将预处理命令行,使用户可以以灵活的方式指定参数。例如,它将 -xzf 扩展为 -x -z -f。它在所有标志之后添加一个 -- 参数,用于将标志与文件名分开;这使您可以运行 cat -- -my-file 来显示 -my-file 的内容,而不会因前导破折号而出错。

尝试使用以下样板代码:

#!/bin/bash

eval set -- "$(getopt -o a:bch -l alpha:,bravo,charlie,help -n "$0" -- "$@")"

while [[ $1 != -- ]]; do
    case "$1" in
      -a|--alpha)
        echo "--alpha $2"
        shift 2
        ;;

      -b|--bravo)
        echo "--bravo"
        shift
        ;;

      -c|--charlie)
        echo "--charlie"
        shift
        ;;

      -h|--help)
        echo "Usage: $0 [-a ARG] [-b] [-c]" >&2
        exit 1
        ;;
    esac
done
shift

请注意,每个选项都有一个短和长的等价物,例如-a--alpha-a标志需要一个参数,因此在getopt调用中指定为a:alpha:,并在其case末尾具有shift 2

Bash内置的getopts更可取,而不是外部的getopt,因为后者“无法处理空参数字符串或带有嵌入式空格的参数”。 - Dennis Williamson
@Dennis,这在现今已经不是真的了。请查看man getopt以查看您是否拥有新的增强版本,可以填补这些漏洞:“传统的getopt(1)实现无法处理参数和非选项参数中的空格和其他(特定于shell的)特殊字符。为了解决这个问题,此实现可以生成带引号的输出,必须再次由shell解释...” - John Kugelman
如果向后兼容性很重要,您可以使用“-T”标志测试您的getopt是否已增强。如果是,则返回退出状态4;较旧的getopt将以0退出。 - John Kugelman
@John:谢谢你提供的信息。我在手册中没有注意到这一点。 - Dennis Williamson
@Dennis,第一次阅读FAQ条目时我感到非常困惑。语言非常强烈:“永远不要使用getopt(1)。”加粗了。“请忘记它曾经存在过。”如果FAQ能够用“除非你有GNU的getopt,那就完全没问题!”来限定这个建议,那就太好了! - John Kugelman

1
另一种迭代参数的方法更接近你正在努力实现的功能,它可能类似于:
for ((i=1; i<=$#; i++))
do
    echo "${@:i:1}"
done

但是,John Kugelman展示的for arg语法明显更可取。然而,在某些情况下,数组切片也很有用。此外,在这个版本中,就像John的版本一样,参数数组保持不变。使用shift会丢弃其元素。

你应该注意,你试图使用方括号做的并不是正则表达式。


0

我建议做其他事情代替:

while [ -n "$1" ] ; do
  # Do something with $1
  shift
  # Now whatever was in $2 is now in $1
done

shift 关键字将 $2 的内容移动到 $1,$3 移动到 $2,以此类推。

假设参数如下:

a b c d

shift之后,参数现在为:

b c d

使用while循环,您可以解析任意数量的参数,甚至可以执行以下操作:

while [ -n "$1" ] ; do
  if [ "$1" = "-f" ] ; then
    shift
    if [ -n "$1" ] ; then
      myfile="$1"
    else
      echo "-f needs an additional argument"
    end
  fi
  shift
done

想象参数是一个数组,并且 $n 是该数组的索引。`shift` 删除第一个元素,所以索引 1 现在引用了在 `shift` 之前索引为 2 的元素。希望您能理解我想说的话。

这是一种写简单的 for 循环的困难方式,而且不需要使用 shift 等操作。 - Jonathan Leffler
我明白了,这应该是一种更好的解析参数的方法。谢谢,当我有更多的参数时,我会使用它。 - default-user
@Jonathan Leffler:是的,for arg in "$@" 更容易,但如果你正在进行“严格”的参数解析,它就不够灵活了...你必须将中间值存储在临时变量中,这使得整个工作比使用 while 循环更加困难。 - DarkDust
如果用户实际上传递了一个空字符串参数:“a b“”d”,那么该while循环可能会过早终止。 - glenn jackman
为了避免@glenn指出的问题,您可以使用while (( $# > 0 ))while [ $# -gt 0 ] - Dennis Williamson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接