【问题标题】:awk complains about non-terminated string in command from concatenated stringsawk 抱怨来自连接字符串的命令中的非终止字符串
【发布时间】:2019-10-17 05:00:40
【问题描述】:

所以背景是我在 awk 中使用 date 命令。此命令在 Linux/GNU 上与 OSX 有不同的标志。我在$date 变量中存储带有标志的正确命令来解决这个问题。以下 awk 命令(依赖于字符串连接)失败:

awk '{
    cmd = "'$date'" substr( $1, 1, length($1) - 3 ) " +\"%Y-%m-%d %H:%M\""
    if ( (cmd | getline dd) > 0 ) {
        $1 = dd
    }
    close(cmd)
    print
}'

有错误:

awk: non-terminated string date... at source line 2
 context is
         >>>  <<<
awk: giving up
 source line number 3

用echo替换awk时,命令输出正确:

{
    cmd = "date -r " substr( $1, 1, length($1) - 3 ) " +\"%Y-%m-%d %H:%M\""
    if ( (cmd | getline dd) > 0 ) {
        $1 = dd
    }
    close(cmd)
    print
}

当上述脚本直接放入 awk 时,它也能正确解析日期(它将标准输入每一行的第一个参数作为时间戳,去除微秒并将日期转换为人类可读的格式)。

$date 变量填充如下:

date="date -d @"
date -d @1550000000 &>/dev/null
if [ $? -eq 1 ]; then
    date="date -r "
fi

【问题讨论】:

  • 你应该使用-v name=value语法传递日期变量,即awk -v dt="$date" '...' file
  • 之后我可以在 awk 脚本中将其称为 $dt 吗?
  • 那行得通,如果将评论移到那里,我可以接受答案。我仍然很好奇上面的字符串连接有什么问题(即使它不是最好的解决方案,我也想了解这个 bash 怪癖)。
  • @AlexanderTsepkov,问题看起来像 awk 撇号在第一个双引号后立即关闭。右大括号之后的最后一个字符似乎是预期的。
  • @AlexanderTsepkov 您遇到的具体问题与您填充 shell 变量 date 的方式有关。设置/使用 awk 变量而不是让 shell 变量在脚本中扩展的一个原因是,当您的 shell 变量包含意外内容时,避免收到类似这样的神秘错误消息。我们看不到你是如何填充它的,所以我们无法帮助解释这个问题。另外:不要创建与命令名称同名的变量 - 这是混淆代码和创建隐蔽错误的可靠方法!

标签: bash awk


【解决方案1】:

您应该始终使用 -v name=value 语法将 shell 变量传递给 awk。

所以在你的情况下:

dt="date -r"

awk -v dt="$dt" '{
   cmd = dt substr( $1, 1, length($1) - 3 ) " +\"%Y-%m-%d %H:%M\""
   if ( (cmd | getline dd) > 0 ) {
       $1 = dd
   }
   close(cmd)
   print
}'

More on: How do I use shell variables in awk scripts?

还要注意 Ed 下面的有用评论,即 awk 索引从 1 开始,而不是其他语言中的 0,例如 C/C++

【讨论】:

  • 虽然这并不能解释为什么 OP 从他发布的脚本中得到了他声称的错误消息。 substr() 的第二个参数是从 1 开始的整数 - 其他任何值(例如 0)都是无效值,awk 将其视为 1。
  • 谢谢,不知道从 1 开始的索引,也解决了这个问题。
  • @AlexanderTsepkov 是的,awk 中的所有内容都从 1 开始 - 字段编号、字符串字符位置和数组索引。
【解决方案2】:

其他有助于我解开谜团的答案是围绕调整 awk 的调用方法并回避以这种方式脱壳到 awk 的 shell 脚本问题。

我想我解决了你的 shell 脚本语法问题。设置:

args.sh:

#!/bin/bash

# copypasta code that shoves $1, $2... into 0-indexed bash array and prints it out.
# store arguments in a special array
args=("$@")
# get number of elements
ELEMENTS=${#args[@]}

# echo each element in array
# for loop
for (( i=0;i<$ELEMENTS;i++)); do
    echo ARGS[${i}]: ${args[${i}]}
done

test.sh:

date="date -r "
./args.sh '{
    cmd = "'$date'" substr( $1, 1, length($1) - 3 ) " +\"%Y-%m-%d %H:%M\""
    if ( (cmd | getline dd) > 0 ) {
        $1 = dd
    }
    close(cmd)
    print
}'

执行:

❯ ./args.sh one two three                                                                                                                                                                                                                                                                  
ARGS[0]: one
ARGS[1]: two
ARGS[2]: three

❯ bash test.sh          
ARGS[0]: { cmd = "date
ARGS[1]: -r
ARGS[2]: " substr( $1, 1, length($1) - 3 ) " +\"%Y-%m-%d %H:%M\"" if ( (cmd | getline dd) > 0 ) { $1 = dd } close(cmd) print }

解释:天真扩展的非双引号 shell 变量中的空格导致使用 echo 的测试无法揭示 awk 接收 3 个参数而不是预期的 1 个参数的实际根本问题。第一个 arg 是格式错误的不完整 awk 程序。

这是我的解决方法:我添加了双引号。 shell 命令现在看起来很粗糙,涉及大量引用。

❯ cat test.sh     
date="date -r "
./args.sh '{
    cmd = "'"$date"'" substr( $1, 1, length($1) - 3 ) " +\"%Y-%m-%d %H:%M\""
    if ( (cmd | getline dd) > 0 ) {
        $1 = dd
    }
    close(cmd)
    print
}'
❯ bash test.sh   
ARGS[0]: { cmd = "date -r " substr( $1, 1, length($1) - 3 ) " +\"%Y-%m-%d %H:%M\"" if ( (cmd | getline dd) > 0 ) { $1 = dd } close(cmd) print }

我不会评论 awk 的用法,因为我不知道如何使用 awk。

这种类型的代码会相当脆弱,但至少我们还没有大的反斜杠堆栈。最近有人写过quine吗?

【讨论】:

  • 谢谢,真不敢相信我错过了。事后看来很明显。
  • 我很满意该问题的解决方案在物理上非常接近错误消息。在某种方式。此外,Awk 甚至报告说放弃解析你给它的内容是另一个明显的迹象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-29
  • 2013-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多