【问题标题】:How to parse/sed multiple string with regex expression如何使用正则表达式解析/sed多个字符串
【发布时间】:2019-09-18 12:45:42
【问题描述】:

我正在尝试从一波 ovpn 文件中提取信息以更新我的服务器列表。我找到了一种使用 sed 提取信息的方法并且一切正常,但是当我尝试提取数据以制作目录结构时,我被卡住了。

我拥有的是文件夹内的文件,例如:

ch101.tcp443.ovpn
ch101.udp1194.ovpn
ch102.nordvpn.com.tcp443.ovpn
ch102.nordvpn.com.udp1194.ovpn
ch102.tcp443.ovpn
ch102.udp1194.ovpn

现在我想提取信息来制作目录结构,所以I made a regex来提取我需要的所有信息

它适用于我拥有的所有文件,并从文件名中获取数据。因此,它会从“ch101.udp1194.ovpn”中提取“ch101”和“udp”,并将其分为第 1 组和第 2 组。

但是当我尝试使它与 sed 一起工作时,我失败了。我试图将其分解为多个步骤,但即使只有第一组在寻找“ch101”,它也不起作用:

echo 'ch101.udp1194.ovpn' | sed -rn 's/^([a-z\-]+\d{1,4})/\1/p'

我错过了什么?我不是 sed 专家,但我发现类似的表达方式有效,但这个无效。

我的最终目的是创建目录并在其中存储我需要的所有信息,所以:

for i in /opt/ovpn/*.ovpn ; do 
    [ -f "$i" ] || continue
    FIRST_ARG=$(echo $i | sed ...) # extract ch101
    SECOND_ARG=$(echo $i | sed ...) # extract udp
    FIRST_ARG_TEXT=$(echo $FIRST_ARG | sed ...) # extract text from FIRST_ARG
    FIRST_ARG_NUM=$(echo $FIRST_ARG | sed ...) # extract num from FIRST_ARG
    FIRST_ARG_NUM_4FORMAT=$(printf '%04i\n' $FIRST_ARG_NUM) # 4 digits for FIRST_ARG_NUM

    mkdir /opt/somedir/$FIRST_ARG_TEXT$FIRST_ARG_NUM_4FORMAT$SECOND_ARG
    cp ........
done

所以从 ch101.udp1194.ovpn 我将以一个名为的目录结束

ch0101udp

也许不是最好和最干净的方法,但对我来说似乎很简单,并且是我的知识所能达到的最大值

任何想法或问题对我都有好处

附言。我在busybox 1.30下,所以这一定是sh而不是bash

【问题讨论】:

  • Sed 不支持\d。请改用[0-9][[:digit:]]
  • 我试过 echo 'it66.tcp443.ovpn' | sed -rn 's/^([a-z\-]+[0-9]{1,4})/\1/p' 没有运气,并且有 echo 'it66.tcp443.ovpn' | sed -rn 's/^([a-z\-]+[[:digit:]]{1,4})/\1/p' 没有运气。我想我做错了
  • @miken32 可以作为一个很好的起点,问题是当你发现这个 ch-onion1.nordvpn.com.tcp443.ovpn print $2 return nordvpn 而不是协议时
  • edit 您要显示的问题,鉴于您发布的示例输入,您希望以与 tree 相同的方式最终得到的目录结构将在创建后显示它们。

标签: regex parsing sed string-formatting


【解决方案1】:

几个问题:sed 不支持很多像\d 这样的字符类转义序列,因此您需要将它们指定为[0-9]

同样,您正在尝试将匹配的序列替换为自身,因此输出不会发生变化。你需要有.* 来捕捉它周围的东西。

这样的方法适用于您的第一组:

sed -En 's/^([a-z\-]+[0-9]{1,4}).*/\1/p'

但实际上你应该做的是使用适当的程序来做到这一点。不确定它是否在 Busybox 上可用,但 awk 可以满足您的所有需求:

echo 'ch101.udp1194.ovpn' | awk -F. '{a=$1; b=$(NF-1); gsub(/[0-9]/, "", a); gsub(/[0-9]/, "", b); gsub(/^[a-z-]+/, "", $1); printf("%s%04d%s", a, $1, b)}'

样本数据的输出:

ch0101tcp
ch0101udp
ch0102tcp
ch0102udp
ch0102tcp
ch0102udp

解释:

awk -F. '{
    a=$1;                          # assign the first field to a
    b=$(NF-1);                     # assign the second last field to b
    gsub(/[0-9]/, "", a);          # remove numbers from a
    gsub(/[0-9]/, "", b);          # remove numbers from b
    gsub(/^[a-z-]+/, "", $1);      # remove letters from the first field
    printf("%s%04d%s", a, $1, b)   # output in desired format
}'

【讨论】:

  • sed 如果有任何其他与 BusyBox sh 兼容的方式,则不需要。因此,如果我理解正确,我只需要包含所有其余的字符串。通过这种方式,我可以使它在一行中工作吗?还是您认为这变得太复杂了?
  • 谢谢@miken32,我认为这是最干净的方式!我不知道 gsub 命令,但是您的单行想法非常好!我做了一些尝试看看是否全部捕获,我每次都要解析超过 9k 个文件
【解决方案2】:

这是你想要做的吗?

$ sed -n 's/\([^.]*\).*\.\([^0-9]*\)[^.]*\.[^.]*$/\1\2/p' file
ch101tcp
ch101udp
ch102tcp
ch102udp
ch102tcp
ch102udp

$ echo 'ch-onion1.nordvpn.com.tcp443.ovpn' | sed -n 's/\([^.]*\).*\.\([^0-9]*\)[^.]*\.[^.]*$/\1\2/p'
ch-onion1tcp

如果是这样,这将适用于任何 UNIX 机器上的任何 shell 中的任何 sed。如果不是,则编辑您的问题以阐明您的要求并为您的各种给定示例输入值提供准确的预期输出。

【讨论】:

  • 感谢@ed-morton,我认为miken32 解决方案相当不错。为什么不清除输出?我在请求中插入了我的最终目的,我在其中表明我想提取每条数据来制作目录结构。如果你觉得不清楚我可以修改它
  • 我说如果我的脚本输出的不是你想要的然后澄清输出但是既然你问了 - 你在你的问题中提供了 7 个输入值但只有 1 个输出值,你说你有一个正则表达式可以满足您的需求,但您的在线演示显示ch101.udp1194.ovpn 将变为ch101udp,而您问题中的文本显示它将变为ch0101udp(添加了额外的0)。因此,将 2 个不同的输出组合在一起,而不是在您的问题中为您的问题中的示例输入显示输出列表,这会使水域变得混乱。
猜你喜欢
  • 2014-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-25
  • 2019-12-10
  • 2010-11-22
  • 2011-10-14
相关资源
最近更新 更多