在 bash 脚本中通过正则表达式获取目标子字符串的最佳方法是什么
what is the best way to get target substring through regex in bash script
我正在创建一个脚本来自动提取大量文本文件;目前,我的问题是从 .html 文件中获取目标 ID,示例如下:
\ \ <body id="some_id" class="calibre2">
我的脚本功能是获取“some_id”并检查它是否有效(ID 不允许以数字开头)否则将此 id 修复为 .html 文件和其他相关文件(toc.ncx、content.opf 等),我主要使用的命令是 sed(但我认为我的方法很麻烦),shell 是下面:
#!/bin/bash
for var in ./*
do
if [[ $var =~ .*.html ]]
then
if grep -q -E '<body id="[0-9]+' $var
then
ID="$(sed -n -E 's/\ \ <body id="[0-9]+(.*?)"\ .*//gp' $var)"
echo $ID
sed -i -E 's/<body\ id="([0-9]+)/<body id="id/g' $var
sed -i -E "s/$ID/id$ID/g" ./../toc.ncx
echo $var
fi
fi
done
也就是说我不知道html的ID,但是我知道ID的规则,例子如下:
\ \ <body id="123char" class="calibre2">
"123char"无效,因为ID不允许以数字开头,所以我需要通过附加前缀字符来修复ID,比如"idchar",所以html变成如下:
\ \ <body id="idchar" class="calibre2">
同时我需要更新其他文件的id(将“123char”更改为“idchar”),例如.ncx文件
<content src="Text/xxx1.html#123char"/>
<!--need changes id as follow-->
<content src="Text/xxx1.html#idchar"/>
PS:如上所示,此shell旨在修复无法通过epub验证器的.epub修复,许多从mobi到epub的电子书转换器都存在此类错误(calibre, convertio...等)
使用 Regex 解析 html 并不容易,也不是正确的工具。
您可以使用 pup 这是一个 HTML 解析器。
输入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Here is h1 tag</h1>
</body>
</html>
测试
pup 'h1 text{}' < index.html
输出
Here is h1 tag
出于任何原因,如果您更喜欢使用正则表达式,perl is much more suitable than bash。将此作为输入:
样本 1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body id="some_id" class="calibre2">
<h1>this is h1 tag</h1>
</body
</html>
用这个 perl one-liner
perl -lne '/<body\s+id="\K[^"]+/ && print $&' index.htm
输出将是:
some_id
样本 2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body id="some_id" class="calibre2">
<h1 id="number-1">this is h1 tag</h1>
<h1 id="number-2">this is h1 tag</h1>
<h1 id="number-3">this is h1 tag</h1>
</body
</html>
Perl one-liner
perl -lne '/<h1\s+id="\K[^"]+/ && print $&' index.html
输出
number-1
number-2
number-3
如果您更喜欢使用 grep
,您可以使用 -P
选项来应用 PCRE(Perl 兼容正则表达式)
grep -oP '<h1\s+id="\K[^"]+' index.html
# output
number-1
number-2
number-3
使用 bash 函数获取标签的 ID 值:
#!/bin/bash
function match_html_id(){
{
local tag=;
local regex="<${tag}\s+id=\"\K[^\"]+";
local filename="";
local result='';
if grep -P "$regex" "$filename" > /dev/null 2>&1; then
result=$(grep -oP $regex $filename);
echo 'match found';
else
echo 'match not found';
fi
} >&2;
echo $result;
}
declare -r r=$(match_html_id body index.html);
echo r: "'$r'"
body 标签上样本 2 或 1 的输出
match found
r: 'some_id'
这已经在这里重复了无数次;使用正则表达式 parse/edit HTML 是一个非常糟糕的主意!像 xidel 这样的 HTML 解析器会更合适。事实上,凭借其集成的 EXPath 文件模块,您只需一次调用即可:
$ xidel -se '
for $x in file:list(.,false(),"*.html")
where matches(doc($x)//body/@id,"^\d")
return
file:write(
$x,
x:replace-nodes(
doc($x)//body/@id,
function($x){attribute {name($x)} {replace($x,"^\d+","id")}}
),
{"method":"html","indent":true()}
)
'
file:list(.,false(),"*.html")
returns 当前目录下的所有 HTML-files
matches(doc($x)//body/@id,"^\d")
将其限制为仅那些 HTML-files 的 id
属性值以数字开头。
x:replace-nodes( [...] )
用字符串“id”替换该值的数字。
file:write( [...] )
替换原来的HTML-file.
我正在创建一个脚本来自动提取大量文本文件;目前,我的问题是从 .html 文件中获取目标 ID,示例如下:
\ \ <body id="some_id" class="calibre2">
我的脚本功能是获取“some_id”并检查它是否有效(ID 不允许以数字开头)否则将此 id 修复为 .html 文件和其他相关文件(toc.ncx、content.opf 等),我主要使用的命令是 sed(但我认为我的方法很麻烦),shell 是下面:
#!/bin/bash
for var in ./*
do
if [[ $var =~ .*.html ]]
then
if grep -q -E '<body id="[0-9]+' $var
then
ID="$(sed -n -E 's/\ \ <body id="[0-9]+(.*?)"\ .*//gp' $var)"
echo $ID
sed -i -E 's/<body\ id="([0-9]+)/<body id="id/g' $var
sed -i -E "s/$ID/id$ID/g" ./../toc.ncx
echo $var
fi
fi
done
也就是说我不知道html的ID,但是我知道ID的规则,例子如下:
\ \ <body id="123char" class="calibre2">
"123char"无效,因为ID不允许以数字开头,所以我需要通过附加前缀字符来修复ID,比如"idchar",所以html变成如下:
\ \ <body id="idchar" class="calibre2">
同时我需要更新其他文件的id(将“123char”更改为“idchar”),例如.ncx文件
<content src="Text/xxx1.html#123char"/>
<!--need changes id as follow-->
<content src="Text/xxx1.html#idchar"/>
PS:如上所示,此shell旨在修复无法通过epub验证器的.epub修复,许多从mobi到epub的电子书转换器都存在此类错误(calibre, convertio...等)
使用 Regex 解析 html 并不容易,也不是正确的工具。
您可以使用 pup 这是一个 HTML 解析器。
输入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Here is h1 tag</h1>
</body>
</html>
测试
pup 'h1 text{}' < index.html
输出
Here is h1 tag
出于任何原因,如果您更喜欢使用正则表达式,perl is much more suitable than bash。将此作为输入:
样本 1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body id="some_id" class="calibre2">
<h1>this is h1 tag</h1>
</body
</html>
用这个 perl one-liner
perl -lne '/<body\s+id="\K[^"]+/ && print $&' index.htm
输出将是:
some_id
样本 2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body id="some_id" class="calibre2">
<h1 id="number-1">this is h1 tag</h1>
<h1 id="number-2">this is h1 tag</h1>
<h1 id="number-3">this is h1 tag</h1>
</body
</html>
Perl one-liner
perl -lne '/<h1\s+id="\K[^"]+/ && print $&' index.html
输出
number-1
number-2
number-3
如果您更喜欢使用 grep
,您可以使用 -P
选项来应用 PCRE(Perl 兼容正则表达式)
grep -oP '<h1\s+id="\K[^"]+' index.html
# output
number-1
number-2
number-3
使用 bash 函数获取标签的 ID 值:
#!/bin/bash
function match_html_id(){
{
local tag=;
local regex="<${tag}\s+id=\"\K[^\"]+";
local filename="";
local result='';
if grep -P "$regex" "$filename" > /dev/null 2>&1; then
result=$(grep -oP $regex $filename);
echo 'match found';
else
echo 'match not found';
fi
} >&2;
echo $result;
}
declare -r r=$(match_html_id body index.html);
echo r: "'$r'"
body 标签上样本 2 或 1 的输出
match found
r: 'some_id'
这已经在这里重复了无数次;使用正则表达式 parse/edit HTML 是一个非常糟糕的主意!像 xidel 这样的 HTML 解析器会更合适。事实上,凭借其集成的 EXPath 文件模块,您只需一次调用即可:
$ xidel -se '
for $x in file:list(.,false(),"*.html")
where matches(doc($x)//body/@id,"^\d")
return
file:write(
$x,
x:replace-nodes(
doc($x)//body/@id,
function($x){attribute {name($x)} {replace($x,"^\d+","id")}}
),
{"method":"html","indent":true()}
)
'
file:list(.,false(),"*.html")
returns 当前目录下的所有 HTML-filesmatches(doc($x)//body/@id,"^\d")
将其限制为仅那些 HTML-files 的id
属性值以数字开头。x:replace-nodes( [...] )
用字符串“id”替换该值的数字。file:write( [...] )
替换原来的HTML-file.