2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

なでしこ3で簡単なcatコマンドを作る

Last updated at Posted at 2021-12-05

なでしこ3 に入門しています。

$ cnako3 -v    
3.2.30

新しくプログラミング言語を覚えるとき、入出力や反復などの基本的な機能の下調べも兼ねて、最近はよく cat コマンドを書いています。なでしこでもやってみます。

ちなみに、なでしこ3は Node.js ベースなので Linux でも使えます。この記事も Docker コンテナ内で動かしながら書いています。

標準出力に出力する

まずは基本ということで。

「こんにちは」と表示。

これは簡単。チュートリアルの最初に出てきますね。

標準入力から読む

マニュアルで探して 標準入力取得時 という命令を見つけました。

使ってみます。

# cat1.nako3

標準入力取得時には(行)
    行を表示
ここまで

動かしてみます。

$ cat cat1.nako3 | cnako3 cat1.nako3
標準入力取得時には(行)
標準入力取得時には(行)
    行を表示
    行を表示
ここまで
ここまで
$

各行が2回表示されていますが、これはインストールしたなでしこに含まれている src/plugin_node.js を修正して createInterface のオプションとして terminal: false を指定することで解消できました。

$ cat cat1.nako3 | cnako3 cat1.nako3 | cat
標準入力取得時には(行)
    行を表示
ここまで
$ 

参考:

はい、これで cat コマンドっぽいものができました。簡単ですね。


さすがにこれだけだとさびしいので、もうちょっとあれこれやってみます。

末尾に改行を付けない版の「表示」が使いたい

<追記 2022-06-05>
v3.2.34 で「継続表示」命令が追加されたようです。

<追記ここまで>

なでしこ v1 には「継続表示」という命令があり、末尾に改行を付けずに出力できるようなのですが、v3 では用意されていないようです(v3.2.30 時点)。

cat コマンドの基本の動作としては入力の内容を変えずにそのまま出力したいのですが、「表示」命令だと末尾に改行がない入力が来た場合に内容が変わってしまいます。

そこで、「JSメソッド実行」という命令を利用して「継続表示」関数を作ってみました。

●(値を)継続表示とは
    「process.stdout」の「write」を["{値}"]でJSメソッド実行
ここまで

123を継続表示
「継続表示の」を継続表示
「テスト」を継続表示
「です。{LF}」を継続表示
$ cnako3 sample_keizoku_hyouji.nako3
123継続表示のテストです。
$ 

よしよし。ちなみに stdoutstderr にすれば標準エラー出力に出力できます。

このように、なでしこ自体に命令が用意されていない場合でも JavaScript(Node.js)の機能を使うことで自分でなんとかすることができます。

この例では「JSメソッド実行」を使いましたが、他にも似た命令として「JS実行」などがあります。


余談ですが eval がありますね!

↓のようなことができるそうです。

AAA=0
『AAA=123』をナデシコする
AAAを表示

標準入力から全部読みたい

標準入力取得時 だと EOF (入力ストリームの終端)が検出できないため、「標準入力からとりあえず全部読んで何かしたい」「標準入力から読み終わったタイミングで何かしたい」といったことができないようでした。

なでしこの機能だけで簡単に済ます方法を思いつけなかったので、標準入力からの入力をシェルスクリプトで一時ファイルに出力して、なでしこではその一時ファイルから読む、という方式にしてみました。
ファイル名は stdin で決め打ち。

#!/bin/bash

STDIN_FILE=stdin

if [ -e $STDIN_FILE ]; then
  rm $STDIN_FILE
fi

if [ -p /dev/stdin ]; then
  # 標準入力の内容をすべてファイルに保存
  cat > $STDIN_FILE
fi

cnako3 "$@"

これを cnako3.sh として保存して、以降は cnako3 コマンドを置き換える形で

$ ls | ./cnako3.sh sample.nako3

のように使っていきます。

参考:

なでしこ側では普通にファイルを読めばよいですね。

# cat2.nako3

●標準入力全部読とは
    もし、「stdin」が存在するならば
        「stdin」を読んで戻す
    違えば
        「標準入力からの読み込みに失敗」のエラー発生
    ここまで
ここまで

標準入力全部読んでテキストに代入
テキストを表示

標準入力全部読んでテキストに代入 したあとは、普通の文字列の処理です。

$ cat sample_end_with_newline.txt | ./cnako3.sh cat2.nako3 
UTF-8 テキストファイル
ファイル終端の改行あり
行末にスペース→  

$ 

改造してみる

cat コマンドなんか作って何が面白いんや? と思われるかもしれませんが、 cat ができるとそれを改造していろいろ遊べます。

LF を "<改行>" + LF に置換してファイル終端も表示

# cat3.nako3
# 継続表示、標準入力全部読 は同じなので省略

標準入力全部読んでテキストに代入

テキストのLFを「<改行>{LF}」に置換して継続表示
「<ファイル終端>」を表示

入力の最後に改行がある場合:

$ cat sample_end_with_newline.txt | ./cnako3.sh cat3.nako3 
UTF-8 テキストファイル<改行>
ファイルの最後に改行あり<改行>
行末にスペース→  <改行>
<ファイル終端>
$ 

入力の最後に改行がない場合:

$ cat sample_end_without_newline.txt | ./cnako3.sh cat3.nako3 
UTF-8 テキストファイル<改行>
ファイルの最後に改行なし<改行>
行末にスペース→  <ファイル終端>
$ 

これで行末の余分なスペースを見つけやすくなりますね!
cat --show-all みたいな感じ。
最後に <ファイル終端> を付けて、最後の改行やスペースがあるかどうかも分かりやすくしてみました。

行番号を表示

cat -n みたいなの。

# cat4.nako3
# 継続表示、標準入力全部読 は同じなので省略

標準入力全部読んでテキストに代入

テキストをLFで区切って行リストに代入
行数 = 行リストの配列要素数

行番号を1から(行数)まで繰り返す
  「{行番号}: {行リスト@(行番号 - 1)}」を表示
ここまで
$ cat sample_end_with_newline.txt | ./cnako3.sh cat4.nako3 
1: UTF-8 テキストファイル
2: ファイルの最後に改行あり
3: 行末にスペース→  
4: 
$ 

行番号が分かってべんり!

1文字ずつ処理

1文字ずつ処理するバージョンも書いてみました。

# cat5.nako3
# 継続表示、標準入力全部読 は同じなので省略

●(テキストから位置の)文字取得
    文字配列 = テキストを文字列分解
    文字配列@位置を戻す
ここまで

標準入力全部読んでテキストに代入

テキスト文字数はテキストの文字数
位置は0
位置が(テキスト文字数 - 1)以下の間繰り返す
    テキストから位置の文字取得して文字に代入
    文字を継続表示
    位置 = 位置 + 1
ここまで

「文字取得」関数を呼び出すたびに文字列分解していて効率悪いですが、おためしなので気にしない。

$ cat sample_end_with_newline.txt | ./cnako3.sh cat5.nako3 
UTF-8 テキストファイル
ファイルの最後に改行あり
行末にスペース→  
$ 

行をソート

cat コマンドを改造すると、たとえば行をソートするコマンドなんかも作れるようになります。

# sort.nako3
# 継続表示、標準入力全部読 は同じなので省略

標準入力全部読んでテキストに代入
テキストをLFで区切って行リストに代入
行リストを配列ソートしてソート済行リストに代入

ソート済行リストを反復
    対象を表示
ここまで
$ cat sort.nako3 | ./cnako3.sh sort.nako3 




        「stdin」を読んで戻す
        「標準入力からの読み込みに失敗」のエラー発生
    「process.stdout」の「write」を["{値}"]でJSメソッド実行
    ここまで
    もし、「stdin」が存在するならば
    対象を表示
    違えば
●標準入力全部読とは
●(値を)継続表示とは
ここまで
ここまで
ここまで
ソート済行リストを反復
テキストをLFで区切って行リストに代入
標準入力全部読んでテキストに代入
行リストを配列ソートしてソート済行リストに代入
$ 

コンパイラ

(この節は 2022-06-05 に追記しました)

cat コマンドを改造して育てていくとコンパイラも作れます。

……というのは半分冗談ですが :sweat: 半分はほんとで、コンパイラがやっていることは

  • (1) 入力を読む
  • (2) 読んだデータを加工する
  • (3) 加工したデータを出力する

のように改造 cat コマンドと同じですし、作るときも実際にまず cat コマンド(のようなもの)を作ってちょっとずつ改造を加える形で作っていました。


というわけで cat コマンドっぽいものを作っていろいろ試してみました。この記事は以上です。

この記事を読んだ人は(ひょっとしたら)こちらも読んでいます

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?