我有以下文件(somefile.txt):
/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/7/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1
我希望得到以下结果(后面的下一个数字F
):
2
5
9
7
8
3
6
8
3
6
鉴于每行的列数是可变的,有没有办法我可以执行如下操作?:
awk -F'/' '/F/ {print <column_of_match> + 1 }' somefile.txt
答案1
使用 Perl,因为数组切片很方便,所以能够将数组中的每对元素视为哈希的键和值:
$ perl -F/ -lane '%f = @F[1..$#F]; print $f{F}' input.txt
2
5
9
7
8
3
6
8
3
6
Perl-F
和-a
(autosplit) 的工作方式与 awk 类似 - 但它不是将行自动拆分为 $1、$2、$3 等,而是将每一行自动拆分为一个名为 的数组@F
。
该脚本将数组切片@F
(除了第零个元素之外的所有元素)转换为名为 的散列(关联数组) ,并使用键“F”%f
打印 的元素。%f
为了强调它的作用/工作原理(以及为什么我们需要排除 @F 的空字符串第零个元素),以下是使用时的内容@F
和%f
外观数据::转储模块dump
功能:
$ perl -F/ -MData::Dump=dump -lane '
%f = @F[1..$#F];
print join("\n", $_, dump(@F), dump(\%f), $f{F}), "\n"' input.txt
/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
("", "A", 1, "B", 1, "C", 1, "D", 1, "E", 1, "F", 2, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ A => 1, B => 1, C => 1, D => 1, E => 1, F => 2, G => 1, H => 1, I => 1, J => 1, K => 1 }
2
/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
("", "B", 1, "C", 1, "D", 1, "E", 1, "F", 5, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ B => 1, C => 1, D => 1, E => 1, F => 5, G => 1, H => 1, I => 1, J => 1, K => 1 }
5
/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
("", "C", 1, "D", 1, "E", 1, "F", 9, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ C => 1, D => 1, E => 1, F => 9, G => 1, H => 1, I => 1, J => 1, K => 1 }
9
...and so on...
F
注意:如果输入中没有,这将打印一个空行。如果这不是您想要的,请执行以下操作:
perl -F/ -lane '%f = @F[1..$#F];
if (defined $f{F}) {
print $f{F}
} else {
print STDERR "Error on input line $.: F has absconded"
}' input.txt
答案2
这是使用的答案sed
:
$ sed -n 's|.*F/\([0-9]\).*|\1|p' <<EOF
/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/7/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1
EOF
2
5
9
7
8
3
6
8
3
6
的解释-n 's|.*F/\([0-9]\).*|\1|p'
:
-n
意味着除非明确告知,否则不要打印任何内容p
表达式中的尾部表示:“如果此表达式匹配,则打印此行”。这意味着没有 an 的行将F/[0-9]
不会被打印。s|foo|bar|
表达式中的意思是: 替换foo
为bar
。您通常将其视为,但由于表达式中s/foo/bar/
有 a ,所以我过去常常避免转义它。/
|
- 比赛部分(
foo
):.*F/[0-9].*
意思是:所有带有F/
然后一个数字的行。.*F/\([0-9]\).*
F/
意思是:匹配包含一个数字的整行,但记住该数字
- 替换部分(
bar
):\1
指的是我们记住的那个数字。
简而言之:
- 找到任何匹配 的行
*F/[0-9]*
,并将其仅替换为数字。
如果可以使用多位正整数,则可以轻松调整表达式:
sed -n 's|.*/F/\([0-9]\+\)/.*|\1|p'
答案3
只需使用与分隔符 和 匹配的模式F
,将该子字符串拆分为一个数组,然后打印该子字段。
测试代码:
$ awk 'match ($0, "/F/[^/]/") {
split (substr ($0, RSTART, RLENGTH), V, "/");
print V[3];
}' Match.txt
无需迭代字段或使用两个进程。
您也可以通过调整字符串索引来删除不需要split
, 的部分,但这会使其不那么通用,并且更有可能出现一次性错误。
awk 'match ($0, "/F/[^/]/") {
print substr ($0, RSTART+3, RLENGTH-4);
}' Match.txt
答案4
这是解决您的问题的一个可能的解决方案,它涉及使用 awk 两次,一次用于在正确的位置分割,下一次抓取数字并打印它。
这是脚本:
awk -F "/F/" '{print $2}' prova.txt | awk -F "/" '{print $1}'
在第一部分中,我们将输入字符串拆分为/F/
,以便第二部分的第一个字母就是我们要查找的数字,而在脚本的第二部分中,我们只是隔离该数字。
当我们每行最多有一个时,这是有效的F
(甚至在没有 F 时也有效,因为它只会打印空行。