Trim AWK 中的额外空格

Trim extra spaces in AWK

我有这个 AWK 脚本。

awk -v line="    foo    bar  " 'END
 {
   gsub(/^ +| +$/,"", line);
   gsub(/ {2,}/, " ", line);
   print line
 }' \
somefile.txt

输入文件(somefile.txt)与我的问题无关。 END 模式之后的部分是在 line 变量中 trim 额外的空格 并将其打印出来。像这样:

foo bar

我想看看在 AWK 中是否有更好、更紧凑的方法来做到这一点。使用 gsub 删除几个额外的空格非常麻烦。它很难阅读,维护者也很难理解它的作用(特别是如果以前从未使用过 AWK)。关于如何使其更短或更明确的任何想法?

谢谢!

** 编辑 **

AWK 变量 line 在输入文件的 awk 处理过程中被过滤,我想 trim 在那之后留下额外的空间。

我在 路径上:

$ awk  -v line="    foo    bar  " '
BEGIN {
    [=10=]=line
    for(i=1;i<=NF;i++)
        printf "%s%s",$i,(i==NF?ORS:OFS)
}'

输出:

foo bar

您开始时使用 gsub() 的另一个选项可以完成为:

awk '{gsub(/  +/," "); sub(/^ /,""); sub(/ $/,"")}1' <<< "    foo    bar  "

第一次调用 gsub() 将所有多个 space 合并为一个 space before/between 字段。第二个 sub(/^ /,"") 只是修剪保留在字符串前面的单个 space,最后一个 sub(/ $/,"") 修剪尾随的 space.

这两种方法都适用。根据您的实际数据和您的 FS 值,可能会偏爱其中一个,但在不知道更多的情况下,它们几乎是一种洗礼。

例子Use/Output

$ awk '{gsub(/  +/," "); sub(/^ /,""); sub(/ $/,"")}1' <<< "    foo    bar  "
foo bar

使用您显示的示例,请尝试执行以下 awk 程序。因为你有一个 awk 变量并且你没有读取任何 Input_file 那么我们不需要使用 END 块我们实际上可以在 [=12= 中使用 BEGIN 块本身] 读取变量的程序。

在这个 awk 程序中,我创建了名为 lineawk 变量,并且在这个程序的 BEGIN 部分,我全局替换了开始和结束 spaces 与 NULL THEN 行全局替换所有出现的 spaces(1 或更多)与变量 line 中的 OFS(它本身是一个 space),然后打印它的值。

awk -v line="    foo    bar  " '
BEGIN{
  gsub(/^[[:space:]]+|[[:space:]]+$/,"",line)
  gsub(/[[:space:]]+/,OFS,line)
  print line
}
'

考虑到您的 awk 程序中还有其他 functions/tasks/work 发生并且您想进行修剪END 部分中的变量然后尝试以下

awk -v line="    foo    bar  " '
END{
  gsub(/^[[:space:]]+|[[:space:]]+$/,"",line)
  gsub(/[[:space:]]+/,OFS,line)
  print line
}
'  Input_file

使用split函数收集数组中的所有字段并substr删除最后一个前导space:

$ awk -vline="    foo    bar  " 'END {s = ""; l = split(line, a)
    for(i = 1; i <= l; i++) s = s " " a[i]; print substr(s, 2) "X"}' /dev/null
foo barX

结尾的 X 在这里表示结尾的 space 也被删除了。如果您最终决定使用它,请抑制它。 patsplit 而不是 split 的其他解决方案:

$ awk -vline="    foo    bar  " 'END {s = ""; l = patsplit(line, a, /[^ ]+/)
    for(i = 1; i <= l; i++) s = s " " a[i]; print substr(s, 2) "X"}' /dev/null
foo barX

对于当前示例,另一个选项可能是 recalculate the text 输入记录,方法是首先将行的值设置为输入记录,然后使用 =

awk -v line="    foo    bar  " 'END {[=10=]=line; =; print}' somefile.txt

输出(引号只是为了清楚,没有前导或尾随 spaces)

"foo bar"

Ed Morton 的评论中描述了如何删除 space 的内部工作原理:

设置 [=13=]=line 或对 [=14=] 的任何其他更改将触发重新计算 字段

使用 = 会触发 记录 重新计算,因为它将从现有字段中重建,从而剥离 leading/trailing 白色 space 并用单个空白字符替换所有其他相邻的白色链 space(假设使用默认的 FS 和 OFS)。

如果你的 space 都是空白字符,那么使用 FS 的任何值和 OFS 的任何值的任何 awk 都是由你的问题中的代码处理的,这里是如何按照您的问题中的要求简要而明确地进行:

gsub(/ +/, " ", line)
gsub(/^ | $/, "", line)

例如,假设您有一个 CSV 文件,并希望打印每行中的字段数,然后是用 | 分隔的字段。示例输入文件为:

$ cat file
stuff,nonsense

要处理的 awk 脚本是:

$ awk -v FS=',' -v OFS='|' '
    { print NF, ,  }
' file
2|stuff|nonsense

现在让我们介绍您的 line 变量及其相关处理(我在输出中添加了 <> 以表明 leading/trailing space s 被剥离):

$ awk -v line='    foo    bar  ' -v FS=',' -v OFS='|' '
    { print NF, ,  }
    END {
        gsub(/ +/, " ", line)
        gsub(/^ | $/, "", line)
        print "<" line ">"
    }
' file
2|stuff|nonsense
<foo bar>

如您所见,一切都完全按预期工作,而到目前为止发布的所有其他解决方案都会以各种方式失败。

如果 line 中的 space 不全是空白,则对 line 中任何类型的白色 space 字符使用 POSIX awk (使用 non-POSIX awk 将 [[:space:]] 替换为 [ \t] 以捕获最常见的空白字符和制表符,根据需要添加其他字符):

gsub(/[[:space:]]+/, " ", line)
gsub(/^ | $/, "", line)

您的脚本:

gsub(/^ +| +$/,"", line);
gsub(/ {2,}/, " ", line);

比它必须的要长,因为你以错误的顺序执行 gsub()s,这需要第一个中的 +s 并且不必要地检查 2 个或更多空白({2,}) 在第二个。如果某些 space 是制表符或其他一些白色 space 字符,它也不会工作。