Programming」カテゴリーアーカイブ

\033[

今日の覚書

【第十二回】エスケープシーケンス(2) – あらほりLinuxメモ

#!/bin/sh

# \033[数値A で戻りたい行数の数値
# \033[0J    でカーソル以降の行を削除

echo いくぞー

for s in いーち にー さーん だぁー!
do
    sleep 1
    printf "\033[1A\033[0J"
    echo $s
done

daa

なるほど、使い道は微妙だけど。
もっと更新してほしいな、忙しいのだろうけど。

1 ページ前の色分けはも含め C や Python からも使えるのね。
しかし cmd.exe はそもそも色分けができないだろ、なんだが。
エスケープシーケンス

Python

#!/usr/bin/env python3

print("\033[32m緑色おおおお");
print("\033[31m赤色おおおお");
# 戻す
print("\033[39m", end='');

C (glib)

#include <glib.h>

int
main (int argc, char* argv[]) {

    g_printf("\033[32m緑色おおおお\n");
    g_printf("\033[31m赤色おおおお\n");
    // 戻す
    g_printf("\033[39m");
    return 0;
}

033

コッチは警告表示で赤で出力とか便利そう。
sh で探して C, Python も使えたので得した気分。

Bash Increment

シェルスクリプトの変数って ++ でインクリメントできたんですね。
Bash – シェルのループ処理内でのインクリメント – Qiita
これも凄いがコメント(一番下)がもっと凄い。

while (( count < 10000 ));

こんなのまでできたのか、マジで速いし。

試すと (( count– )) で普通にデクリメントするね。
Ramen Timer for Ubuntu sh | PaePoi
これを使えば seq を逆順ソートとかしていたラーメンタイマーを

#!/bin/sh
 
# カップラーメン用にどうぞ
 
read -p 完成までの残り時間を秒で指定: count
echo 止めるには Ctrl+C
 
while (( count > 0 )); do
    printf "残り%02g分%02g秒  \r" $(( count / 60 )) $(( count % 60 ))
    (( count-- ))
    sleep 1
done
echo できあがりぃ?

inc

速いうえに圧倒的に可読性の良いコードになりました。
C と似た感じにするだけで随分と違うものだ。
分と秒を分けるのはコレでいいです。

Fedora の sh (Bash) のみでの動作確認ですが。
シェルが違う人は bash 指定に書き換えしてね。

しかし覚書を qiita に書き込んでいる人達はなんだろう?
それよりこういう有用なコードを詰め込んだサイトを作ってほしいな。
覚書色々 – L’Isola di Niente
自力でチビチビ作っているんだけどまだ少ないよな。。。

GIOChannel @ End of line

GIOChannel にて scanf でよく使う行末から値を得るように。

#include <glib.h>

int
main (int argc, char* argv[]) {

    GError *error = NULL;
    GIOChannel *channel;
    gchar *text;
    gsize length, terminator_pos;

    g_printf("Input :");

    channel = g_io_channel_unix_new(0);
    g_io_channel_read_line(channel, &text, &length, &terminator_pos, &error);
    g_io_channel_unref(channel);

    text[terminator_pos] = '\0';
    g_printf("output[%s]\n", text);
    g_free(text);

    return 0;
}

giochannel

なんだよコレ、いきなり入力画面になるんだが。
しばらく悩んでやっと手段を見つける。

#include <glib.h>

int
main (int argc, char* argv[]) {

    GError *error = NULL;
    GIOChannel *channel;
    gchar *text;
    gsize length, terminator_pos;

    g_printf("Input\n"); /* Important '\n' */

    channel = g_io_channel_unix_new(0);
    g_io_channel_read_line(channel, &text, &length, &terminator_pos, &error);
    g_io_channel_unref(channel);

    text[terminator_pos] = '\0';
    g_printf("output[%s]\n", text);

    g_free(text);
    return 0;
}

giochannel2

改行するだけだった、いやだから行末から値を得たいのですけど。
GIOChannel では一行単位でしか値を得られないのだろうか。

よくワカラン、ならば入力も GIOChannel にしたらどうだ?
これは上手くいったのでコメントと例外処理を付ける(外国人用)

#include <glib.h>

int
main (int argc, char* argv[]) {

    GError *error = NULL;
    GIOChannel *channel;
    GIOStatus status;
    gchar *text;
    gsize length, terminator_pos;

    for (;;) {

        /* stdout */
        channel = g_io_channel_unix_new(1);
        status = g_io_channel_write_chars(channel, "Exit if string is empty :", -1, NULL, &error);
        if (status == G_IO_STATUS_ERROR) {
            g_warning (error->message);
            g_error_free (error);
            g_io_channel_unref(channel);
            return 1;
        }
        g_io_channel_unref(channel);

        /* stdin */
        channel = g_io_channel_unix_new(0);
        /* Start from the end of the line */
        status = g_io_channel_read_line(channel, &text, &length, &terminator_pos, &error);
        if (status == G_IO_STATUS_ERROR) {
            g_warning (error->message);
            g_error_free (error);
            g_io_channel_unref(channel);
            return 2;
        }
        g_io_channel_unref(channel);

        /* Exit if string is empty */
        if (terminator_pos == 0) {
            g_free(text);
            break;
        }
        /* Remove '\n' */
        text[terminator_pos] = '\0';
        g_printf("[%s]\n", text);

        g_free(text);
    }
    return 0;
}

giochannel3

g_io_channel_write_chars での出力なら当然のごとく行末からに。
g_printf がバッファの行末へ入出力ポインタを seek しないのかな。
いや、g_printf を連続して呼べば追記になるのだからそれは違う。

単純にまったく別のポインタ管理をしているのかも。
もしそうなら下記のようにすれば上書きか挿入かがされるはず。

#include <glib.h>

int
main (int argc, char* argv[]) {

    GIOChannel *channel;

    g_printf("First");
    g_printf("Second");
    channel = g_io_channel_unix_new(1);
    g_io_channel_write_chars(channel, "Third", -1, NULL, NULL);
    g_io_channel_unref(channel);
    return 0;
}

giochannel4

見事に挿入されてしまった、そういうことか。
最初のいきなり入力画面は表示できない二行目に押し出されていたのね。
日本語情報が無いに等しいから自分で見つけるしかないのが辛いNE。

GIOChannel と g_printf は混在させないのが無難、という結論で。

g_application_hold

Glib の g_io_add_watch を使う – biochem_fan’s note

判り易いサンプルコードをありがとう。
GIOChannel はファイル・ディスクリプタであれば何でも監視できるのか。
だいぶ解ってきた、かも。

色々試してみると。

GIOChannel *channel;
channel = g_io_channel_unix_new(0);

だけで stdin の GIOChannel が得られるようだ。
ファイル・ディスクリプタってつまり固有の整数と思えばいい?

関係無いけど GApplication で新たに解ったことが。
g_application_hold(app); でインクリメントすれば参照カウントが増える。
これなら GtkWindow を作らずともメインループが回せる。

実は GApplication を使うコレをやろうと思っていたんですが。
HowDoI/GNotification – GNOME Wiki!
何をどうやっても動かない、書いているとおりにコピペしてもだ。
手段が悪いのかバグなのかは解らない、後回し!

これを組み合わせて通知領域にメッセージを投げるコードでも。
端末に撃ち込んだ文字列を即座に通知するサンプル。
今回は notify-send コマンドに投げて茶でも濁しておきます。

#include <glib.h>
#include <gio/gio.h>

/*
gcc ioc.c `pkg-config --cflags --libs gio-2.0`
*/

static gboolean
io_func_cb(GIOChannel *source, GIOCondition condition, gpointer app) {

    GError *error = NULL;
    GNotification *notification;
    gchar *text;
    gchar *s;

    g_io_channel_read_line(source, &text, NULL, NULL, &error);
    if (g_strcmp0(text, "exit\n") == 0) {
        /* Decriment */
        g_application_release(app);
        g_free(text);
        return FALSE;
    }
    s = g_strdup_printf("notify-send -i gtk-ok %s", text);
    g_spawn_command_line_async(s, NULL);
    g_free(s);
    g_free(text);
    
    return TRUE;
}

static void
activate_cb (GApplication *app, gpointer user_data) {

    GIOChannel *channel;

    /* 0 == stdin */
    channel = g_io_channel_unix_new(0);
    g_io_add_watch (channel, G_IO_IN, io_func_cb, app);
    g_io_channel_unref(channel);
    /* Inclement */
    g_application_hold(app);
}

int
main (int argc, char* argv[]) {

    GApplication *app;
/*
    g_type_init(); // < 2.36
    g_thread_init(argc, argv); // < 2.32
*/
    app = g_application_new("org.suzuki.hayabusa", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (activate_cb), NULL);
    g_printf("Type 'exit' to exit\n");
    /* MainLoop run */
    g_application_run(app, argc, argv);

    g_application_quit(app);
    return 0;
}

notify_send

作った後で気が付いたけどコレだと普通にループ文でも同じことができるやw
やっぱり GUI を使ってナンボの仕組みなんだろうね。
g_application_hold の実験ということで。

stdin ? command : pipe

コマンドからパイプで渡されたデータはどう処理するのだろう?
どうせ stdin だろう、サクッと Python で実験

#!/usr/bin/env python3

# if stdin != pipe:
#     Press Ctrl+D

import sys
print(sys.argv)
print(sys.stdin.read())

pypipe

普通に stdin に入ってくるので値を得るのは簡単。
だけどうっかり単独起動すると当然のように入力待ちになる。
なので強制終了するしかない、cat コマンドもそうなるね。

でも bc コマンドなんかはパイプ経由だと計算結果を出力のみ。
単独起動だとインタラクティブシェルにと振り分けされる。
何か方法はあるのだろう、検索検索。

シェルスクリプトでパイプからの入力とキーボード入力を区別する

シェルスクリプトなら上記リンク先の方法で振り分けできた。
ファイルディスクプリタで見分けるのか、おぉワカラン。

LinuxやUNIXでパイプで渡されたデータをC言語で適切に処理するには? – Yahoo!知恵袋

やっぱりファイルディスクリプタか。
Non-blocking I/O がどうかでなく振り分けがしたいだけなんですが。
これはこれで勉強になるのでブックマークと。

面倒だ、bc のソースを読んでしまえ。
結構利用しているけどもう 15 年もメンテされていなかったのか…
http://ftp.gnu.org/gnu/bc/

grep しまくって isatty という関数を見つけた。
UNIXの部屋 コマンド検索:isatty (*BSD/Linux)

コレだよコレ。
STDOUT_FILENO はどうせ enum だし in 側はゼロだろ、ならば。

#include <stdio.h>
#include <unistd.h>

int
main (int argc, char* argv[]) {
    if (isatty(0)) {
        printf("stdin == Console\n");
    } else {
        char pipe[256];
        fgets(pipe, 256, stdin);
        printf(pipe);
    }
    return 0;
}

isatty

こんなにアッサリと振り分けできるじゃない。
マネしただけなんですが。
いやぁ GPL って本当に素晴らしいですね。