カテゴリー
Sedコマンドで改行も含めた正規表現パターンをキャプチャする方法の考察
※ 当ページには【広告/PR】を含む場合があります。
2020/04/29
2022/09/30
今回は
はじめに
sed
Steam 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
今回の記事では、複数行にまたがる正規表現をどう取り扱うのかを深堀して理解してみようという内容です。
ちなみに、
EOL
sed
EOLの文字コード
本題に入る前に少し余談ですが、脳裏に留めておきたい知識として、上記の改行文字(エスケープシーケンス)の16進数文字(ASCII エンコーディングのバイト値)および8進法文字です。
それぞれ、16進数のプレフィックスは
0x
0X
0
プレフィックスなしは10進数を意味します。
ということで、ASCII文字コードに基づいたシステムにおいては、
CR:
0x0D = 015 = 13
LF:
0x0A = 012 = 10
CRLF:
0x0D 0x0A = 015 012 = 13 10
と解釈されます。
ちなみに、8進数表記をほとんど見かけるケースが少ない(...という個人の見解)ですが、通信制御分野では
厳密に8ビット
...進数の違いによる数字の取扱は混乱しやすいので気を払う必要があります。
Sedコマンドの動作環境のアレコレ
今回利用した
sed
Debian
GNU 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
手順①〜改行文字を一時的に別の文字に置換させる
まずは、
シングルクォートで囲ってエスケープシーケンス+文字リテラル置換
\n
sed
コンソールから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 sed
BSD 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>
シンプルとはいかないまでも、中々見通しのよいスクリプトにすることができました。
参考サイト
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー