Jan 23 00:46:24 portal postfix/smtp[31481]: 1B1653FEA1: to=<wanted1918_ke@yahoo.com>, relay=mta5.am0.yahoodns.net[98.138.112.35]:25, delay=5.4, delays=0.02/3.2/0.97/1.1, dsn=5.0.0, status=bounced (host mta5.am0.yahoodns.net[98.138.112.35] said: 554 delivery error: dd This user doesn't have a yahoo.com account (wanted1918_ke@yahoo.com) [0] - mta1321.mail.ne1.yahoo.com (in reply to end of DATA command))
Jan 23 00:46:24 portal postfix/smtp[31539]: AF40C3FE99: to=<devi_joshi@yahoo.com>, relay=mta7.am0.yahoodns.net[98.136.217.202]:25, delay=5.9, delays=0.01/3.1/0.99/1.8, dsn=5.0.0, status=bounced (host mta7.am0.yahoodns.net[98.136.217.202] said: 554 delivery error: dd This user doesn't have a yahoo.com account (devi_joshi@yahoo.com) [0] - mta1397.mail.gq1.yahoo.com (in reply to end of DATA command))
从上面的maillog我想提取括号< ... >
之间的电子邮件地址,例如。 to=<wanted1918_ke@yahoo.com>
至wanted1918_ke@yahoo.com
我正在使用cut -d' ' -f7
来提取电子邮件,但我很好奇是否有更灵活的方法。
答案 0 :(得分:5)
使用GNU grep,只需使用包含外观的正则表达式并向前看:
$ grep -Po '(?<=to=<).*(?=>)' file
wanted1918_ke@yahoo.com
devi_joshi@yahoo.com
这说:嘿,提取所有字符串前面加to=<
后跟>
。
答案 1 :(得分:3)
您可以像这样使用awk
:
awk -F'to=<|>,' '{print $2}' the.log
我按to=<
或>,
拆分行并打印第二个字段。
答案 2 :(得分:3)
仅显示sed
替代方案(由于sed
需要GNU或BSD / macOS -E
):
sed -E 's/.* to=<(.*)>.*/\1/' file
请注意正则表达式必须与整个行匹配,以便替换捕获组匹配(电子邮件地址),使仅匹配。
稍微更有效 - 但可能不太可读 - 变化是
sed -E 's/.* to=<([^>]*).*/\1/' file
由于BRE(基本正则表达式)所需的遗留语法,符合POSIX的公式更加麻烦:
sed 's/.* to=<\(.*\)>.*/\1/' file
fedorqui's helpful GNU grep
answer的变体:
grep -Po ' to=<\K[^>]*' file
\K
,删除与此相匹配的所有内容,不仅在语法上比后面的断言((?<=...)
更简单,而且更灵活 - 它支持变量 -length表达式 - 并且更快(尽管在许多实际情况下这可能无关紧要;如果性能至关重要:见下文)。
以下是本页各种解决方案在性能方面的比较。
请注意,在许多用例中这可能并不重要,但可以深入了解:
绝对值并不重要,但相对表现有望提供一些见解。请参阅底部,了解产生这些数字的脚本,这些数据是在2012年末27和34岁时获得的。 iMac运行macOS 10.12.3,使用通过复制问题中的样本输入创建的250,000行输入文件,平均每次运行10次。
Mawk 0.364s
GNU grep, \K, non-backtracking 0.392s
GNU awk 0.830s
GNU grep, \K 0.937s
GNU grep, (?>=...) 1.639s
BSD grep + cut 2.733s
GNU grep + cut 3.697s
BSD awk 3.785s
BSD sed, non-backtracking 7.825s
BSD sed 8.414s
GNU sed 16.738s
GNU sed, non-backtracking 17.387s
一些结论:
grep
通常是一个不错的选择,即使它需要与cut
sed
出乎意料地慢,而GNU awk
比BSD awk
快。奇怪的是,(部分)非回溯解决方案较慢与GNU sed
。这是产生上述时间的脚本;请注意,g
- 前缀命令是通过Homebrew安装在macOS上的 GNU 实用程序;同样,mawk
是通过Homebrew安装的。
请注意&#34;非回溯&#34;仅将部分应用于某些命令。
#!/usr/bin/env bash
# Define the test commands.
test01=( 'BSD sed' sed -E 's/.*to=<(.*)>.*/\1/' )
test02=( 'BSD sed, non-backtracking' sed -E 's/.*to=<([^>]*).*/\1/' )
# ---
test03=( 'GNU sed' gsed -E 's/.*to=<(.*)>.*/\1/' )
test04=( 'GNU sed, non-backtracking' gsed -E 's/.*to=<([^>]*).*/\1/' )
# ---
test05=( 'BSD awk' awk -F' to=<|>,' '{print $2}' )
test06=( 'GNU awk' gawk -F' to=<|>,' '{print $2}' )
test07=( 'Mawk' mawk -F' to=<|>,' '{print $2}' )
#--
test08=( 'GNU grep, (?>=...)' ggrep -Po '(?<= to=<).*(?=>)' )
test09=( 'GNU grep, \K' ggrep -Po ' to=<\K.*(?=>)' )
test10=( 'GNU grep, \K, non-backtracking' ggrep -Po ' to=<\K[^>]*' )
# --
test11=( 'BSD grep + cut' "{ grep -o ' to=<[^>]*' | cut -d'<' -f2; }" )
test12=( 'GNU grep + cut' "{ ggrep -o ' to=<[^>]*' | gcut -d'<' -f2; }" )
# Determine input and output files.
inFile='file'
# NOTE: Do NOT use /dev/null, because GNU grep apparently takes a shortcut
# when it detects stdout going nowhere, which distorts the timings.
# Use dev/tty if you want to see stdout in the terminal (will print
# as a single block across all tests before the results are reported).
outFile="/tmp/out.$$"
# outFile='/dev/tty'
# Make `time` only report the overall elapsed time.
TIMEFORMAT='%6R'
# How many runs per test whose timings to average.
runs=10
# Read the input file up to even the playing field, so that the first command
# doesn't take the hit of being the first to load the file from disk.
echo "Warming up the cache..."
cat "$inFile" >/dev/null
# Run the tests.
echo "Running $(awk '{print NF}' <<<"${!test*}") test(s), averaging the timings of $runs run(s) each; this may take a while..."
{
for n in ${!test*}; do
arrRef="$n[@]"
test=( "${!arrRef}" )
# Print test description.
printf '%s\t' "${test[0]}"
# Execute test command.
if (( ${#test[@]} == 2 )); then # single-token command? assume `eval` must be used.
time for (( n = 0; n < runs; n++ )); do eval "${test[@]: 1}" < "$inFile" >"$outFile"; done
else # multiple command tokens? assume that they form a simple command that can be invoked directly.
time for (( n = 0; n < runs; n++ )); do "${test[@]: 1}" "$inFile" >"$outFile"; done
fi
done
} 2>&1 |
sort -t$'\t' -k2,2n |
awk -v runs="$runs" '
BEGIN{FS=OFS="\t"} { avg = sprintf("%.3f", $2/runs); print $1, avg "s" }
' | column -s$'\t' -t
答案 3 :(得分:0)
awk -F'[<>]' '{print $2}' file
wanted1918_ke@yahoo.com
devi_joshi@yahoo.com