这个问题的目的是为常见问题提供一个强大,灵活的解决方案。
处理文本时经常出现的情况是需要将输入分成字段,操作字段然后重新组合进行打印。例如,给出了这个输入:
$ cat file
A 7 C 3
如果我们想确保每个数字都是.2f格式,并且我们想保留字段之前/之后/之间的间距,那么我们可以编写(使用GNU awk将第4个arg分割为()):
$ cat tst.awk
{
split($0,flds,FS,seps)
for (i in flds) {
if (flds[i] ~ /[0-9]/) {
flds[i] = sprintf("%.2f",flds[i])
}
}
#### print the flds[] array, interleaving seps[] values:
printf "%s", seps[0]
for (i=1; i in flds; i++) {
printf "%s%s", flds[i], seps[i]
}
print ""
#####
}
$ awk -f tst.awk file
A 7.00 C 3.00
我们将数组展平为字符串以进行打印的最后一个循环对于许多awk脚本来说是很常见的。有时分隔符存储在如上所述的不同数组中,有时它们是特定字符,有时它们不是必需的。此外,我们希望flds []打印的顺序可以基于如上所述的数字上升索引,或者它可以是降序(例如模仿UNIX工具" rev")或者它可以基于flds []值而不是它们的指数。
那么 - 是否有一个awk实用程序函数,它使用指定顺序的提供的分隔符将数组转换为字符串?
答案 0 :(得分:1)
我们一直在与GNU awk开发人员进行讨论,以提供问题中描述的功能,但是直到if / when到达时,以下用户空间特定于gawk(用于sorted_in
)函数将执行该操作工作。注意不要将元素添加到调用之前不存在的flds,seps或PROCINFO数组中。它可以使用如下:
$ cat tst.awk
{
split($0,flds,FS,seps)
for (i in flds) {
if (flds[i] ~ /[0-9]/) {
flds[i] = sprintf("%.2f",flds[i])
}
}
print "arr2str() usage examples:"
print "1)", arr2str(flds,OFS)
print "2)", arr2str(flds,seps)
print "3)", arr2str(flds,seps,"@ind_num_desc")
print "4)", arr2str(flds,seps,"@val_str_asc")
print "5)", arr2str(flds,",")
}
$ awk -f arr2str.awk -f tst.awk file
arr2str() usage examples:
1) A 7.00 C 3.00
2) A 7.00 C 3.00
3) 3.00 C 7.00 A
4) 3.007.00 A C
5) A,7.00,C,3.00
$ cat arr2str.awk
# Usage:
# arr2str(flds[,seps,[sortOrder]])
#
# flds:
# This function converts the mandatory "flds" array argument into a string.
#
# seps:
# If "seps" is not present then the "flds" values will simply be concatenated
# in the returned string.
#
# If "seps" is present and is a string then that "seps" value will be inserted
# between each "flds" value in the returned string.
#
# If "seps" is present and is an array then each "seps" value with the same index
# as a "flds" index will be inserted in the returned string before or after
# (sort order dependent) the corresponding "flds" value with that same index.
# - All "seps" values that do not have an index in "flds" will be inserted in
# the returned string before or after all of the "flds" and other "seps" values.
# This ensures that a "seps" array that, for example, starts at zero as a result
# of a previous split(str,flds,re,seps) will have its zeroth entry included.
#
# sortOrder:
# If "sortOrder" is present then it will be used as the order the "flds" values
# are visited in, otherwise it uses PROCINFO["sorted_in"] if set, otherwise it
# uses ascending numeric indices.
# - If the sort order is descending (ends in "desc") and "seps" is an array then
# the "seps" values are inserted before each "flds" value, otherwise after them.
#
# Example:
# $ cat tst.awk
# BEGIN {
# orig = ",a+b:c-d="
# split(orig,flds,/[^[:alpha:]]/,seps)
#
# printf "orig: <%s>\n", orig
# printf "asc: <%s>\n", arr2str(flds,seps)
# printf "desc: <%s>\n", arr2str(flds,seps,"@ind_num_desc")
# }
# $ awk -f arr2str.awk -f tst.awk
# orig: <,a+b:c-d=>
# asc: <,a+b:c-d=>
# desc: <=d-c:b+a,>
function arr2str(flds, seps, sortOrder, sortedInPresent, sortedInValue, currIdx, prevIdx, idxCnt, outStr) {
if ( "sorted_in" in PROCINFO ) {
sortedInPresent = 1
sortedInValue = PROCINFO["sorted_in"]
}
if ( sortOrder == "" ) {
sortOrder = (sortedInPresent ? sortedInValue : "@ind_num_asc")
}
PROCINFO["sorted_in"] = sortOrder
if ( isarray(seps) ) {
# An array of separators.
if ( sortOrder ~ /desc$/ ) {
for (currIdx in flds) {
outStr = outStr (currIdx in seps ? seps[currIdx] : "") flds[currIdx]
}
}
for (currIdx in seps) {
if ( !(currIdx in flds) ) {
outStr = outStr seps[currIdx]
}
}
if ( sortOrder !~ /desc$/ ) {
for (currIdx in flds) {
outStr = outStr flds[currIdx] (currIdx in seps ? seps[currIdx] : "")
}
}
}
else {
# Fixed scalar separator.
# We would use this if we could distinguish an unset variable arg from a missing arg:
# seps = (magic_argument_present_test == true ? seps : OFS)
# but we cant so just use whatever value was passed in.
for (currIdx in flds) {
outStr = outStr (idxCnt++ ? seps : "") flds[currIdx]
}
}
if ( sortedInPresent ) {
PROCINFO["sorted_in"] = sortedInValue
}
else {
delete PROCINFO["sorted_in"]
}
return outStr
}