我正在尝试使用sed来清理URL行以仅提取域名..
所以来自:
http://www.suepearson.co.uk/product/174/71/3816/
我想要:
(有或没有尾随斜线,没关系)
我试过了:
sed 's|\(http:\/\/.*?\/\).*|\1|'
和(逃避非贪婪量词)
sed 's|\(http:\/\/.*\?\/\).*|\1|'
但我似乎无法使非贪婪量词工作,所以它总是最终匹配整个字符串。
基本或扩展的Posix / GNU正则表达式都不能识别非贪心量词;你需要一个后来的正则表达式。幸运的是,这个上下文的Perl正则表达式很容易获得:
perl -pe 's|(http://.*?/).*|\1|'
% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar
肯定有它的位置,但这不是其中之一!
正如迪伊指出:只需使用% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar
。在这种情况下,它更简单,更安全。这是一个使用Bash语法从URL中提取各种组件的示例:
string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print $1,$2,$3}' OFS="/"
给你:
sed
正如您所看到的,这是一种更灵活的方法。
(全部归功于迪)
仍然有希望使用纯(GNU)sed来解决这个问题。尽管在某些情况下这不是通用解决方案,但您可以使用“循环”来消除字符串中所有不必要的部分,如下所示:
cut
这里唯一的问题是它也会删除最后一个分隔符('/'),但是如果你真的需要它,你仍然可以在“循环”结束后简单地将它放回去,只需在前一个末尾追加这个附加命令命令行:
url="http://www.suepearson.co.uk/product/174/71/3816/"
protocol=$(echo "$url" | cut -d':' -f1)
host=$(echo "$url" | cut -d'/' -f3)
urlhost=$(echo "$url" | cut -d'/' -f1-3)
urlpath=$(echo "$url" | cut -d'/' -f4-)
protocol = "http"
host = "www.suepearson.co.uk"
urlhost = "http://www.suepearson.co.uk"
urlpath = "product/174/71/3816/"
sed -E将正则表达式解释为扩展(现代)正则表达式
更新:-E在MacOS X上,-r在GNU sed中。
因为您特别声明您尝试使用sed(而不是perl,cut等),请尝试分组。这避免了可能无法识别的非贪婪标识符。第一组是协议(即'http://','https://','tcp://'等)。第二组是域名:
echo "http://www.suon.co.uk/product/1/7/3/" | sed "s|^\(.*//\)\([^/]*\).*$|\1\2|"
如果您不熟悉分组,请启动sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop"
。
我意识到这是一个旧条目,但有人可能会发现它很有用。由于完整域名的总长度不得超过253个字符,因此替换。* with。\ {1,255 \}
这是如何使用sed稳健地进行多字符串的非贪婪匹配。假设您想要将每个-e "s,$,/,"
更改为sed 's|(http:\/\/[^\/]+\/).*|\1|'
,例如此输入:
here
应该成为这个输出:
foo...bar
要做到这一点,你将foo和bar转换为单个字符,然后使用它们之间的那些字符的否定:
<foo...bar>
在上面:
$ cat file
ABC foo DEF bar GHI foo KLM bar NOP foo QRS bar TUV
正在将ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV
和$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/g; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV
转换为输入中不存在的占位符字符串,因此这些字符可用于将s/@/@A/g; s/{/@B/g; s/}/@C/g
和{
转换为。}
分别将foo
和bar
转换为s/foo/{/g; s/bar/}/g
和foo
bar
正在执行我们想要的操作 - 将{
转换为}
s/{[^{}]*}/<&>/g
正在将foo...bar
和<foo...bar>
转换回s/}/bar/g; s/{/foo/g
和{
。}
正在将占位符字符串转换回原始字符。请注意,上面的内容不依赖于输入中不存在的任何特定字符串,因为它在第一步中制作了这样的字符串,也不关心您想要匹配的任何特定正则表达式的出现,因为您可以使用foo
多次在表达式中必要的,以隔离您想要的实际匹配和/或与seds数字匹配运算符,例如仅替换第二次出现:
bar
s/@C/}/g; s/@B/{/g; s/@A/@/g
不要打扰,我在另一个论坛上得到它:)
{[^{}]*}
也有效
另一个sed版本:
$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/2; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC foo DEF bar GHI <foo KLM bar> NOP foo QRS bar TUV
它匹配echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*|\1|'
后跟一个字母数字字符(所以不是另一个正斜杠)以及其余字符直到行尾。之后它没有替换它(即删除它。)
在这种特定情况下,您可以在不使用非贪婪正则表达式的情况下完成工作。
试试这个非贪婪的正则表达式[^/]*
而不是.*?
:
sed 's|\(http://[^/]*/\).*|\1|g'
这是你可以用两步法和awk做的事情:
sed 's|\(http:\/\/www\.[a-z.0-9]*\/\).*|\1|
输出:
sed 's|/[:alphanum:].*||' file.txt
希望有所帮助!
还没有看到这个答案,所以这里你可以用/
或A=http://www.suepearson.co.uk/product/174/71/3816/
echo $A|awk '
{
var=gensub(///,"||",3,$0) ;
sub(/\|\|.*/,"",var);
print var
}'
做到这一点:
http://www.suepearson.co.uk
这将全局运行vi
vim
替换(尾随vi -c '%s/\(http:\/\/.\{-}\/\).*/\1/ge | wq' file &>/dev/null
),如果找不到模式(vi
)则避免引发错误,然后将得到的更改保存到磁盘并退出。 :%s
防止GUI在屏幕上短暂闪烁,这可能很烦人。
我喜欢有时使用g
作为超级复杂的正则表达式,因为(1)perl是
死
死亡,(2)vim有一个非常先进的正则表达式引擎,(3)我已经在我的日常使用编辑文档中非常熟悉e
正则表达式。
使用sed,我通常通过搜索除分隔符之外的任何内容来实现非贪婪搜索,直到分隔符为止:
echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*\)/.*;\1;p'
输出:
http://www.suon.co.uk
这是:
-n
s/<pattern>/<replace>/p
;
搜索命令分隔符而不是/
,以便更容易键入所以s;<pattern>;<replace>;p
\(
...\)
\1
,\2
意味着http://
或[]
或[ab/]
a
的第一个b
意味着/
,所以除了^
中的东西之外的任何东西[]
意味着除了not
角色以外的任何东西[]
将重复前一组,所以[^/]
意味着除/
之外的人物。*
意味着搜索和记住[^/]*
follow任何字符除了/
并记住你发现了什么sed -n 's;\(http://[^/]*\)
所以在最后添加另一个http://
:/
但我们想要匹配域之后的其余行,所以添加/
/
)记住的比赛是域名,所以用sed -n 's;\(http://[^/]*\)/'
组中保存的东西替换匹配的行并打印:.*
如果你想在域之后包含反斜杠,那么在组中添加一个反斜杠来记住:
\1
输出:
\1
sed不支持“非贪婪”运营商。
您必须使用“[]”运算符从匹配中排除“/”。
sed -n 's;\(http://[^/]*\)/.*;\1;p'
附:没有必要反斜杠“/”。
echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*/\).*;\1;p'
中的懒惰(非贪婪)量词和所有其他正则表达口味!
http://www.suon.co.uk/
选项)
正则表达式:
sed 's,\(http://[^/]*\)/.*,\1,'
桑达:
sed
示例(查找第一个数字序列)-r
:
(EXPRESSION).*|.
sed -r "s/(EXPRESSION).*|./\1/g" # Global `g` modifier should be on
它是如何工作的?
这个正则表达式得益于交替Live demo。在每个位置,引擎将寻找交替的第一侧(我们的目标),如果不匹配,则交替的第二侧有一个点$ sed -r "s/([0-9]+).*|./\1/g" <<< "foo 12 bar 34"
匹配下一个直接字符。
12
由于设置了全局标志,因此引擎会尝试继续逐个字符地匹配输入字符串或目标的末尾。只要交替左侧的第一个也是唯一一个捕获组匹配,|
线路的其余部分立即被消耗,以及.
。我们现在在第一个捕获组中保持我们的价值。
POSIX BRE
正则表达式:
桑达:
(EXPRESSION)
示例(查找第一个数字序列):
.*
\(\(\(EXPRESSION\).*\)*.\)*
这个版本与ERE版本类似,但没有涉及更改。就这样。在每个单一位置,引擎会尝试匹配一个数字。
sed "s/\(\(\(EXPRESSION\).*\)*.\)*/\3/"
如果找到,则消耗并捕获其他后续数字,然后立即匹配其余行,否则因为$ sed "s/\(\(\([0-9]\{1,\}\).*\)*.\)*/\3/" <<< "foo 12 bar 34"
意味着更多或零,它跳过第二个捕获组12
并到达点以匹配单个字符并且此过程继续。*
输入字符串:
\(\([0-9]\{1,\}\).*\)*
-EDE:.
-SDE:sed "s/\(END-DELIMITER-EXPRESSION\).*/\1/; \
s/\(\(START-DELIMITER-EXPRESSION.*\)*.\)*/\1/g"
foobar start block #1 end barfoo start block #2 end
输出:
end
第一个正则表达式start
匹配并捕获第一个结束分隔符$ sed "s/\(end\).*/\1/; s/\(\(start.*\)*.\)*/\1/g"
和替换所有匹配最近捕获的字符,这是最终分隔符。在这个阶段,我们的输出是:start block #1 end
。
\(end\).*
然后将结果传递给第二个正则表达式end
,它与上面的POSIX BRE版本相同。如果起始分隔符foobar start block #1 end
未匹配,则匹配单个字符,否则匹配并捕获起始分隔符并匹配其余字符。
使用方法#2(分隔表达式),您应该选择两个适当的表达式:
\(\(start.*\)*.\)*
start
用法:
输出:
[^:/]\/
这个帖子真的很旧但我认为人们仍然需要它。让我们说你要杀掉一切,直到第一次出现http:
。你不能说$ sed "s/\([^:/]\/\).*/\1/g; s/\(\(http:.*\)*.\)*/\1/" <<< "http://www.suepearson.co.uk/product/174/71/3816/"
......
所以一个不错的解决方案涉及两个步骤,假设你可以在输入中留下一个你不期望的独特单词,比如http://www.suepearson.co.uk/
。
在这种情况下,我们可以:
HELLO
当然,通过更简单的输入,您可以使用更小的单词,甚至可以使用单个字符。
HTH!
这可以使用cut来完成:
[^HELLO]
top_sekrit
在sed中获得非贪婪匹配的技巧是匹配除终止匹配的字符之外的所有字符。我知道,这是一个不费吹灰之力,但我浪费了宝贵的时间,而且shell脚本应该是快速而简单的。所以如果其他人可能需要它:
贪心匹配
s/HELLO/top_sekrit/ #will only replace the very first occurrence
s/.*top_sekrit// #kill everything till end of the first HELLO
非贪心匹配
echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3
另一种方法,不使用正则表达式,是使用字段/分隔符方法,例如
sed - non greedy matching by Christoph Sieghart