我有一个具有以下格式的文件:
INTEGER INTEGER TEXT
文本为 unicode,可以包含空格。
我正在尝试使用 awk 来使用 printf 以特定格式打印文件中的第一个 INTEGER 和 TEXT 。
问题:由于某些行中的 TEXT 有空格,$3 没有完整的 TEXT,因此该行在更多字段中被破坏。
例子:
12 42956 Cinema - 3D/Multiplex
7 12560 Status Update
5 184 Movie
我的方法如下:
awk '{ c=$3; for(i=4; i< NF;++i){c=c" "$i}; printf "<tag>%d</tag>\n<tag>%s</tag>\n", $1,c}';
但我认为可能有更好的方法
答案1
awk
如果数据来自明确指定的记录,则非常有用。这个数据没有。但是,数据的格式为“ integer stuff the_rest
”,其中“ integer
”和“ stuff
”中都没有空格。这恰好是read
实用程序喜欢阅读的内容。它将读取以空格分隔的单词,与您赋予它的变量读取的数量一样多,然后它将将该行的“其余”放入最后一个变量中。
bash-4.4$ while read -r integer stuff the_rest; do printf '%d\t"%s"\n' "$integer" "$the_rest"; done <data
12 "Cinema - 3D/Multiplex"
7 "Status Update"
5 "Movie"
它会自动去除所有尾随空格。
答案2
基于模式提取字段perl
通常优于awk
:
perl -lne '
if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
print "<tag>$1</tag><tag>$2</tag>"
}'
您的输入给出:
<tag>12</tag><tag>Cinema - 3D/Multiplex</tag>
<tag>7</tag><tag>Status Update</tag>
<tag>5</tag><tag>Movie</tag>
这意味着您可以执行更高级的操作,例如根据需要进行正确的 HTML 编码,例如:
perl -Mopen=locale -MHTML::Entities -lne '
if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
print map {"<tag>" . encode_entities($_) . "</tag>"} $1, $2
}'
或者 XML 编码:
perl -Mopen=locale -MXML::LibXML -lne '
if (/^\s*(\d+)\s*\S+\s*(.*?)\s*$/) {
print map {
my $e = XML::LibXML::Element->new("tag");
$e->appendText($_);
$e->toString} $1, $2
}'
答案3
将 $2 (您无论如何都不会使用)替换为未使用的字符(字符串中不存在的字符)。之后,只需执行以下操作:
awk '{$2="+";print}' input-file.txt | awk -F "+" '{printf "<tag>%d</tag>\n<tag>%s</tag>\n",$1,$2}'
上面我使用加号“+”作为分隔符。
这不是最优雅的解决方案,但很简单。
答案4
如果这不是一个大文件,并且由于文本始终位于末尾,那么作为替代方案,您可以考虑使用经典的 bash 方法,例如:
while IFS=' ' read -r int1 int2 text;do
#do your stuff
done <file
与 while - read 的情况一样,read 命令中的最后一个 var $text 会将所有剩余字段作为一个字段获取。
测试:
$ IFS=' ' read -r int1 int2 text <<<"10 5 some text here"
$ echo "$text"
some text here
Bash while read 在大数据文件中执行速度可能相当慢,但您可以尝试一下您的情况。