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