我在 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
标志)。
通过将区域设置固定为( ),您可以获得类似于perl
s in 的行为,以及通过添加选项来获得类似于s in 的行为,该选项将根据区域设置字符集解码字符并使用 Unicode 属性(而不是区域设置分类)对字符进行分类。sed
C
LC_ALL=C sed...
sed
perl
-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}&:?@@@@@@@@@@@@