【问题标题】:Parse a string and extract a word delimited by comma and assign value from inside [] brackets解析字符串并提取以逗号分隔的单词并从 [] 括号内赋值
【发布时间】:2017-05-15 08:38:52
【问题描述】:

我需要帮助来解析字符串并提取以逗号分隔的单词并从 [] 括号内赋值。 输入字符串是这样的:

KEEP_DFB,?(y/n),[y];
DFB_VERSION,?(1.4.2/1.7.6),[1.4.2]:

预期的输出是

KEEP_DFB=y
DFB_VERSION=1.4.2

我可以使用 sed 实现的最接近的是:

echo 'KEEP_DFB,?(y/n),[y]:' | sed 's/\([^,]*,\).*,\([^,]*\):.*/\1=\2/'

但它没有给出预期的结果。

我也试过'cut',但结果和上面一样。 不允许使用 IFS 更改分隔符。 你能帮忙吗?

【问题讨论】:

  • 模式(y/n),[y] 总是成立吗?我的意思是即使在(1.4.2/1.7.6),[1.4.2] 中,[] 中的值也与/() 中的值相同
  • @Sundeep:由于问题是关于捕获方括号之间的值,因此依赖样本数据中圆括号之间材料的相同部分与材料匹配的巧合是不明智的在方括号之间。
  • @JonathanLeffler 是的,我想是的,我没有完全阅读这个问题,已经准备好答案,然后在发布之前注意到我的假设......因此我要求澄清,因为解决方案会更简单......
  • 当您使用它向用户询问值时,您可能会考虑另一种方法:使用参数question options defaultvalue 创建function askvalue 并将其用作KEEP_DFB=$(askvalue "Do you want to keep DFB" "y/n" "y")

标签: shell sed scripting cut


【解决方案1】:

你已经很接近了:

$ printf "%s\n" 'DFB_VERSION,?(1.4.2/1.7.6),[1.4.2]:' 'KEEP_DFB,?(y/n),[y]:' |
> sed 's/\([^,]*\),.*,\[\([^],]*\)][;:].*/\1=\2/'
DFB_VERSION=1.4.2
KEEP_DFB=y
$

第一个逗号移到捕获之外。第二个捕获之前是\[(数据中的文字[),然后是](不需要反斜杠转义,因为]仅在它是字符类的一部分时才特殊,虽然我很想加一个,不管有没有反斜杠都可以正常工作)。

Sundeep 注意到其中一个数据行中有一个分号而不是冒号,但echo 中的示例数据有一个冒号而不是分号(这就是我没有发现问题的原因第一遍;我复制了原型命令)。这可以通过使用[;:] 作为字符类而不是直接的: 来轻松处理。

否定字符类不包括] 和逗号——尽管不清楚为什么需要排除逗号。这意味着您不会认为这是有效的:

VERSION_LIST,?(1.2/1.3/1.4/1.7),[1.4,1.7]:

【讨论】:

  • 非常感谢@Jonathan..这工作正常并且符合需求脚本..这正是我所缺少的..非常感谢您的解释。
  • 对不起,我错过了最后一点...是的,你猜对了,这将被视为无效。
【解决方案2】:

你应该试试这个代码。它应该可以正常工作。

awk -F"," '{print $1,$3}' OFS="=" file_name | sed -e 's/\[\(.*\)\]./\1/'

这将使用 awk 输出文件中包含的行,并将分隔符替换为 =,然后将以 [ 开头并以 ] 或任何其他字符结尾的部分替换为 [] 中的值。

你也可以试试这个更短的:

sed -e 's/,.*\[\(.*\)\]./=\1/' file

两者的输出是:

KEEP_DFB=y
DFB_VERSION=1.4.2

【讨论】:

  • 或使用,[]作为分隔符:awk -F'[,[\]]' '{print $1"="$4}' file
  • @VinnyAdjibi:没有必要使用 1 个 awk 和 1 个 sed,您可以将其封装在 1 个单个 awk 中(除了 Cyrus 建议的)。 awk -F"," '{gsub(/[|]|;|:/,X,$3);print $1,$3}' OFS="=" Input_file
  • @RavinderSingh:感谢您的建议。我不知道gsub。当然这是一种更短更快速的解决方法。
【解决方案3】:

我建议:

sed 's/,.*\[/=/;s/].//' file

输出:

KEEP_DFB=y DFB_VERSION=1.4.2

【讨论】:

  • 谢谢@Cyrus,虽然它适用于给定的一组示例,但我有时必须将单词分开并将其存储为 \1 \2 \3.. Jonathan 刚刚回答我说到点子上了。它有助于我理解上面例子中的缺失点。
【解决方案4】:
awk -F'[][,]' '{print $1"="$4}' file 

KEEP_DFB=y
DFB_VERSION=1.4.2

【讨论】:

    【解决方案5】:

    你没有说你将使用什么 shell,但是对于大多数 shell,以下方法可以工作:

    # Drop the last two characters
    x=${original:0:-2}
    # Store the name part
    name=${x%%,*}
    # Store the value part
    value=${x##*\[}
    

    例如,如果original 包含DFB_VERSION,?(1.4.2/1.7.6),[1.4.2]:,则name 将包含DFB_VERSIONvalue 将包含1.4.2

    顺便说一句,你为什么不想修改IFS?当然你不想永久修改它,但是只修改一个语句,不会影响程序的其余部分。

    【讨论】:

    • 脚本似乎必须在不同的构建服务器上运行,用户可能没有“root/superuser”权限或 IFS 可能被禁用。因此,现有的 IFS 解决方案适用于“root”用户,而对于其他用户却卡住了。
    • 您不需要拥有超级用户权限即可更改 IFS,而且我看不到可以“禁用”对 IFS 的修改的方法。
    【解决方案6】:

    POSIX shell 方法,给定输入文件'foo':

    while IFS=',[]' read a b c d e ; do echo "$a${a:+=}$d" ; done < foo
    

    输出:

    KEEP_DFB=y
    DFB_VERSION=1.4.2
    

    【讨论】:

    • 这里${a:+=}= 有什么优势?是否根本不为空行打印任何内容?如果是这样,稍微解释一下会有所帮助。
    • @BenjaminW.,这是正确的,如果没有${a:+=},只要一行空白,输出就会有无用的= 符号。它比做的要快一点:[ "$a"] &amp;&amp; echo "$a$=$d"
    【解决方案7】:

    @Suresh K:您能否尝试关注一下,如果这对您有帮助,请告诉我。

    awk -F, '{match($0,/\[.*\]/);print $1"="substr($0,RSTART+1,RLENGTH-2)}' Input_file
    

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-26
      • 1970-01-01
      • 2019-08-10
      • 1970-01-01
      • 1970-01-01
      • 2015-08-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多