1

我正在使用以下awk命令:

my_command | awk -F "[[:space:]]{2,}+" 'NR>1 {print $2}' | egrep "^[[:alnum:]]"

它成功地返回了我的数据,如下所示:

fileName1
file Name 1
file Nameone
f i l e Name 1

所以你可以看到一些文件名有空格。这很好,因为我只是想回显文件名(没什么特别的)。问题是在循环中调用该特定行。我正在尝试这样做:

i=1
for num in $rows
do
  fileName=$(my_command | awk -F "[[:space:]]{2,}+" 'NR==$i {print $2}' | egrep "^[[:alnum:]])"
  echo "$num $fileName"
  $((i++))
done

但我的输出总是null

我也尝试过使用awk -v record=$i然后打印$record,但我得到了以下结果。

f i l e Name 1

编辑

很抱歉造成混淆: rows是一个列出这样的 id 的变量,11 12 13 并且每个这些 id 都与一个文件名相关联。我没有做任何解析的命令如下所示:

     id      File Info      OS
     11      File Name1     OS1
     12      Fi leNa me2    OS2
     13      FileName 3     OS3

我只能使用该id字段来运行我需要的命令,但我想使用该File Info字段来通知用户File正在执行该命令的实际情况。

4

4 回答 4

2

我认为您$i没有按预期扩展。你应该这样引用你的论点:

  fileName=$(my_command | awk -F "[[:space:]]{2,}+" "NR==$i {print \$2}" | egrep "^[[:alnum:]]")

而你忘记了另一个)

编辑

作为对您要求的更新,您可以将行传递给单个 awk 命令,而不是循环内的重复命令:

#!/bin/bash

ROWS=(11 12)

function my_command {
    # This function just emulates my_command and should be removed later.
    echo "     id      File Info      OS
     11      File Name1     OS1
     12      Fi leNa me2    OS2
     13      FileName 3     OS3"
}

awk -- '
    BEGIN {
        input = ARGV[1]
        while (getline line < input) {
            sub(/^ +/, "", line)
            split(line, a, /   +/)
            for (i = 2; i < ARGC; ++i) {
                if (a[1] == ARGV[i]) {
                    printf "%s %s\n", a[1], a[2]
                    break
                }
            }
        }
        exit
    }
' <(my_command) "${ROWS[@]}"

该 awk 命令可以压缩为一行:

awk -- 'BEGIN { input = ARGV[1]; while (getline line < input) { sub(/^ +/, "", line); split(line, a, /   +/); for (i = 2; i < ARGC; ++i) { if (a[1] == ARGV[i]) {; printf "%s %s\n", a[1], a[2]; break; }; }; }; exit; }' <(my_command) "${ROWS[@]}"

或者更好的是只使用 Bash 作为一个整体:

#!/bin/bash

ROWS=(11 12)

while IFS=$' ' read -r LINE; do
    IFS='|' read -ra FIELDS <<< "${LINE//  +( )/|}"
    for R in "${ROWS[@]}"; do
        if [[ ${FIELDS[0]} == "$R" ]]; then
            echo "${R} ${FIELDS[1]}"
            break
        fi
    done
done < <(my_command)

它应该给出如下输出:

11 File Name1
12 Fi leNa me2
于 2013-08-26T18:57:47.077 回答
2

Shell 变量不会在单引号字符串中展开。使用该-v选项将 awk 变量设置为 shell 变量:

fileName=$(my_command | awk -v i=$i -F "[[:space:]]{2,}+" 'NR==i {print $2}' | egrep "^[[:alnum:]])"

这种方法避免了按照 konsolebox 的回答要求转义脚本$中的所有字符。awk

于 2013-08-26T18:58:58.950 回答
1

每次通过循环重新运行my_command(and ) 只是为了从其输出中提取一行是非常低效的。awk尤其是当您所做的只是按顺序打印出每行的一部分时。(我假设这my_command确实是完全相同的命令,并且每次通过循环都会产生相同的输出。)

如果是这样的话,这个单线应该可以解决问题:

paste -d' ' <(printf '%s\n' $rows) <(my_command | 
  awk -F '[[:space:]]{2,}+' '($2 ~ /^[::alnum::]/) {print $2}')
于 2013-08-26T19:16:12.113 回答
1

正如您已经听说的,您需要从您的 shell 变量中填充一个 awk 变量,以便能够在 awk 脚本中使用所需的值,因此:

awk -F "[[:space:]]{2,}+" 'NR==$i {print $2}' | egrep "^[[:alnum:]]"

应该是这样的:

awk -v i="$i" -F "[[:space:]]{2,}+" 'NR==i {print $2}' | egrep "^[[:alnum:]]"

另外,尽管如此,您不需要 awk 和 grep,因为 awk 可以做任何 grep van 做的事情,因此您可以更改脚本的这一部分:

awk -v i="$i" -F "[[:space:]]{2,}+" 'NR==i {print $2}' | egrep "^[[:alnum:]]"

对此:

awk -v i="$i" -F "[[:space:]]{2,}+" '(NR==i) && ($2~/^[[:alnum:]]/){print $2}'

并且您不需要+数字范围之后的 a ,因此您可以更改{2,}+{2,}

awk -v i="$i" -F "[[:space:]]{2,}" '(NR==i) && ($2~/^[[:alnum:]]/){print $2}'

不过,最重要的是,您不必为每次调用 awk 调用一次,my_command而是为所有这些调用一次,即代替 this(假设 this 可以满足您的要求):

i=1
for num in rows
do
  fileName=$(my_command | awk -v i="$i" -F "[[:space:]]{2,}" '(NR==i) && ($2~/^[[:alnum:]]/){print $2}')
  echo "$num $fileName"
  $((i++))
done

你可以做更多这样的事情:

for num in rows
do
  my_command
done |
awk -F '[[:space:]]{2,}' '$2~/^[[:alnum:]]/{print NR, $2}'

我说“类似”是因为您没有告诉我们“my_command”、“rows”或“num”是什么,所以我不能准确,但希望您能看到这种模式。如果您向我们提供更多信息,我们可以提供更好的答案。

于 2013-08-26T19:30:11.163 回答