我正在使用以下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
是一个变量,列出了像11 12 13
这样的ID
这些ID中的每一个都与文件名相关联。我没有进行任何解析的命令如下所示:
id File Info OS
11 File Name1 OS1
12 Fi leNa me2 OS2
13 FileName 3 OS3
我只能使用id
字段来运行我需要的命令,但是我想使用File Info
字段来通知用户该命令的实际File
正在被执行。
答案 0 :(得分: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
答案 1 :(得分:2)
Shell变量不会在单引号字符串中展开。使用-v
选项将awk变量设置为shell变量:
fileName=$(my_command | awk -v i=$i -F "[[:space:]]{2,}+" 'NR==i {print $2}' | egrep "^[[:alnum:]])"
此方法避免了必须转义$
脚本中的所有awk
字符,这是konsolebox答案中的要求。
答案 2 :(得分:1)
每次通过循环重新运行my_command
(和awk
)只是为了从输出中提取一行是非常低效的。特别是当您所做的只是按顺序打印出每行的一部分时。 (我假设my_command
确实是完全相同的命令,并且每次循环都会产生相同的输出。)
如果是这种情况,这个单行应该可以解决问题:
paste -d' ' <(printf '%s\n' $rows) <(my_command |
awk -F '[[:space:]]{2,}+' '($2 ~ /^[::alnum::]/) {print $2}')
答案 3 :(得分: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}'
并且您在数字范围后不需要+
,因此您可以将{2,}+
更改为{2,}
:
awk -v i="$i" -F "[[:space:]]{2,}" '(NR==i) && ($2~/^[[:alnum:]]/){print $2}'
最重要的是,不是每次调用my_command
都调用一次awk,而是可以为它们调用一次,而不是这个(假设这样做你想要的):
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”是什么,所以我不能准确但希望你能看到这种模式。如果您向我们提供更多信息,我们可以提供更好的答案。