打印两个记住的模式,并且仅打印它们之间的非字母数字字符

打印两个记住的模式,并且仅打印它们之间的非字母数字字符

我在 Debian 11 上使用 GNU sed 4.7。

我有一个包含多行的文件,所有行的第一个字符串位于行首的大括号中,最后一个字符串位于@行尾的字符中,以及一个包含字母数字、标点符号和上述之间的其他字符的字符串终端字符串。

我想生成完整显示第一个和最后一个字符串的输出,并且仅有的中间字符串中的非字母字符,因此:

./file包含以下内容:

{string-no1}middle@string-no2@
{AAAAAAAAAA}1,a.B£@ZZZZZZZZZZ@
{GGGGGGGGGG}&:3m?J@@@@@@@@@@@@

...我想看到输出:

{string-no1}@string-no2@
{AAAAAAAAAA},.£@ZZZZZZZZZZ@
{GGGGGGGGGG}&:?@@@@@@@@@@@@

我试过:

sed 's/\({[^}]*}\)[^a-zA-Z0-9]*\(@[^@]*@\)/\1\2/' ./file

...但这不起作用,也不起作用:

sed 's/\({[^}]*}\)[[:punct:]]*\(@[^@]*@\)/\1\2/' ./file`

...也不:

sed '/}/,/@/ s/[a-zA-Z0-9]*//' ./file

我尝试过在 grymoire 上寻求帮助,也在 StackExchange 上寻求帮助,这通常可以解决任何问题,但这个确实让我着迷。有人可以帮忙吗?

答案1

这在 中很难做到sed(因为您需要对s///每个输入行的三个不同部分做不同的事情 - 什么都不做,用 修改,然后什么也不做),但在 中很容易做到perl

$ perl -lne '($first,$middle,$last) = (/({[^}]*})([^@]*)(@.*)/);
             $middle =~ s/[[:alnum:]]+//g;
             print $first, $middle, $last' file 
{string-no1}@string-no2@
{AAAAAAAAAA},.£@ZZZZZZZZZZ@
{GGGGGGGGGG}&:?@@@@@@@@@@@@

首先,它使用正则表达式将输入行的第一部分、中间部分和最后部分提取到适当命名的变量中。然后它从 $middle 中删除所有字母数字字符。然后它打印它们。

答案2

您的尝试不起作用,因为中缀字符串(中间位)包含字母数字和非字母数字字符的混合。该中缀必须使用 进行处理s/[[:alnum:]]//g,同时避免对前缀和后缀字符串执行相同的操作。

因此,您需要隔离变量中的中缀字符串,或者,在 的情况下sed,在编辑缓冲区中,对其应用删除字母数字字符的操作,然后将前缀和后缀字符串重新应用到结果。

使用sed编辑脚本:

h
s/^{[^}]*}//
s/@[^@]*@$//
s/[[:alnum:]]//g
G
s/^\(.*\)\n\({[^}]*}\).*\(@[^@]*@\)$/\2\1\3/

测试:

$ sed -f script file
{string-no1}@string-no2@
{AAAAAAAAAA},.£@ZZZZZZZZZZ@
{GGGGGGGGGG}&:?@@@@@@@@@@@@

请注意,最后一行的中缀字符串实际上是

&:?@@@@@@@@@@

后缀是

@@

带注释的脚本:

# Remember the original line in the hold space.
h

# Remove the prefix and the suffix strings.
# The prefix is "{...}" at the start of the line.
# The suffix is "@...@" at the end of the line.
# The interior of these strings does not contain
# the respective string terminator.
s/^{[^}]*}//
s/@[^@]*@$//

# We are left with the isolated infix portion of the
# original line. Remove the alphanumerical characters
# from this. This creates the final infix string.
s/[[:alnum:]]//g

# Append the original line from the hold space to the end of
# the infix string with a newline (\n) as the delimiter.
G

# Match the modified infix, prefix, and suffix only, and
# substitute the entire buffer with these parts in the
# correct order.
s/^\(.*\)\n\({[^}]*}\).*\(@[^@]*@\)$/\2\1\3/

答案3

使用perl,您还可以执行以下操作:

perl -lne 'print /^\{.*?\}|@.*|\W/g' < your-file

\W匹配除 alnum 和下划线之外的字符(默认情况下仅匹配 ASCII 字符)。如果您希望包含下划线,则可以替换为[^a-zA-Z0-9]或。[^[:alnum:]]

使用,您可以在循环中sed删除第一个}和之后第一个之间的 alnum 字符:@

sed -e :1 -e 's/^\([^}]*}[^@]*\)[[:alnum:]]/\1/; t1' < your-file

对于sed[[:alnum:]]是在语言环境中进行分类的,并且文本根据语言环境的字符集进行解码,而perl默认情况下,文本被解释为好像在 iso8859-1 中编码,并且[[:alnum:]]仅与 ASCII 数字匹配(只要您不这样做) t 添加/u标志)。

通过将区域设置固定为( ),您可以获得类似于perls in 的行为,以及通过添加选项来获得类似于s in 的行为,该选项将根据区域设置字符集解码字符并使用 Unicode 属性(而不是区域设置分类)对字符进行分类。sedCLC_ALL=C sed...sedperl-Mopen=locale

答案4

awk如果您想避免使用正则表达式,这里有一个等效的:

cat file.txt
{string-no1}@string-no2@
{AAAAAAAAAA},.£@ZZZZZZZZZZ@
{GGGGGGGGGG}&:?@@@@@@@@@@@@

复制粘贴时可以留下评论,因为awk可以在内部处理它们

awk '
   BEGIN{                             # sets '}' as field separator
        FS="}"                        # splitting text into 2 fields 
   }                                  
   { 
    i=index($2,"@")                   # finds index position of '@'
    str1=$1                           # str1 = 1st field
    str2=substr($2,0,i-1)             # str2 = 2nd field until first '@' 
    str3=substr($2,i,length($2))      # str3 = 2nd field from '@' till end
    gsub(/[[:alnum:]]/,"",str2)       # replaces alphan in str2 with blanks
    print str1"}"str2 str3            # combines str1,str2,str3 and prints

}' file.txt

输出:

{string-no1}@string-no2@
{AAAAAAAAAA},.£@ZZZZZZZZZZ@
{GGGGGGGGGG}&:?@@@@@@@@@@@@

相关内容