Regex 如何使用正则表达式解析/sed多个字符串

Regex 如何使用正则表达式解析/sed多个字符串,regex,parsing,sed,string-formatting,Regex,Parsing,Sed,String Formatting,我试图从ovpn文件中提取信息,以便更新我的服务器列表。我找到了一种使用sed提取信息的方法,所有这些都很有效,但是当我试图提取数据以生成目录结构时,我被卡住了 我拥有的是文件夹中的文件,例如: ch101.tcp443.ovpn ch101.udp1194.ovpn ch102.nordvpn.com.tcp443.ovpn ch102.nordvpn.com.udp1194.ovpn ch102.tcp443.ovpn ch102.udp1194.ovpn 现在,我想提取信息来建立目录结构

我试图从ovpn文件中提取信息,以便更新我的服务器列表。我找到了一种使用sed提取信息的方法,所有这些都很有效,但是当我试图提取数据以生成目录结构时,我被卡住了

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

ch101.tcp443.ovpn
ch101.udp1194.ovpn
ch102.nordvpn.com.tcp443.ovpn
ch102.nordvpn.com.udp1194.ovpn
ch102.tcp443.ovpn
ch102.udp1194.ovpn
现在,我想提取信息来建立目录结构,以便提取我需要的所有信息

它适用于我拥有的所有文件,并从文件名获取数据。因此,从“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]

另外,您正试图用自身替换匹配的序列,因此输出不会有任何变化。你需要有
*
来捕捉周围的东西

对于您的第一个团队来说,类似这样的方式是可行的:

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不支持很多字符类转义序列,比如
\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 -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。如果没有,请编辑您的问题,以澄清您的需求,并为各种给定的示例输入值提供准确的预期输出。

这就是您想要做的吗

$ 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。如果没有,则编辑您的问题以澄清您的要求,并为各种给定的示例输入值提供准确的预期输出。

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',没有运气。我想我成功了wrong@miken32这可能是一个很好的起点,问题是当您发现此ch-onion1.nordvpn.com.tcp443.ovpn打印$2返回nordvpn而不是您要显示的协议时,考虑到您发布的示例输入,您希望最终使用的目录结构与创建后显示它们的方式相同。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',没有运气。我想我成功了wrong@miken32这可能是一个很好的起点,问题是当您发现此ch-onion1.nordvpn.com.tcp443.ovpn打印$2返回nordvpn而不是您要显示的协议时,考虑到您发布的示例输入,如果有任何其他方式与BusyBox sh兼容,则不需要使用sed。因此,如果我理解正确,我只需要包含字符串的所有其余部分。这样我就可以让它在一条线上工作?还是你认为这太复杂了?谢谢@miken32,我认为这是最干净的方式!我不知道gsub命令,但是你的一行想法很好!我做了一些尝试,看看如果有任何其他方式与BusyBox sh兼容,是否不需要对每个timesed都解析超过9k的文件。因此,如果我理解正确,我只需要包含字符串的所有其余部分。这样我就可以让它在一条线上工作?还是你认为这太复杂了?谢谢@miken32,我认为这是最干净的方式!我不知道gsub命令,但是你的一行想法很好!我做了一些尝试,看看是否一网打尽,我每次都有超过9k的文件要解析谢谢@ed morton,我认为miken32解决方案相当不错。为什么不清楚输出?我在请求中插入了我的最终目的,在这里我展示了我想要提取每一段数据以形成目录结构。如果您认为不清楚,我可以修改itI我说如果我的脚本输出的不是您想要的,那么请澄清输出,但由于您提出了问题-您在问题中提供了7个输入值,但只有1个输出值,您说您有一个regexp可以做您想要的,但您的在线演示显示
ch101.udp1194.ovpn
将成为
ch101udp
,而您问题中的文本显示它将成为
ch0101udp
(额外添加的
0
)。因此,将两种不同的输出组合在一起,而不显示问题中的输出列表