あると便利かな、と思うのだが見つからなかったコマンド。pause
シェルスクリプトでユーザからの入力待ちするコマンド。
DOSにはあるんだけどなぁ...。
つぅことで作ってみた
#include <stdio.h> #include <unistd.h> #include <termios.h> #include <stdbool.h> #include <stdlib.h> #include <getopt.h> #include <stdint.h> #include <sys/time.h> #include <signal.h> #include <errno.h> #include <string.h> #define DEFAULT_TIMEOUT 0 #define DEFALUT_PROMPT NULL void usage( FILE* output, int argc, char** argv ) { fprintf( output, "Usage: %s [OPTION]...\n", *argv ); fprintf( output, "Pause untill press key or time out.\n" ); fprintf( output, "Return pressed key code, or return 255 if timeout.\n" ); fprintf( output, "\n" ); fprintf( output, "Mandatory arguments to long options are mandatory for short options too.\n" ); fprintf( output, " -c, --stdout show keycode instead use exit code.\n" ); fprintf( output, " -h, --help show this help and exit.\n" ); fprintf( output, " -p, --prompt[=STRING] show STRING before pause.\n" ); fprintf( output, " -t, --timeout[=NUM] timeout after NUM sec(0:infinity).\n" ); } void dummy( int _ ) {} uint8_t pause( char* prompt, long sec, long usec ) { struct termios tm; struct termios tm_save; struct sigaction act; struct sigaction act_save; struct itimerval itimer; ssize_t size; uint8_t buf; // ターミナル情報の保存 tcgetattr( STDIN_FILENO, &tm_save ); tm = tm_save; // シグナルoff カノニカルoff エコーoff tm.c_lflag &= ~ISIG; tm.c_lflag &= ~ICANON; tm.c_lflag &= ~ECHO; tcsetattr( STDIN_FILENO, TCSANOW, &tm ); // プロンプトの表示 if( prompt != NULL ) { printf( "%s", prompt ); fflush( stdout ); } // シグナルをpauseを終了させるために使う。 // ので、handlerは何もしない。 memset( &act, 0, sizeof( act ) ); act.sa_handler = dummy; if( sigaction( SIGALRM, &act, &act_save ) < 0 ) { perror( "sigaction" ); exit( EXIT_FAILURE ); } // itimerval構造体の初期化。 itimer.it_value.tv_sec = sec; itimer.it_value.tv_usec = usec; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; if( setitimer( ITIMER_REAL, &itimer, NULL ) < 0 ) { perror( "setitimer" ); exit( EXIT_FAILURE ); } // 一字読み込み size = read( STDIN_FILENO, &buf, sizeof(uint8_t) ); if( size < 0 ) { if( errno == EINTR ) { buf = -1; // TIMEOUT } else { perror( "read" ); exit( EXIT_FAILURE ); } } else if( size == 0 ) { buf = 0; // EOF } // タイマーを殺す。 itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; if( setitimer( ITIMER_REAL, &itimer, NULL ) < 0 ) { perror( "setitimer" ); exit( EXIT_FAILURE ); } // ハンドラを元に戻す。 //act.sa_flags = SA_RESETHAND; if( sigaction( SIGALRM, &act_save, NULL ) < 0 ) { perror( "sigaction" ); exit( EXIT_FAILURE ); } // ターミナル情報を元に戻す tcsetattr( STDIN_FILENO, TCSANOW, &tm_save ); // 値を返す。 return buf; } int main( int argc, char** argv ) { bool use_stdout = false; char* prompt = DEFALUT_PROMPT; int timeout = DEFAULT_TIMEOUT; // 引数処理 while( 1 ) { static struct option longopts[] = { { "stdout", no_argument, NULL, 'c' }, { "help", no_argument, NULL, 'h' }, { "prompt", required_argument, NULL, 'p' }, { "timeout", required_argument, NULL, 't' }, { 0, 0, 0, 0 } }; int c = getopt_long( argc, argv, "chp:t:", longopts, NULL ); if( c == -1 ) { break; } switch( c ) { case 'c': use_stdout = true; break; case 'h': usage( stdout, argc, argv ); exit( EXIT_SUCCESS ); break; case 'p': prompt = optarg; break; case 't': timeout = atoi( optarg ); break; default: usage( stderr, argc, argv ); exit( EXIT_FAILURE ); } } if( optind == argc - 1 ) { usage( stderr, argc, argv ); exit( EXIT_FAILURE ); } uint8_t buf = pause( prompt, timeout, 0 ); if( use_stdout ) { printf( "%d\n", buf ); return 0; } else { return buf; } }
せっかくC++なのにほとんどCと言う...。ってかこれCでコンパイル通る気がしてきた...。
むだにusageとかつけてみたり。
まぁ、そのusageが妙ちくりんな英語なんですがね。
一時入力があるまでポーズするコマンド。
多分、単品利用ではほとんど無価値かと。
で、似たようなものにreadつーコマンドがあるのですが、readと違って改行しなくても終了します。
で、押下したキーのキーコードを終了ステータスとして返します。
オプションとしては、終了ステータスではなくて、標準出力にキーコードを吐く-c。
あと、一定秒数後にタイムアウトする-tオプション。
入力待ちの前にプロンプトを吐く-pオプション。
あたり。
返り値はtimeoutで255、EOFで0。(ただし、Ctrl-dはキーコードの4になります。
つうこって使用例。
#!/bin/sh # irm if [ $# -ne 0 -a -e $1 ]; then while true; do pause -p "remove file '$1'? (y/N) " case $? in 121|89) # y/Y echo rm $1 break ;; 110|78|10|3|4) # n/N/Enter/C-c/C-d echo break ;; *) echo $? ;; esac done fi
...Ctrl-Cぐらいは受け付けるべきだったかなぁ?
まぁいいや。
こんんか感じで。rm -i使えという話ですが。
あとrm -iとの違いはEnter押下が必要かどうか。
んー。あまり有用性なかったかも。
まぁ、そんなこんな。
あと、shuffleとかも作ったんでそっちもよろしく。
http://d.hatena.ne.jp/goth_wrist_cut/20071129/1196316532