【问题标题】:Passing a Bash Array Element to an Awk Regex Expression将 Bash 数组元素传递给 Awk 正则表达式
【发布时间】:2012-07-14 06:20:28
【问题描述】:

我发现了几个关于如何将变量从bash 传递到awk 的问题,最值得注意的是-v 命令,但我似乎无法让它们做我想做的事情。

在脚本之外,我正在运行的命令是

awk '$2 ~ /^\/var$/ { print $1 }' /etc/fstab

/etc/fstab 中搜索/var 分区,并且应该打印出物理挂载点,或者如果没有,则什么都没有。

现在在脚本中我有一个包含许多分区的数组,我想做的是遍历该数组以搜索 fstab 中的每个物理挂载点。问题在于数组中的元素有一个/

所以我想要做的(在非常不正确的 awk 中)是:

PARTITIONS=(/usr /home /var tmp);
for ((n=0; n<${#PARTITION[@]}; n++)); do
    cat /etc/fstab | awk '$2 ~ /^\${PARTITIONS[$n]}$/ { print $1 }';
done

但我知道那是不正确的。我现在最接近的是:

PARTITIONS=(/usr /home /var tmp);
for ((n=0; n<${#PARTITION[@]}; n++)); do
    cat /etc/fstab | awk -v partition="${PARTITIONS[$n]}" '$2 ~ /^\/var$/ { print $1," ",partition }';
done

至少将分区变量放入 awk,但对匹配它完全没有帮助。

所以基本上,我需要输入阵列,然后取出物理分区。最终结果将被分配给另一个数组,但是一旦我得到输出,我就可以从那里开始。

我也知道 awk 可以在一开始就消除对 cat 的需求,但我对 awk 的了解还不够,无法做到这一点。 :)

感谢您的帮助。

编辑

cat /etc/fstab | awk -v partition="${PARTITIONS[$n]}" '$2 ~ partition { print $1 }'

大概是我需要的足够有用的东西。我显然过于关注包含正则表达式。如果其他人可以清理它,将不胜感激:)

【问题讨论】:

  • 附带说明,我也尝试在正则表达式中使用 ENVIRON[${PARTITIONS[$n]}],但这让 awk 非常不高兴。
  • 你不能通过环境传递数组。您可以传递标量变量,但它们需要在 shell(脚本)中导出。在 AWK 中访问变量的语法是 ENVIRON["varname"](没有大括号或美元符号)。
  • @DennisWilliamson 你有这方面的参考吗?

标签: regex arrays bash variables awk


【解决方案1】:

你可以这样做:

awk -v partitions="${PARTITIONS[*]}" '
    BEGIN { split(partitions,a," ") }
    { for (e in a) { if ($2 ~ a[e]) { print $1 } } }' /etc/fstab 

因此您无需在awk 之外创建for 循环,这意味着更少的进程。

【讨论】:

  • if ($2 ~ e) 应该是if ($2 ~ a[e])e 是数组的数字索引。
【解决方案2】:

首先,为了解决最烦人的事情(GUoC),awk 可以像cat 一样处理文件,所以直接传递即可。您不能通过-v unflattened 传递整个数组,但是由于您正在迭代项目,所以没关系。如果你想避免-v,你可以通过直接将它们包含到awk脚本中来传递bash变量,你只需要小心引用(空格和awk自己的$variable用法)。例子:

awk '$2 ~ "'${PARTITIONS[$n]}'" { print $1 }' /etc/fstab

或者带有软引号的更复杂的版本:

awk "\$2 ~ /${PARTITIONS[$n]//\//\\/}/ { print \$1 }" /etc/fstab

【讨论】:

    【解决方案3】:

    以下是一些可能有所帮助的观察结果。

    如果输入只是一个文件,则不需要cat 任何东西。那就是:

    $ cat file | program # would normally just be ...
    $ program < file
    

    如果您需要向awk(1) 提供一些复杂的内容,那么也许您确实有cat x | y 的用例...您可以执行类似...

    (echo StartFlag ${PARTITIONS[*]}; cat /etc/fstab) | awk ...
    

    最后,为了获得最佳结果,请以 ...

    1. 我的 PARTITIONS bash 变量包含 简化的示例内容
    2. 假设我的 /etc/fstab 包含 fstab 的简化示例
    3. 如何获得以下输出:基于简化输入的准确期望输出
    4. 这是我尝试过的:有些人不会只提供代码,它有助于就您试图找到解决方案的特定编程问题寻求帮助

    【讨论】:

      【解决方案4】:
      awk -v partition="${partitions[$n]}" '$2 ~ "^/" partition "$" { print $1 }' /etc/fstab
      

      您可以连接您的正则表达式字符(^ - 字符串开头和 $ - 字符串结尾)和作为分区名称一部分的斜杠和包含分区名称的变量,方法是将它们彼此相邻放置.您不需要使用斜线来分隔硬编码的正则表达式。

      AWK 将接受文件名作为参数,而不使用cat 来管道它或使用&lt; 来重定向它。

      我建议习惯于在 shell 中使用混合或小写的变量名,以避免与 shell 或环境变量的潜在名称冲突。

      【讨论】:

      • 非常感谢!而且我什至没有考虑碰撞的可能性,我会退回到小写字母。再次感谢:)
      【解决方案5】:

      您可以使用 bash 的进程替换将数组作为附加文件传递给 awk。

      partitions=( /usr /home /var /tmp )
      awk '
          FNR==NR { partitions[$0]=""; next } 
          $1 !~ /^#/ && ($2 in partitions) { print $1 }
      ' <(printf '%s\n' "${partitions[@]}") /etc/fstab
      

      NR保存的是当前读取的记录数(行数),FNR保存的是当前文件读取的记录数,所以FNR==NR只有在读取第一个文件时才为真,也就是这里的进程替换案子。所以你填充了第一个文件的分区数组。

      然后,对于第二个文件,您只需检查第二个字段是否在数组中...

      不过,在这种情况下,我只使用 bash(版本 >= 4.0),因为 /etc/fstab 通常相当小。

      declare -A 'partitions=([/usr]= [/home]= [/var]= [/tmp]=)'
      while read -r spec file vfstype mntops freq passno; do
          [[ $spec != \#* && ${partitions[$file]+set} ]] && echo "$spec"
      done < /etc/fstab
      

      或者根据实际目标,您可以解析df,它会告诉您该目录所在的文件系统。

      dirs=( /usr /home /var /tmp )
      for dir in "${dirs[@]}"; do
          { read -r; read -r part _; } < <(df -P "$dir")
          echo "$part"
      done
      

      【讨论】:

      • df 实际上比我试图做的事情要飞跃。我现在使用 df 来获取文件系统。感谢您的详细解释和额外帮助。我从来没有想过它。
      【解决方案6】:

      您也可以将整个数组传递到awk-v,这假定目录名称不包含空格:

      PARTITIONS=(/usr /home /var /tmp)
      awk -v partition="${PARTITIONS[*]}" \
        '$2 != "" && partition ~ $2"\\>" { print $1 }' /etc/fstab
      

      这避免了for 循环的需要。

      说明

      • `partition="${PARTITIONS[*]}" 作为空格分隔的字符串传入整个数组。
      • $2 != "" 表示没有空行匹配。
      • partition ~ $2"\\&gt;"$2 匹配到传入的字符串,\\&gt; 要求匹配位于单词的末尾。

      【讨论】:

        猜你喜欢
        • 2014-10-16
        • 1970-01-01
        • 2017-09-11
        • 1970-01-01
        • 1970-01-01
        • 2014-01-10
        • 2018-11-10
        • 2011-01-14
        • 1970-01-01
        相关资源
        最近更新 更多