golangのldflagsに引っかかった

golangにはビルド時のリンク関連の制御のldflagsオプションがある。

ldflagsを利用するとビルド時に変数の埋め込みなどが出来るのだが、これで引っかかった。

やろうとしていたことは次の様なコード。

環境変数の代わりにldflagsで変数埋め込みを行い、実行する場合は環境変数の設定を無くしたかった。

package main

import (
    "fmt"
    "os"
)

var envtest string = os.Getenv("ENVTEST")

func main() {
    fmt.Println(envtest)
}

実際にこれを go build -ldflags "-X main.envtest=goodnight" ./main.goの様にldflagsを使って変数埋め込み行いビルドした。

環境変数を設定していない状態で、生成したバイナリを実行するとこの様な結果になる。

$ ./main

何も表示されなかった。つまりenvtestENVTEST環境変数をロードしにいっていることになる。 とはいえstringsコマンドをかけてやると普通に出てくる。どういうことなの。

$ strings ./main | grep goodnight
goodnight

え〜と思い代入文を外して実験した。

package main

import (
    "fmt"
)

var envtest string

func main() {
    fmt.Println(envtest)
}

これで全く同じ方法でビルドした。

$ ./main 
goodnight

埋め込まれてる!!!!!!!!!!

ネタバラシ

ドキュメントを見たところちゃんと書いてあった

-X importpath.name=value

Set the value of the string variable in importpath named name to value. This is only effective if the variable is declared in the source code either uninitialized or initialized to a constant string expression. -X will not work if the initializer makes a function call or refers to other variables. Note that before Go 1.5 this option took two separate arguments.

「This is only effective if the variable is declared in the source code either uninitialized or initialized to a constant string expression.」とのことだったので、ちゃんとドキュメントは読もうな.....

ちなみに開発環境では環境変数使って実行環境では使いたくないときはどうするべきなんだろう。。。 initで空文字列かどうかを判定して、空文字列だったら環境変数から取るとかにするのがいいのかな。

WEB+DB PRESS vol.117 「Perl Hackers Hub」 第62回「Perl歴史散策」に寄稿しました

というわけで一度投稿してみたかったエントリです!!!

2020年6月24日(水)に発売される WEB+DB Press Vol.117Perl Hackers Hub に「Perl歴史散策」を書かせていただきました!!みんな買ってくれ!!!!!!

沖縄だと多分無いですが大手書店では6月16日からテスト販売があるそうです!!!自分の近所の書店が大手書店だと思われる人は買ってください!!!

ちなみに外出するのはな...という皆様のために電子書籍でも販売しています!!!

f:id:anatofuz:20200608185623j:plain

気になる内容

気になる内容ですが

  • Perl1.0から現在のPerl5.32に至るまでの代表的なバージョン(1,2,3,4,5)の解説
  • Perl6/Rakuの歴史と現在
  • 最新Perl5情報

...と、個人的な趣味MAXみたいな内容でお送りしています。(別に僕が書いたコードの話ではない^q^)

今からPerlを始めてみよう、 Perlってどんな言語なのかちょっと知りたいというPerlビギナーの方はもちろんのこと。 PerlがどのようにC言語で実装されているか気になる方まで、 Perlに興味がある方なら面白いかなと思う内容だと思います!

所感

Perl Hackers Hubは第一線のバリバリPerlエンジニアの方が多く執筆されており、業務で使っていない以前にそもそも業務をしていない学生だったので(!!!!????)となりましたが、せっかくチャンスを頂いたので書かせていただきました!

執筆はmarkdownベースの文法なファイルをガンガン書いていって、そのファイル上で校正をしていただける運用でした。こういう執筆作業でもgitみたいなバージョン管理使っているのか~!と驚きました。

実際に書籍の文章が我々の手元に届くまでにはかなりの労力がかかっている事を身を以て体験できました。徹底的に編集部の皆様に文章の校正をしていただいて、人々に伝わりやすい文章を作る難しさを痛感しました。今までと異なる目線で技術書などの書籍を見ることが出来るようになったと思います。

あとは犯罪以外で自分の名前が書かれた書籍が全国に頒布されるのは嬉しいですね。しかも憧れのWEB+DB PRESSなので感慨もひとしおです。

謝辞

お誘い頂いたid:papixさんや、 的確な指摘で文章のリファクタリングをしていただいた技術評論社の稲尾さん、 英語や技術的内容のレビューをしていただいた監修の牧さんを始めとする校正などに関わってくださった皆さんに御礼申し上げます。

もう一度

2020年6月24日(水)に発売される WEB+DB Press Vol.117Perl Hackers Hub に「Perl歴史散策」を書かせていただきました!!よろしくお願いします!!!!!!

docker-composeのvolumeを途中から移動させたら上手く行かなかった

dockerでDBを使っているとデータを永続化させるためにローカルの特定のディレクトリなどをマウントさせる事が多い。運用しているdocker-composeを次のような感じで書いていた。

    image: mongo:3.6
    restart: unless-stopped
    volumes:
      - mongo_configdb:/data/configdb
      - mongo_db:/data/db
    restart: always

volumes:
  mongo_db:

まぁこれで全然問題なかったのだけど、volumesで特にパスを指定していなかった。

そのため /var/lib/docker/volumes以下に動かしているコンテナ名でディレクトリが作成されていて、そのディレクトリがmountされている感じで動いていた。

/var/lib/docker以下に保存していると、なにかdocker pruneとかしてしまうとファイルが消えてしまう可能性があるらしい。それは避けたいのでローカルディレクトリ上に保存しようとした。

ファイルの移動

どうも調べていると一度volumeとimageを消さないと移動はできないらしい。

mvで移動しても良かったが、最初にbackupがてらcpでディレクトリごとコピーした。 今思い返せばcpじゃなくてrsync使ってたほうが良かったのかもしれない。

$cp -r /var/lib/docker/volumes /home/anatofuz/backup

この時点で/var/lib/docker/volumesls -lしたら全てroot:rootになっていたので油断していた。

ホームディレクトリに移したのでanatofuz:dockerくらいにchownしていた。

docker-composeへの追記

docker-composeにカレントディレクトリ以下にデータを保存させたかったので次の様な感じで追記をした。

volumes:
  growi_data:
      driver_opts:
        type: none
        device: /home/anatofuz/growi/data/growi_growi_data
        o: bind
  mongo_configdb:
      driver_opts:
        type: none
        device: /home/anatofuz/growi/data/growi_mongo_configdb
        o: bind
  mongo_db:
      driver_opts:
        type: none
        device: /home/anatofuz/growi/data/growi_mongo_db
        o: bind
  es_data:
      driver_opts:
        type: none
        device: /home/anatofuz/growi/data/growi_es_data
        o: bind

docer-compose stopしてdocker-compose rmしたあとにdocer-compose buildしようとしたら所、 volumeを消せと言われたのでポチポチvolumeを消していった。

ビルド時のpermission

ビルドするとこんな感じになる

Successfully built e3a815260e49
Successfully tagged growi_elasticsearch:latest
Building app
Traceback (most recent call last):
  File "bin/docker-compose", line 6, in <module>
  File "compose/cli/main.py", line 72, in main
  File "compose/cli/main.py", line 128, in perform_command
  File "compose/cli/main.py", line 304, in build
  File "compose/project.py", line 397, in build
  File "compose/project.py", line 380, in build_service
  File "compose/service.py", line 1104, in build
  File "site-packages/docker/api/build.py", line 160, in build
  File "site-packages/docker/utils/build.py", line 30, in tar
  File "site-packages/docker/utils/build.py", line 49, in exclude_paths
  File "site-packages/docker/utils/build.py", line 214, in rec_walk
  File "site-packages/docker/utils/build.py", line 214, in rec_walk
  File "site-packages/docker/utils/build.py", line 214, in rec_walk
  File "site-packages/docker/utils/build.py", line 184, in rec_walk
PermissionError: [Errno 13] Permission denied: '/home/anatofuz/growi/data/growi_mariadb_data/mysql'
[395570] Failed to execute script docker-compose

そもそもappは次の様にdocker-compose.ymlを書いている。

  app:
    build:
      context: .
      dockerfile: ./Dockerfile
    ports:
      - 3000:3000
    links:
      - mongo:mongo
      - elasticsearch:elasticsearch
    depends_on:
      - mongo
      - elasticsearch
    environment:
      - MONGO_URI=mongodb://mongo:27017/growi
      - ELASTICSEARCH_URI=http://elasticsearch:9200/growi
      # - FILE_UPLOAD=mongodb   # activate this line if you use MongoDB GridFS rather than AWS
      - FILE_UPLOAD=local     # activate this line if you use local storage of server rather than AWS
      - MATHJAX=1             # activate this line if you want to use MathJax

    command: "dockerize
              -wait tcp://mongo:27017
              -wait tcp://elasticsearch:9200
              -timeout 60s
              npm run server:prod"
    restart: unless-stopped
    volumes:
      - growi_data:/data
    restart: always

mariadbのdataにアクセスする必要は無いと思うのだけれど、なぜかこのパーミッションで怒られている。dataディレクトリの中でコンテナごとにディレクトリを切っていたけれど、この運用が駄目だったのかもしれない。

root:rootなのが怪しいのかと思いchownで変更した。

sudo chown -R anatofuz:docker data/

こうするとbuildは通った。

起動するとDBの中身を読んでくれない問題

build通ったので喜びながらdocker-compose upしたがアプリケーションの初期設定画面が表示されてしまっていた。DBがデータを読んでなさそうな気配はあったが、docker-composeのlogを見る限りPermission関連で怒られている気配はない。

色々見た結果、もともと/var/lib/docker/volumesの中にあったgrowi_growi_dataの様なコンテナ名のディレクトリをプロジェクトのdataディレクトリにコピっていた。このコピー先の/home/anatofuz/growi/data/growi_growi_dataをdocker-composeでvolumeとして指定していたのだけど、これが駄目だった。

実際には/home/anatofuz/growi/data/growi_growi_dataの中に_dataディレクトリがあり、このディレクトリの中に実際のデータが存在している。つまりこの_dataまで指定しなければならない。

今回はdocker-composeを操作せずに、各コンテナ名ディレクトリの_datamv ..して解決した。正直この解決方法が正しいかは微妙である。

とはいえ

もう一度docer-compose buildすると、相変わらず

PermissionError: [Errno 13] Permission denied: '/home/anatofuz/growi/data/growi_mariadb_data/mysql'
[395570] Failed to execute script docker-compose

と怒られてしまう。

$ ls -l data/
total 56
drwxr-xr-x 3   anatofuz docker  4096 Jun  4 23:18 growi_es_data
drwxr-xr-x 3   anatofuz docker  4096 Jun  4 23:18 growi_growi_data
drwxr-xr-x 4   anatofuz docker  4096 Jun  4 23:18 growi_https-portal_data
drwxr-xr-x 5        999 docker  4096 Jun  4 23:23 growi_mariadb_data
drwxr-xr-x 2        999 docker  4096 Apr 30 02:20 growi_mongo_configdb
drwxr-xr-x 4        999 docker  4096 Jun  5 08:53 growi_mongo_db
-rw------- 1   anatofuz docker 65536 Jun  4 23:18 metadata.db

どうもdocker-compose runした瞬間にmariadbなどのDB系のユーザーが999で実行されてしまい、anatofuzで起動しているgrowi(app)からは参照できないらしい。

実際にps auxするとmariadbが999ユーザーで実行されていた。/var/lib/docker/volumesで実行していたときは999ではなくrootで動いていた気がするので、なぜこうなったかはよく解っていない。

所感

/var/lib/docker/volumesに結局データが保存されるのであれば、別にわざわざ違うディレクトリをマウントしようと頑張る必要はなかったのかもしれない。少なくともdocker-compose buildがスムーズに行かなくなってしまった。

dockerのvolumeを適当なディレクトリに保存して永続化させるよりは、mongodbやmysqlなどのコンテナにattachするなりしてバックアップを取っていくほうが無難であるかなという気がした。今の環境を元に戻すかは怪しいところ。戻せたら良いんだけど…

全体的にdockerよく解っていないことが露呈しているので、なぜこうなったか検討がつく方がいれば教えていただけると嬉しいです

追記 (2020/06/08)

'/home/anatofuz/growi/data/以下においていた999ユーザーで実行されてしまうDB系のファイル群を '/home/anatofuz/db/dataの様なトップディレクトリが異なるディレクトリ内に移動させてvolumeを作り直したところ、docker-compose build時のパーミッション問題は出なくなった。

おそらくappのビルド時にカレントディレクトリをマウントしていて、カレントディレクトリ以下のファイルのパーミッションは全て確認する挙動のような気がする。

yaml, toml, json, hclの相互変換ツールsclevine/yjの紹介

yjっていうコマンド名多すぎ問題がある気がしますが、YAML, TOML, JSON, HCLの相互変換をしてくれるツールの紹介です。

tomlを使う必要があったのですが、いまいち形式になれてないのでjsonで見たいと思い探していました。

github.com

プルリクとissueが溜まっている感は少しあるので、別のコマンドを探すのが良いかもしれません。

インストール方法

brewとかで配布されているわけではないので、素直にgo getします。

$go get github.com/sclevine/yj

使い方

$yj --helpとすると雰囲気がつかめます

$ yj --help
Usage: yj [-][ytjcrneikh]

Convert between YAML, TOML, JSON, and HCL.

-x[x]  Convert using stdin. Valid options:
          -yj, -y = YAML to JSON (default)
          -yy     = YAML to YAML
          -yt     = YAML to TOML
          -yc     = YAML to HCL
          -tj, -t = TOML to JSON
          -ty     = TOML to YAML
          -tt     = TOML to TOML
          -tc     = TOML to HCL
          -jj     = JSON to JSON
          -jy, -r = JSON to YAML
          -jt     = JSON to TOML
          -jc     = JSON to HCL
          -cy     = HCL to YAML
          -ct     = HCL to TOML
          -cj, -c = HCL to JSON
          -cc     = HCL to HCL
-n     Do not covert inf, -inf, and NaN to/from strings (YAML in/out only)
-e     Escape HTML (JSON out only)
-i     Indent output (JSON or TOML out only)
-k     Attempt to parse keys as objects or numbers types (YAML out only)
-h     Show this help message

Error: invalid flags specified: l p

特筆すべきはこのコマンドは入力を標準入力で受け取ります。

つまり yj hoge.yml みたいにはできません。

この点を踏まえて実際に使ってみます。今回はtoml -> jsonを試してみます。

例題として使うtomlはこのサンプルです。

https://github.com/toml-lang/toml/blob/master/tests/example.toml

さてこれをsample.tomlで保存し、リダイレクトを使って実行します。

$ yj -tj < example.toml
{"clients":{"data":[["gamma","delta"],[1,2]],"hosts":["alpha","omega"]},"database":{"connection_max":5000,"enabled":true,"ports":[8001,8001,8002],"server":"192.168.1.1"},"owner":{"bio":"GitHub Cofounder & CEO\nLikes tater tots and beer.","dob":"1979-05-27T07:32:00Z","name":"Tom Preston-Werner","organization":"GitHub"},"products":[{"name":"Hammer","sku":738594937},{"color":"gray","name":"Nail","sku":284758393}],"servers":{"alpha":{"dc":"eqdc10","ip":"10.0.0.1"},"beta":{"country":"中国","dc":"eqdc10","ip":"10.0.0.2"}},"title":"TOML Example"}

いい感じですね。 さらにjq をパイプでつなげると見やすくなります。

$yj -tj < example.toml | jq .
{
  "clients": {
    "data": [
      [
        "gamma",
        "delta"
      ],
      [
        1,
        2
      ]
    ],
    "hosts": [
      "alpha",
      "omega"
    ]
  },
  "database": {
    "connection_max": 5000,
    "enabled": true,
    "ports": [
      8001,
      8001,
      8002
    ],
    "server": "192.168.1.1"
  },
  "owner": {
    "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.",
    "dob": "1979-05-27T07:32:00Z",
    "name": "Tom Preston-Werner",
    "organization": "GitHub"
  },
  "products": [
    {
      "name": "Hammer",
      "sku": 738594937
    },
    {
      "color": "gray",
      "name": "Nail",
      "sku": 284758393
    }
  ],
  "servers": {
    "alpha": {
      "dc": "eqdc10",
      "ip": "10.0.0.1"
    },
    "beta": {
      "country": "中国",
      "dc": "eqdc10",
      "ip": "10.0.0.2"
    }
  },
  "title": "TOML Example"
}

個人サイトをGitHubPagesベースに変えた

ということで個人サイトこと anatofuz.net をGitHubPagesベースかつhttpsに変更しました。

anatofuz.net

以前までの個人サイト

もともとこの個人サイトは、沖縄でGCPUGのイベントがあった際にLT駆動開発としてGoogle Domainドメインを買って作成したもの。なので当時のLTの資料は静的なHTMLにしてサイトにあげていたりした。

以前のサイトは資料にもある通り、Google Cloud Storageに置いたHTMLにDNSを対応させて表示させていた。Google Cloud Storageはトップドメインの表示に対応していなかったので、www.anatofuz.netのみで配信していた。

このときはhugoを使ってサイトを構築していたのだけれど、このサイトを作成したときのソースコードが消失しており、現状このサイトを更新しようとするとHTMLを生で編集する必要があった。さらにGoogle Cloud StorageのCLIPythonで書かれており、環境構築や使い勝手の面でよろしくないというとこがあった。

さらにGoogle Cloud Storageの場合httpsに対応していないので、Cloud Flareを中継してhttpsにするというクソ面倒な構成だった。

qiita.com (当時の構築をまとめたQiita)

GCPの無料枠を使い切ってしまったのか、最近毎月30円くらい請求されていて微妙にめんどうだったこともあるので、今回を機にGCPを落とす構成で作り直すことにした。

今回の変更

というわけで前回の反省を踏まえて

という3要素を全て満たすGitHubPagesでエイヤッと作ることにした。

hugoを使った構築

静的html作成ツールは色々あるが、個人的にgolangで書かれたhugoを気に入っているので今回もそれを採用した。 CSSjavascriptはあまり書きたくないので、雑にテーマをhugo公式から検索し aafuというテーマを選択した。

github.com

このテーマをそのまま使うでも良かったのだけれど

  • ブログは個人サイトではなくはてなブログへのリンクにする
  • リンク集などのアイコンと箇条書きを増やす
  • フォントを変更する
  • 改行コードを統一する

などの欲求があったので、これをforkしてパッチをあてたものを使った。

リポジトリ構成とデプロイ

hugoを使ったGitHub pagesへのデプロイは、なんやかんや公式のチュートリアルが一番しっかりしているので、それを見ながらやった。

gohugo.io

やり方をかいつまんでみると

  1. github pagesとして使うリポジトリと、hugoプロジェクトのリポジトリをそれぞれ作る
  2. hugoプロジェクトリポジトリの publicディレクトリに、 github pagesのリポジトリをsub moduleとして追加する
  3. デプロイスクリプトをコピってあとはそれを実行

みたいな感じだった。極めて楽ですね。

GitHubの2段階認証を有効化している関係で、httpではpushできないという罠があったので、適宜hugoプロジェクトの.git/module以下でsub moduleを指定している箇所をsshに変更するなどもした。

まぁデプロイスクリプトがあるけれど、いちいち手でやるのしんどいのでGitHub Actionに仕込みたいですねという気もする。

Google Cloud Storageの削除

プロジェクごと削除すると、プロジェクトで利用していたSotrageとかが一括で消えて便利。 間違えて消してしまった時様に、リカバリが出来る期間が一応あるらしい。

今回はリカバリする必要がないので、そのメールは無視して潔くプロジェクトごと消した。

ドメイン周り

ドメイン周りの変更はこのサイトがドンピシャだった。

dev.to

まずDNSのネームサーバーをGoogle Domainのものに変更した。 そのあとトップドメインgithub pagesにしたいのでカスタムレコードの @に以下のIPを追加した。

  • 185.199.111.153
  • 185.199.110.153
  • 185.199.109.153
  • 185.199.108.153

f:id:anatofuz:20200518173349j:plain

次に以前のwww.anatofuz.netにアクセスにきたものをanatofuz.netにリダイレクトさせたい。 これは合成レコードの設定をよしなにすれば出来るらしいので、設定してみた。

f:id:anatofuz:20200518173445j:plain

ここまでできたら次はCLoud Flare側のDNS関係の設定を削除し、GitHubリポジトリ設定から、転送先のURLをanatofuz.netに設定とhttpsへの対応ボタンを押して構築完了となった。

所感

やる気になれば手早く終わりましたね....。 Cloud Storageのものは、CLIで取得するのがめんどうだったので、 wget -rで雑に必要そうなものだけ抜き取るという運用でした。

まぁ個人サイトあるとたまに便利なので、こんな感じでデプロイとか管理がしやすい状況に保ちたいですね。

growiとhackmd(codimd)をhttpsで配信しながらhackmd連携を使う

研究室のwikiとして最近growiを使っている。

growiの構築には公式が出しているdocker-composeを使うのが便利で、研究室のwikiもこれを使って運用している。 さらにgrowiにはhackmdのOSSバージョンであるCodiMDを呼び出して使える機能があり、これも公式のdocker-composeをいい感じに編集することで実現できる。

今まではこれらをdocker-composeでlocalhostの3000番と3100番くらいで動かしておいて、dockerでなくaptで入れたsystemdで動かしているnginxでリバースプロキシしていた。 httpオンリー接続で元気に動かす運用だった。 とはいえ最近のインターネットはhttps通信をしたがるムーブがあるので、growiとhackmdをそれぞれhttpsで動かす方針でやってみた。

httpsでの配信

今回も公式のdocker-composeにある通り、httpsでの設定例を見て構築する。 公式がhttps配信のやり方を書いていて便利。

これはSteveLTN/https-portalのプロジェクトのDocker Imageを利用している。 github.com

https-portalはdocker-composeにドメインとリバースプロキシ先を書いておくと、よしなにLets'Encryptから証明書を持ってきて配置してくれる上にnginxでリバースプロキシしてくれる便利なツール。 READMEとかを見た感じ証明書の更新とかも勝手にやってくれるらしい。良いですね。

というわけでdocker-compose.override.yml に次くらいのものを書いておく

  https-portal:
    image: steveltn/https-portal:1
    ports:
      - '80:80'
      - '443:443'
    links:
      - app:app
    environment:
      DOMAINS: 'growi.cr.ie.u-ryukyu.ac.jp -> http://app:3000, hackmd.cr.ie.u-ryukyu.ac.jp -> http://hackmd:3000'
      STAGE: 'production'
      FORCE_RENEW: 'false'
      WEBSOCKET: 'true'
      CLIENT_MAX_BODY_SIZE: 0
    restart: unless-stopped
    volumes:
      - https-portal_data:/var/lib/https-portal

volumes:
  mariadb_data:
  https-portal_data:

ここまで書いてバシッと起動するとhackmdとgrowiがそれぞれhttpsで配信される。

hackmd連携の対応

とはいえスムーズに進まないのが人生で(?) growiのhackmd連携をしようとすると301のエラーが発生する。

Error: Error: Request failed with status code 301

なんでこうなるかはhttpsではなくhttpのhackmdにcurlするとわかる。

<head><title>301 Moved Permanently</title></head>

SteveLTN/https-portalはデフォルトの設定でhttpにアクセスに来たものを301でhttpsの方に引き渡している。 通常では問題ないのだけれど、growiのhackmd連携はwindow.postMessage を利用して行われている。

つまりwindow.postMessageでhttpアクセスに行ったところ301が出てしまって破滅しているため表示されないということになる。 この問題を回避するには、httpsとhttpを共存させれば良いということになる。

幸いSteveLTN/https-portalはnginxの設定をerbで書けば上書きできるらしいので、301ではなく直接リバースプロキシする様に設定を書き換える。

server {
    listen       80;
    server_name  <%= domain.name %>;


    location / {
              proxy_redirect                      off;
              proxy_set_header Host               $host;
              proxy_set_header X-Real-IP          $remote_addr;
              proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
              proxy_read_timeout          1m;
              proxy_connect_timeout       1m;
              proxy_pass  <%= domain.upstream %>;
    }

    # This helper sets up the path for the ACME server to verify that you own the site
    # Defaults to /.well-known/acme-challenge/
    <%= acme_challenge_location %>
}

ここで設定したドメイン名は <%= domain.name %> で、 リバースプロキシ先は <% domain.upstream > でそれぞれ設定出来る。

これをdocker-compose.override.ymlに以下のように書き加える。

    volumes:
      - https-portal_data:/var/lib/https-portal
      - ./hackmd.cr.ie.u-ryukyu.ac.jp.conf.erb:/var/lib/nginx-conf/hackmd.cr.ie.u-ryukyu.ac.jp.conf.erb:ro

ここまでしてdocker-compose upしなおすといい感じにhackmd連携が使える。めでたしめでたし。

コマンド一発でファイルをゼロクリアする

シェルスクリプトの講義のTAが明日あるので、入門unixシェルプログラミングを見ていたところ面白いコマンドがあったので。

ファイルを消さずにゼロクリアするコマンドの紹介。

>file
: >file

このいずれかの方法で出来るらしい。 >fileの場合は虚無をfileにリダイレクトしているのでゼロクリアされる。 : >file の場合は、 何もしないコマンドである: の実行結果がfileにリダイレクトされるので0クリアされる。

ちなみにzshで上書き防止をしていた場合は

: >| file

とかするといい感じにゼロクリアされる。