tag:crieit.net,2005:https://crieit.net/tags/sed/feed 「sed」の記事 - Crieit Crieitでタグ「sed」に投稿された最近の記事 2023-07-06T01:13:51+09:00 https://crieit.net/tags/sed/feed tag:crieit.net,2005:PublicArticle/18505 2023-07-06T01:13:51+09:00 2023-07-06T01:13:51+09:00 https://crieit.net/posts/Ubuntu-Main Ubuntu で Main リポジトリ以外のインストール済みパッケージを一覧にする <p>以下のコマンドを使用すると、 Ubuntu にインストールされているパッケージのうち、Main リポジトリ以外(Restricted, Universe, Multiverse)に属するものを一覧で表示できる。</p> <pre><code>dpkg-query --show --showformat='${binary:Package}\n' | xargs -IX bash -c "apt-cache policy X | sed -n --regexp-extended '1h;/.*(\/restricted|\/universe|\/multiverse) .*/{x;p;x;p;Q}'" </code></pre> <p>以上!</p> <p>…とすると、この記事だけでは少し物足りない気がするので、使い方についても少し説明しておく。</p> <h2 id="なぜ Main リポジトリ以外のものを把握したいのか?"><a href="#%E3%81%AA%E3%81%9C+Main+%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E4%BB%A5%E5%A4%96%E3%81%AE%E3%82%82%E3%81%AE%E3%82%92%E6%8A%8A%E6%8F%A1%E3%81%97%E3%81%9F%E3%81%84%E3%81%AE%E3%81%8B%EF%BC%9F">なぜ Main リポジトリ以外のものを把握したいのか?</a></h2> <p>パッケージの取得元リポジトリのカテゴリを把握する理由は、Canonical による Ubuntu Pro のサポート範囲が関係しているからだ。</p> <p><code>apt</code> コマンドを使用して、Ubuntu の標準リポジトリから取得される各パッケージは、以下の4つのカテゴリに分類される。 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p> <ul> <li>Main <ul> <li>Ubuntu チームによってサポートされる、フリーソフトウェアを収容するコンポーネントで、 2,300 以上のパッケージを含む。</li> <li>Ubuntu インストール時にデフォルトでインストールされるパッケージの大半はこれ。</li> </ul></li> <li>Restricted <ul> <li>ドライバーなど、利便性の面でデフォルトでインストールされたほうが望ましいものの、プロプライエタリであるソフトウェアを含むコンポーネント。</li> </ul></li> <li>Universe <ul> <li>Ubuntu コミュニティによって管理されている、 Linux 界の多くのオープンソースソフトウェアを収容するコンポーネントで、 23,000 以上のパッケージを含む。</li> </ul></li> <li>Multiverse <ul> <li>フリーソフトの要件を満たさないような、特殊なライセンス要件をもつソフトウェアを収容するコンポーネント。</li> <li>GPU 関連 (CUDA 等) や, VirtualBox などが含まれている。</li> </ul></li> </ul> <p>これらのうち、Restricted と Multiverse リポジトリのパッケージについては、それぞれのソフトの提供元によってのみメンテナンスされ、Canonical によるサポートは行われない。</p> <p>一方で、Main と Universe リポジトリのパッケージについては、セキュリティ修正の提供期間や、電話/オンラインチケットサポート範囲が、 Ubuntu Pro の有無やライセンスの種類ごとに以下のような内訳となっている。 <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p> <blockquote> <div class="table-responsive"><table> <thead> <tr> <th>Security patching</th> <th>Ubuntu LTS</th> <th>Ubuntu Pro (Infra-only)</th> <th>Ubuntu Pro</th> </tr> </thead> <tbody> <tr> <td>Over 2,300 packages in Ubuntu <strong>Main</strong> repository</td> <td>5 years</td> <td>10 years</td> <td>10 years</td> </tr> <tr> <td>Over 23,000 packages in Ubuntu <strong>Universe</strong> repository</td> <td>Best effort</td> <td>Best effort</td> <td>10 years</td> </tr> <tr> <td><a target="_blank" rel="nofollow noopener" href="https://ubuntu.com/support">Optional phone/ticket support</a></td> <td>No</td> <td>Yes</td> <td>Yes</td> </tr> </tbody> </table></div> </blockquote> <p>5年のLTSの期間中、Main リポジトリのパッケージを主に使用する場合、(ライブパッチ等の他のUbuntu Proのサービスが不要であれば)無料の範囲内で十分だ。</p> <p>Main リポジトリのパッケージを主に使用しながら、10年の延長サポートを望む場合は、"Ubuntu Pro (Infra-only)" を契約することをおすすめする。</p> <p>また、Universe リポジトリのパッケージを積極的に活用したい場合は、"Ubuntu Pro" を契約しておくと、より安心だろう。</p> <h2 id="Ubuntu の Universe リポジトリのパッケージの更新基準"><a href="#Ubuntu+%E3%81%AE+Universe+%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E6%9B%B4%E6%96%B0%E5%9F%BA%E6%BA%96">Ubuntu の Universe リポジトリのパッケージの更新基準</a></h2> <p>ちなみに、Universe repository の "Best effort" と "10 years" の違いがいまいちわかりにくいが…</p> <p>例えば、サポート中の Ubuntu 22.04 LTS であっても、 Universe リポジトリの imagemagick パッケージを更新しようとすると、 <code>Ubuntu Pro with 'esm-apps'</code> の有効化が必要と広告が表示される。</p> <pre><code>$ cat /etc/os-release PRETTY_NAME="Ubuntu 22.04.2 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.2 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammy $ $ sudo apt update $ sudo apt install imagemagick $ $ sudo apt upgrade Reading package lists... Done Building dependency tree... Done Reading state information... Done Calculating upgrade... Done Get more security updates through Ubuntu Pro with 'esm-apps' enabled: imagemagick ansible libopenexr25 libmagickcore-6.q16-6-extra libmagickwand-6.q16-6 imagemagick-6.q16 libmagickcore-6.q16-6 imagemagick-6-common Learn more about Ubuntu Pro at https://ubuntu.com/pro 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. $ $ apt-cache policy imagemagick imagemagick: Installed: 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.3 Candidate: 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.3 Version table: *** 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.3 500 500 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages 500 http://archive.ubuntu.com/ubuntu jammy-security/universe amd64 Packages 100 /var/lib/dpkg/status 8:6.9.11.60+dfsg-1.3build2 500 500 http://archive.ubuntu.com/ubuntu jammy/universe amd64 Packages </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://askubuntu.com/questions/1452299/im-getting-the-message-the-following-security-updates-require-ubuntu-pro-with/1453309#1453309">package management - I'm getting the message: "The following security updates require Ubuntu Pro with 'esm-apps' enabled" when updating Ubuntu 22.04 - Ask Ubuntu</a></p> <p>上記の回答によると、 Ubuntu の Universe リポジトリのアップストリームパッチの取り込みは引き続き行っているものの、 Canonical のセキュリティチームによるパッチは Ubuntu Pro の 'esm-apps' に限って行うようになっている。</p> <p>ここでいう「アップストリームパッチ」とはなんぞや?</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/ImageMagick/ImageMagick6/tree/6.9.12-90">ImageMagick/ImageMagick6 at 6.9.12-90</a></p> <p>ImageMagick の元の開発元の GitHub を見れば分かる通り、より高いバージョンのパッケージもメンテナンスリリースされ続けているが、これがそのまま取り込まれているわけではなさそうだ。</p> <p><a target="_blank" rel="nofollow noopener" href="https://packages.debian.org/bullseye/imagemagick">Debian -- bullseye の imagemagick パッケージに関する詳細</a></p> <p>一方で、Ubuntu 22.04 LTS の元となっている Debian 11 bullseye のパッケージを見ると、 2023年7月 現在は</p> <pre><code>imagemagick (8:6.9.11.60+dfsg-1.3+deb11u1) </code></pre> <p>と、 Ubuntu 側の非ESMのバージョンと、似たようなバージョンにとどまっている。</p> <p>すなわち、ココで言う「アップストリーム」とは、 Debian のパッケージ管理の方を指しているのだと思われる。</p> <p>おそらく、Universe リポジトリのパッケージについては以下のようになっているのだろう。</p> <ul> <li>ベースとなっている Debian でリリースされているバージョンは Best effort で LTS へ取り込み</li> <li>それ以外の Canonical 側で当てたセキュリティパッチは Ubuntu Pro のみに提供</li> </ul> <p>だとすれば、Ubuntu Pro の esm-apps を有効にしていなくても、ベースとなった Debian レベルの修正はされると考えてよさそうだ。</p> <h3 id="実際にソースコードを見てみる"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AB%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%A6%8B%E3%81%A6%E3%81%BF%E3%82%8B">実際にソースコードを見てみる</a></h3> <p>実際に、 <a target="_blank" rel="nofollow noopener" href="http://ftp.debian.org/debian/dists/bullseye/main/source/Sources.gz">Debian 11 bullseye</a>, <a target="_blank" rel="nofollow noopener" href="http://archive.ubuntu.com/ubuntu/dists/jammy-security/universe/source/Sources.gz">Ubuntu 22.04 LTS</a>, <a target="_blank" rel="nofollow noopener" href="https://esm.ubuntu.com/apps/ubuntu/dists/jammy-apps-security/main/source/Sources.gz">Ubuntu 22.04 ESM</a> それぞれに於ける2023年7月現在最新版のソースを、 <code>apt source</code> コマンドで入手してみて比べてみると…</p> <div class="table-responsive"><table> <thead> <tr> <th>ディストリビューション</th> <th><a target="_blank" rel="nofollow noopener" href="https://debian-handbook.info/browse/ja-JP/stable/sect.source-package-structure.html">.dsc (Debian Source Control) ファイル</a></th> <th>ソースファイル</th> </tr> </thead> <tbody> <tr> <td>Debian 11 bullseye</td> <td><a target="_blank" rel="nofollow noopener" href="http://ftp.debian.org/debian/pool/main/i/imagemagick/imagemagick_6.9.11.60+dfsg-1.3+deb11u1.dsc">imagemagick_6.9.11.60+dfsg-1.3+deb11u1.dsc</a></td> <td>imagemagick_6.9.11.60+dfsg.orig.tar.xzimagemagick_6.9.11.60+dfsg-1.3+deb11u1.debian.tar.xz</td> </tr> <tr> <td>Ubuntu 22.04 LTS</td> <td><a target="_blank" rel="nofollow noopener" href="http://archive.ubuntu.com/ubuntu/pool/universe/i/imagemagick/imagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3.dsc">imagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3.dsc</a></td> <td>imagemagick_6.9.11.60+dfsg.orig.tar.xzimagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3.debian.tar.xz</td> </tr> <tr> <td>Ubuntu 22.04 esm-apps</td> <td><a target="_blank" rel="nofollow noopener" href="https://esm.ubuntu.com/apps/ubuntu/pool/main/i/imagemagick/imagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3+esm2.dsc">imagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3+esm2.dsc</a> <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></td> <td>imagemagick_6.9.11.60+dfsg.orig.tar.xzimagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3+esm2.debian.tar.xz</td> </tr> </tbody> </table></div> <p>このうち、 <code>imagemagick_6.9.11.60+dfsg.orig.tar.xz</code> についてはハッシュ値まで同じファイルなので、相違点は以下のファイルとなる。</p> <ul> <li><code>imagemagick_6.9.11.60+dfsg-1.3+deb11u1.debian.tar.xz</code></li> <li><code>imagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3.debian.tar.xz</code></li> <li><code>imagemagick_6.9.11.60+dfsg-1.3ubuntu0.22.04.3+esm2.debian.tar.xz</code></li> </ul> <p>これらのアーカイブファイルの中身に対して WinMerge の3方向マージなどを使い、changelog やパッチファイルなどの比較をしてみよう。<br /> すると、いずれも <code>6.9.11.60+dfsg-1.3</code> までのパッチがあたっているのに加えて、それぞれ以下のセキュリティフィックスを含んでいることがわかる。</p> <ul> <li>Debian 11 と Ubuntu 22.04 LTS <ul> <li>CVE-2022-44267</li> <li>CVE-2022-44268</li> </ul></li> <li>Ubuntu 22.04 esm-apps <ul> <li>CVE-2022-44267</li> <li>CVE-2022-44268</li> <li>CVE-2021-3574</li> <li>CVE-2021-3610</li> <li>CVE-2021-4219</li> <li>CVE-2021-20241</li> <li>CVE-2021-20243</li> <li>CVE-2021-20244</li> <li>CVE-2021-20245</li> <li>CVE-2021-20246</li> <li>CVE-2021-20309</li> <li>CVE-2021-20312</li> <li>CVE-2021-20313</li> <li>CVE-2021-39212</li> <li>CVE-2022-1114</li> <li>CVE-2022-28463</li> <li>CVE-2022-32545</li> <li>CVE-2022-32546</li> <li>CVE-2022-32547</li> <li>CVE-2023-1289</li> <li>CVE-2023-1906</li> <li>CVE-2023-3195</li> <li>CVE-2023-3428</li> <li>CVE-2023-34151</li> </ul></li> </ul> <p>前述の、 <code>Debian 11</code> ≒ <code>Ubuntu 22.04 LTS</code> ≦ <code>Ubuntu 22.04 esm-apps</code> だろうという予想は当たっていそうだ。</p> <p>ちなみに、つい先日2023年6月にリリースされたばかりの <a target="_blank" rel="nofollow noopener" href="http://ftp.debian.org/debian/dists/bookworm/main/source/Sources.gz">Debian 12 bookworm</a> では、<a target="_blank" rel="nofollow noopener" href="http://ftp.debian.org/debian/pool/main/i/imagemagick/imagemagick_6.9.11.60+dfsg-1.6.dsc">imagemagick_6.9.11.60+dfsg-1.6.dsc</a> という少し dfsg のバージョンが高いパッケージがリリースされている。<br /> その changelog を見てみると、<code>6.9.11.60+dfsg-1.3</code> までのパッチに加えて、以下のセキュリティフィックスが含まれていた。</p> <ul> <li>Debian 12 <ul> <li>CVE-2022-44267</li> <li>CVE-2022-44268</li> <li>CVE-2021-3574</li> <li>CVE-2021-4219</li> <li>CVE-2021-20241</li> <li>CVE-2021-20243</li> <li>CVE-2021-20244</li> <li>CVE-2021-20245</li> <li>CVE-2021-20246</li> <li>CVE-2021-20309</li> <li>CVE-2021-20312</li> <li>CVE-2021-20313</li> <li>CVE-2021-39212</li> <li>CVE-2022-1114</li> <li>CVE-2022-28463</li> <li>CVE-2022-32545</li> <li>CVE-2022-32546</li> <li>CVE-2022-32547</li> </ul></li> </ul> <p>Ubuntu Pro の esm-apps のほうが、Debian 12 に含まれているパッチに加え、更に多くのセキュリティフィックスを含んでいることがわかる。</p> <p>… Debian 12 bookworm のパッケージでも、ImageMagick 6.9.12系 や ImageMagick 7系へのパッケージの更新はしていないのね。</p> <hr /> <p>参考:</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://gihyo.jp/admin/clip/01/ubuntu-topics/202210/07">「Ubuntu Pro」のワークステーション・データセンター向けベータリリース | gihyo.jp</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://pc.watch.impress.co.jp/docs/column/ubuntu/1512815.html">【Ubuntu日和】【第30回】DebianとUbuntu、CentOSとRHELから学ぶ、Upstreamとの関係 - PC Watch</a></li> </ul> <div class="footnotes" role="doc-endnotes"> <hr /> <ol> <li id="fn:1" role="doc-endnote"> <p><a target="_blank" rel="nofollow noopener" href="https://help.ubuntu.com/community/Repositories">Repositories - Community Help Wiki</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:2" role="doc-endnote"> <p><a target="_blank" rel="nofollow noopener" href="https://ubuntu.com/pro">Ubuntu Pro | Ubuntu</a> <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>ESM向けの配信ページはパスワード保護がかかっているが、Ubuntu Pro を有効すると作成される<a target="_blank" rel="nofollow noopener" href="https://askubuntu.com/a/1454416"><code>/etc/apt/auth.conf.d/</code>内のファイルに記載されたユーザー名(<code>bearer</code>)とパスワードで認証できる</a>。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> advanceboy tag:crieit.net,2005:PublicArticle/17814 2021-12-04T00:20:20+09:00 2021-12-04T00:20:20+09:00 https://crieit.net/posts/sed-0 置換ができない/複数ある場合に sed の終了コード0以外にする <p>本記事は、 <a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2021/shellscript">シェルスクリプト Advent Calendar 2021</a> の 4日目 の記事だ。<br /> そして、 <strong>且つ</strong> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2021/docker">docker Advent Calendar 2021</a> 4日目 の記事でもある。</p> <p>どちらのカレンダーもまだまだスッカスカなので、禁じ手で埋めにかかってしまった。</p> <hr /> <p><a target="_blank" rel="nofollow noopener" href="https://matsuand.github.io/docs.docker.jp.onthefly/docker-hub/official_images/">Docker 公式イメージ</a> などをベースにして、カスタムしてイメージをビルドして使おうとした際、 なるべくなら <code>/etc/apt/apt.conf.d/</code> 等のように、設定用の<strong>ファイルを追加</strong>して、ツール側がいい感じにマージして利用してくれるのが望ましい。<br /> しかし、 場合によってはやむを得ず、既存のファイルを <code>sed</code> コマンドなどで編集せざるを得ないこともあるだろう。</p> <p>カスタムイメージの Dockerfile をビルドする際に、当初は意図通り書き換えられていても、イメージが更新された結果、イメージのリビルド時にファイルの書き換えが意図しない結果となってしまう場合がある。 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p> <p>通常、 <code>sed</code> コマンドは、置換が発生してもしなくても、 終了コード 0 で終了する。<br /> このため、書き換えの成否にかかわらず、 docker build 時にエラーにならないため、コンテナ実行時に初めて置換が意図しない結果だったことに気づくことがある。</p> <p>そこで、<strong><code>sed</code> コマンドの書き換えで適切なパターンが見つからなかった</strong>場合に 0以外の終了コードを返し、<strong>ビルド時にエラー</strong>とする方法を考える。</p> <p>以下、 <code>sed</code> は GNU sed を前提とし、 "行頭の foo" を BARfooBAR に置き換える場合の例。</p> <h2 id="ひとつもヒットしなかったら終了コード16 のエラー"><a href="#%E3%81%B2%E3%81%A8%E3%81%A4%E3%82%82%E3%83%92%E3%83%83%E3%83%88%E3%81%97%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%82%89%E7%B5%82%E4%BA%86%E3%82%B3%E3%83%BC%E3%83%8916+%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC">ひとつもヒットしなかったら終了コード16 のエラー</a></h2> <p>まずは、 書き換えるパターンが見つからなかった場合に、エラーコードを返す方法。</p> <pre><code class="bash">sed -e '/^foo/{s//BAR\0BAR/;h};$!b;p;x;/./Q;Q16' </code></pre> <p>参考: https://stackoverflow.com/a/15966279</p> <p>ざっとコマンドの流れを解説すると、以下のようになる。</p> <ol> <li>まず、 <a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#Regexp-Addresses">正規表現アドレス</a> で置換する行を選択する。</li> <li>ブロック <code>{}</code> を用いて、正規表現に一致する行について以下を実行する。 <ol> <li><a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#The-_0022s_0022-Command">s コマンド</a> で、<a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#regexp-extensions">後方参照</a>を使って FOObarBAR に置換する。<br /> <a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#Regexp-Addresses">空の正規表現 '//'は最後の正規表現のマッチを繰り返</a>すので、正規表現アドレスでマッチした "行頭の foo" が置き換えられる。</li> <li><a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#sed-commands-list">h コマンド</a> で、パターンスペース の内容をホールドスペース (sed 内のクリップボードみたいなもの) にコピーする。</li> </ol></li> <li><code>$!b</code> の部分は、最終行でなければ次のサイクルに移動する。 すなわち、以降のコマンドは<strong>最終行でのみ実行</strong>される。</li> <li><a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#The-_0022s_0022-Command">p コマンド</a> でパターンスペースの内容を出力にプリントする。</li> <li><a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#sed-commands-list">x コマンド</a> でパターンスペースの内容とホールドスペースをスワップする。<br /> <strong>パターンスペースの内容は結果的に</strong> 、1度でも最初の正規表現がヒットすればその文字列に、 最後まで一度も正規表現がヒットしなければ空っぽになる。</li> <li>最後、パターンスペースの内容が空でない (即ち、1度以上正規表現がヒットした) なら、 終了コード 0 で終了し、 そうでなければ 16 で終了する。</li> </ol> <h2 id="ヒットしなければ終了コード16, 2つ以上ヒットしたら終了コード32 のエラー"><a href="#%E3%83%92%E3%83%83%E3%83%88%E3%81%97%E3%81%AA%E3%81%91%E3%82%8C%E3%81%B0%E7%B5%82%E4%BA%86%E3%82%B3%E3%83%BC%E3%83%8916%2C+2%E3%81%A4%E4%BB%A5%E4%B8%8A%E3%83%92%E3%83%83%E3%83%88%E3%81%97%E3%81%9F%E3%82%89%E7%B5%82%E4%BA%86%E3%82%B3%E3%83%BC%E3%83%8932+%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC">ヒットしなければ終了コード16, 2つ以上ヒットしたら終了コード32 のエラー</a></h2> <p>さらに一歩踏み込んで、 書き換えるパターンが見つからない場合と、 2つ以上見つかってしまった場合<strong>両方</strong>で、エラーにする方法。</p> <pre><code class="bash">sed -e '/^foo/{s//BAR\0BAR/;x;/./Q32;g};$!b;p;x;/./Q;Q16' </code></pre> <p>基本的な動きは、ひとつもヒットしなかったパターンと同じだ。<br /> ただ、ブロック <code>{}</code> 内部のコマンドを以下のように変更して、複数ヒットした場合にエラーで終了している。</p> <ol> <li>まず、 正規表現アドレス で置換する行を選択する。</li> <li>ブロック <code>{}</code> を用いて、正規表現に一致する行について以下を実行する。 <ol> <li>s コマンド で、後方参照を使って FOObarBAR に置換する。</li> <li><a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#sed-commands-list">x コマンド</a> でパターンスペースの内容とホールドスペースをスワップする。<br /> <strong>パターンスペースの内容は結果的に</strong> 、以前の行でも正規表現がヒットすればその文字列に、 最後まで一度も正規表現がヒットしなければ空っぽになる。</li> <li>パターンスペースの内容が空でない (即ち、正規表現のヒットが2回目) なら、 終了コード 32 で<strong>エラー終了</strong>し、 そうでなければそのまま次へ。</li> <li><a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#sed-commands-list">g コマンド</a> でホールドスペースの内容をパターンスペースにコピーして戻す</li> </ol></li> <li>以降は「ひとつもヒットしなかったら…」と同じ</li> </ol> <p>なお、このコマンドは、 2つヒットした時点で出力が止まる。</p> <h2 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h2> <p>モダンなプログラミング言語が軒並み型推論でコンパイル時にエラーとするように、 Dockerfile もビルド時にエラーにしてしまおう。</p> <div class="footnotes" role="doc-endnotes"> <hr /> <ol> <li id="fn:1" role="doc-endnote"> <p>カスタムイメージの Dockerfile ベースイメージを選択する際、基本的にはタグである程度絞っておくべきだが、それはさておき。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> advanceboy tag:crieit.net,2005:PublicArticle/17813 2021-12-04T00:00:04+09:00 2021-12-04T00:00:04+09:00 https://crieit.net/posts/sed-i-r-e-unmatched sed の ブロック {} 内で i, r, e コマンドを使うと "unmatched `{'" とエラーになる <p>本記事は、 <a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2021/shellscript">シェルスクリプト Advent Calendar 2021</a> の 3日目 の記事だ。<br /> 3日目が終わりそうになっても誰も書きそうにないので、最近 <code>sed</code> コマンドで ブロック <code>{}</code> を使っていたら、 "unmatched `{" というエラーにハマったので、そのメモ。</p> <hr /> <p><code>target.txt</code>:</p> <pre><code class="plain">foo bar foo bar foo </code></pre> <p><code>insert.txt</code>:</p> <pre><code class="plain">*** </code></pre> <p>上記のような、2つのファイルがあったとする。</p> <p><code>target.txt</code> ファイルに対して、 <a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#Regexp-Addresses">正規表現アドレス</a> で <code>bar</code> から始まる行を選択し、 その後ろに <a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#sed-commands-list">r コマンド</a> <code>insert.txt</code> のファイルの中身を挿入する。</p> <p>するとこんな結果になる。</p> <pre><code>$ sed -e '/^bar/rinsert.txt' target.txt foo bar *** foo bar *** foo </code></pre> <p>では、アドレス指定の後ろにブロック <code>{}</code> を追加し、以下のように bar が2回以上ヒットしたらエラーコード出して終了するようにしてみる。</p> <pre><code>$ sed -e '/^bar/{rinsert.txt;x;/./Q129;g}' target.txt sed: -e expression #1, char 0: unmatched `{' $ echo $? 1 </code></pre> <p>はい、別のエラーで失敗した。<br /> ちゃんと <code>{</code> と <code>}</code> の数はマッチしているのに……</p> <p>これは、 i, r, e などのコマンドは、 コマンドの終了区切りに改行が必須となっていて、 セミコロン (<code>;</code>) などで区切ろうとしても、 その文字もコマンドのオプションとして渡されてしまうためだ。 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p> <p>これを回避する場合は、 コマンドの後ろに改行を入れるか、 -e を使ってコマンド区切る必要がある。</p> <pre><code>$ sed -e '/^bar/{rinsert.txt' -e 'x;/./Q129;g}' target.txt foo *** bar foo *** $ echo $? 129 </code></pre> <pre><code>$ sed -e '/^bar/{rinsert.txt' -e 'x;/./Q129;g}' <<EOF > foo > bar > foo > foo > EOF foo bar *** foo foo $ echo $? 0 </code></pre> <p>様々な区切り文字でつかわれるので忘れがちだけど、セミコロンをファイル名にすることだって、できるもんな。<br /> そう考えれば納得。</p> <p>ちなみに、同様の振る舞いをするコマンド ("Commands Requiring a newline") は、以下の通り。</p> <ul> <li><code>a,c,i (append/change/insert)</code></li> <li><code># (comment)</code></li> <li><code>r,R,w,W (reading and writing files)</code></li> <li><code>e (command execution)</code></li> <li><code>s///[we] (substitute with e or w flags)</code></li> </ul> <div class="footnotes" role="doc-endnotes"> <hr /> <ol> <li id="fn:1" role="doc-endnote"> <p><a target="_blank" rel="nofollow noopener" href="https://superuser.com/questions/456246/sed-weirdness-unmatched">command line - sed weirdness, unmatched { - Super User</a><br /> <a target="_blank" rel="nofollow noopener" href="https://www.gnu.org/software/sed/manual/sed.html#Commands-Requiring-a-newline">sed, a stream editor #3.8.1 Commands Requiring a newline - GNU sed online manual</a><br /> <a target="_blank" rel="nofollow noopener" href="https://kuma35.github.io/sed47doc-jp/sed.html#Commands-Requiring-a-newline">ストリームエディタ sed #3.8.1 sedコマンドに改行が必要な時 - GNU sed オンラインマニュアル</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> advanceboy