カテゴリー
Sedコマンドで改行も含めた正規表現パターンをキャプチャする方法の考察
※ 当ページには【広告/PR】を含む場合があります。
2020/04/29
2022/09/30
今回は
はじめに
sedSteam EDiter広義のテキスト・ストリーム処理では、1つのデータを読み出すための、
データの終わり識別子これには特に規定のフォーマットないようですが、
sedデータの終わりEOL(End of Line)ちなみに
UNIX系/Linux OS/現行のMac OS:
\n
LF(Line Feed)と記される。
単に"改行"と呼ばれればだいたいコッチ。
昔のMac OS(バージョン9以前):
\r
CR(Carriage Return)と記される。
コンソールから見て一番左端の文字入力位置に戻すので"復帰"と呼ばれる。
Windows系:
\r\n
CR+LFの意味。
が採用されている(いた)ようです。
よって、基本的な
sed今回の記事では、複数行にまたがる正規表現をどう取り扱うのかを深堀して理解してみようという内容です。
ちなみに、
EOLsedEOLの文字コード
本題に入る前に少し余談ですが、脳裏に留めておきたい知識として、上記の改行文字(エスケープシーケンス)の16進数文字(ASCII エンコーディングのバイト値)および8進法文字です。
それぞれ、16進数のプレフィックスは
0x0X0プレフィックスなしは10進数を意味します。
ということで、ASCII文字コードに基づいたシステムにおいては、
CR:
0x0D = 015 = 13
LF:
0x0A = 012 = 10
CRLF:
0x0D 0x0A = 015 012 = 13 10
と解釈されます。
ちなみに、8進数表記をほとんど見かけるケースが少ない(...という個人の見解)ですが、通信制御分野では
厳密に8ビット...進数の違いによる数字の取扱は混乱しやすいので気を払う必要があります。
Sedコマンドの動作環境のアレコレ
今回利用した
sedDebianGNU sedよく比較される
BSD sed $ sed --version
sed (GNU sed) 4.7
パッケージ作成者: Debian
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
作者 Jay Fenlason、 Tom Lord、 Ken Pizzini、
Paolo Bonzini、 Jim Meyering、および Assaf Gordon。
GNU sed home page: <https://www.gnu.org/software/sed/>.
General help using GNU software: <https://www.gnu.org/gethelp/>.
E-mail bug reports to: <bug-sed@gnu.org>.
Sedで改行までキャプチャさせてみる
では実際の例を挙げて、
sedひとまず以下のような改行を含むHTMLテキストファイルを
cat $ cat test.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css; charset=UTF-8">
<meta http-equiv="Content-Script-Type" content="text/javascript; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<base href="/">
</head>
<body>
<div>
<header>
<span>蛸壺のプロブラミング・ブログ</span>
<a href="/home"><span>HOME</span></a>
</header>
<footer>
<ul>
<li><a href="/home">Home</a> |</li>
<li><a href="/home#blog">Blog</a> |</li>
<li><a href="/home#about">About</a> |</li>
<li><a href="/home#contactus">Contact Us</a></li>
</ul>
</footer>
</div>
</body>
</html>
たとえば、
<head>~</head>sed $ str_show=$(cat test.html)
$ echo $str_show
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Style-Type" content="text/css; charset=UTF-8"> <meta http-equiv="Content-Script-Type" content="text/javascript; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <base href="/"> </head> <body> <div> <header> <span>蛸壺のプロブラミング・ブログ</span> <a href="/home"><span>HOME</span></a> </header> <footer> <ul> <li><a href="/home">Home</a> |</li> <li><a href="/home#blog">Blog</a> |</li> <li><a href="/home#about">About</a> |</li> <li><a href="/home#contactus">Contact Us</a></li> </ul> </footer> </div> </body> </html>
#複数行にまたがる<head>要素の中身をキャプチャして消去したい
$ echo $str_show | sed -e "s/<head>[\s\S]*<\/head>//g"
#消えてない(複数行ではキャプチャされない)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Style-Type" content="text/css; charset=UTF-8"> <meta http-equiv="Content-Script-Type" content="text/javascript; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <base href="/"> </head> <body> <div> <header> <span>蛸壺のプロブラミング・ブログ</span> <a href="/home"><span>HOME</span></a> </header> <footer> <ul> <li><a href="/home">Home</a> |</li> <li><a href="/home#blog">Blog</a> |</li> <li><a href="/home#about">About</a> |</li> <li><a href="/home#contactus">Contact Us</a></li> </ul> </footer> </div> </body> </html>
#複数行にまたがる<head>要素の中身をキャプチャして消去したい(シングルクォートで囲う)
$ echo $str_show | sed -e 's/<head>[\s\S]*<\/head>//g'
#消えてない(複数行ではキャプチャされない)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Style-Type" content="text/css; charset=UTF-8"> <meta http-equiv="Content-Script-Type" content="text/javascript; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <base href="/"> </head> <body> <div> <header> <span>蛸壺のプロブラミング・ブログ</span> <a href="/home"><span>HOME</span></a> </header> <footer> <ul> <li><a href="/home">Home</a> |</li> <li><a href="/home#blog">Blog</a> |</li> <li><a href="/home#about">About</a> |</li> <li><a href="/home#contactus">Contact Us</a></li> </ul> </footer> </div> </body> </html>
このように通常通りに
sed手順①〜改行文字を一時的に別の文字に置換させる
まずは、
シングルクォートで囲ってエスケープシーケンス+文字リテラル置換\nsedコンソールからLFを入力するには、ダブルクォート囲いなしの
$'\n' #改行文字LFを出力
$ echo $'\n'
#16進法でLF(上のコマンドと等価)
$ echo $'\x0a'
#8進法でLF(上のコマンドと等価)
$ echo $'\012'
これを利用すると、改行文字を
\n上記でも解説したように、
\x0a\012基本どれを利用されてもOKです。
$ str_show_2=${str_show//$'\n'/\\n}
$ echo $str_show_2
<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8">\n <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n <meta http-equiv="Content-Style-Type" content="text/css; charset=UTF-8">\n <meta http-equiv="Content-Script-Type" content="text/javascript; charset=UTF-8">\n <meta http-equiv="X-UA-Compatible" content="IE=Edge">\n <base href="/">\n </head>\n <body>\n <div>\n <header>\n <span>蛸壺のプロブラミング・ブログ</span>\n <a href="/home"><span>HOME</span></a>\n </header>\n <footer>\n <ul>\n <li><a href="/home">Home</a> |</li>\n <li><a href="/home#blog">Blog</a> |</li>\n <li><a href="/home#about">About</a> |</li>\n <li><a href="/home#contactus">Contact Us</a></li>\n </ul>\n </footer>\n </div>\n </body>\n</html>
これで一行扱いになったので
sed $ echo $str_show_2 | sed -e 's/<head>.*<\/head>//g'
<!doctype html>\n<html lang="en">\n \n <body>\n <div>\n <header>\n <span>蛸壺のプロブラミング・ブログ</span>\n <a href="/home"><span>HOME</span></a>\n </header>\n <footer>\n <ul>\n <li><a href="/home">Home</a> |</li>\n <li><a href="/home#blog">Blog</a> |</li>\n <li><a href="/home#about">About</a> |</li>\n <li><a href="/home#contactus">Contact Us</a></li>\n </ul>\n </footer>\n </div>\n </body>\n</html>
これで通常通りの
sed<head>あとは、改行文字として置換した
\n余談〜BSD sedで文字置換する場合
GNU sed${str_show//$'\n'/\\n}BSD sedBSD sed${str_show//$'\n'/\\\\n}\ % str_show_2=${str_show//$'\n'/\\\\n}
% echo $str_show_2
<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8">\n <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n <meta http-equiv="Content-Style-Type" content="text/css; charset=UTF-8">\n <meta http-equiv="Content-Script-Type" content="text/javascript; charset=UTF-8">\n <meta http-equiv="X-UA-Compatible" content="IE=Edge">\n <base href="/">\n </head>\n <body>\n <div>\n <header>\n <span>蛸壺のプロブラミング・ブログ</span>\n <a href="/home"><span>HOME</span></a>\n </header>\n <footer>\n <ul>\n <li><a href="/home">Home</a> |</li>\n <li><a href="/home#blog">Blog</a> |</li>\n <li><a href="/home#about">About</a> |</li>\n <li><a href="/home#contactus">Contact Us</a></li>\n </ul>\n </footer>\n </div>\n </body>\n</html>
これで
GNU sed手順②〜改行文字を一時的に別の文字に置換させる
前項目では
改行文字(LF) --> \n\n --> 改行文字(LF)まず、当然ながら先程の文字リテラルの置換の逆操作で、以下のように可能です。
$ echo "${str_show_2//\\n/$'\n'}"
<!doctype html>
<html lang="en">
<body>
<div>
<header>
<span>蛸壺のプロブラミング・ブログ</span>
<a href="/home"><span>HOME</span></a>
</header>
<footer>
<ul>
<li><a href="/home">Home</a> |</li>
<li><a href="/home#blog">Blog</a> |</li>
<li><a href="/home#about">About</a> |</li>
<li><a href="/home#contactus">Contact Us</a></li>
</ul>
</footer>
</div>
</body>
</html>
ちなみに、
echo(")これだけだと
sedそこで同様の操作を
sed $ echo $str_show_2 | sed -e s/'\\n'/\\$'\n'/g
<!doctype html>
<html lang="en">
<body>
<div>
<header>
<span>蛸壺のプロブラミング・ブログ</span>
<a href="/home"><span>HOME</span></a>
</header>
<footer>
<ul>
<li><a href="/home">Home</a> |</li>
<li><a href="/home#blog">Blog</a> |</li>
<li><a href="/home#about">About</a> |</li>
<li><a href="/home#contactus">Contact Us</a></li>
</ul>
</footer>
</div>
</body>
</html>
このとき
sed\\$'\n'ここの部分で
$$sedそこでコマンド入力だと示すために
\\$まとめ
以上の手順から、
sed今回やったことをパイプライン処理でつなげ、更に元のファイルに上書き保存まで加えると、以下のようになります。
$ str_show=$(cat test.html)
$ echo ${str_show//$'\n'/\\n} | sed -e s/'<head>.*<\/head>'//g -e s/'\\n'/\\$'\n'/g > test.html
<!doctype html>
<html lang="en">
<body>
<div>
<header>
<span>蛸壺のプロブラミング・ブログ</span>
<a href="/home"><span>HOME</span></a>
</header>
<footer>
<ul>
<li><a href="/home">Home</a> |</li>
<li><a href="/home#blog">Blog</a> |</li>
<li><a href="/home#about">About</a> |</li>
<li><a href="/home#contactus">Contact Us</a></li>
</ul>
</footer>
</div>
</body>
</html>
シンプルとはいかないまでも、中々見通しのよいスクリプトにすることができました。