Answers:
使此功能生效的关键是告诉sed
您排除不希望输出的内容,并指定所需的内容。
string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
这说:
-n
)p
)通常,在sed
使用括号捕获组中并使用向后引用输出捕获的内容:
echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
将输出“栏”。如果您将-r
(-E
用于OS X)用于扩展正则表达式,则无需转义括号:
echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'
最多可以有9个捕获组及其反向引用。后面的引用按组出现的顺序编号,但是它们可以按任何顺序使用并可以重复:
echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'
输出“ bar a”。
如果您具有GNU grep
(它也可能在BSD(包括OS X)中运行):
echo "$string" | grep -Po '\d+'
或诸如以下的变体:
echo "$string" | grep -Po '(?<=\D )(\d+)'
该-P
选项启用Perl兼容的正则表达式。请参阅man 3 pcrepattern
或man
3 pcresyntax
。
sed
示例,则使用-r
选项(或-E
对于OS X,是IIRC),则不需要转义括号。基本正则表达式和扩展正则表达式(-r
)之间的区别。
Sed最多可以记住9种模式,但是您需要使用转义括号来记住正则表达式的各个部分。
看到这里的例子和更多细节
sed -e 's/version=\(.+\)/\1/' input.txt
这仍然会输出整个input.txt
\+
而不是+
。而且我不明白为什么人们只使用-e
一个sed命令。
sed -e -n 's/version=\(.+\)/\1/p' input.txt
请参阅:mikeplate.com/2012/05/09/…–
sed -E
所谓的“现代”或“扩展”正则表达式,它们看起来更接近Perl / Java / JavaScript / Go /任何形式。(与grep -E
或相比egrep
。)默认语法具有那些奇怪的转义规则,被认为是“过时的”。有关两者之间差异的更多信息,请运行man 7 re_format
。
你可以使用grep
grep -Eow "[0-9]+" file
o
选择是有- unixhelp.ed.ac.uk/CGI/man-cgi?grep:-o,--only匹配显示匹配行的只有一部分匹配pattern
grep -Eow -e "[0-9]+" -e "[abc]{2,3}"
我不知道您如何可以要求将这两个表达式除了从上一个grep进行管道传递之外,必须位于一行上(如果任一模式在一行上匹配多个,则仍然无法使用)。
此答案适用于任何数量的数字组。例:
$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
有什么办法告诉sed仅输出捕获的组?
是。用捕获组替换所有文本:
$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123
s/[^0-9]* # several non-digits
\([0-9]\{1,\}\) # followed by one or more digits
[^0-9]* # and followed by more non-digits.
/\1/ # gets replaced only by the digits.
或使用扩展语法(较少的反引号,并允许使用+):
$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123
为避免在没有数字时打印原始文本,请使用:
$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
并匹配多个数字(并打印它们):
$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456
这适用于任何位数的运行:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
这与grep命令非常相似:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166
和模式:
/([\d]+)/
Sed无法识别'\ d'(快捷方式)语法。上面使用的ascii等效项[0-9]
并不完全等效。唯一的替代解决方案是使用字符类:'[[:digit:]]`。
选择的答案使用此类“字符类”构建解决方案:
$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
该解决方案仅适用于(完全)两个数字位数。
当然,由于答案是在Shell中执行的,因此我们可以定义几个变量以使答案更短:
$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"
但是,正如已经说明的那样,使用s/…/…/gp
命令更好:
$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987
这将覆盖数字的重复运行和编写短命令。
我相信问题中给出的模式仅是示例,目标是匹配任何模式。
如果您使用的是带有GNU扩展名的sed,允许在模式空间中插入换行符,则建议:
> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers
这些示例与CYGWIN 一起用于tcsh(是的,我知道它的外壳错误)。(编辑:对于bash,删除set和=周围的空格。)
+
,您需要对其进行转义或使用-r
选项(-E
对于OS X)。您也可以使用\{1,\}
(-r
或-E
不使用转义符)。
放弃并使用Perl
由于sed
不切实际,我们就扔毛巾使用Perl,至少它是LSB,而grep
GNU扩展不是:-)
打印整个匹配部分,无需匹配组或后退:
cat <<EOS | perl -lane 'print m/\d+/g'
a1 b2
a34 b56
EOS
输出:
12
3456
每行一次匹配,通常是结构化的数据字段:
cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
a1 b2
a34 b56
EOS
输出:
1
34
往后看:
cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
a1 b2
a34 b56
EOS
多个字段:
cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
a1 c0 b2 c0
a34 c0 b56 c0
EOS
输出:
1 2
34 56
每行多次匹配,通常是非结构化数据:
cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
a1 b2
a34 b56 a78 b90
EOS
输出:
1
34 78
往后看:
cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
a1 b2
a34 b56 a78 b90
EOS
输出:
1
3478
尝试
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
我在cygwin下得到了这个:
$ (echo "asdf"; \
echo "1234"; \
echo "asdf1234adsf1234asdf"; \
echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
1234
1234 1234
1 2 3 4 5 6 7 8 9
$
sed
使用该-E
标志打开扩展的正则表达式。