までの途中にあるものなら , それを保 存ファイルに書く といったようなパターンをたくさん定義す る方法と , List 11 のように この文字列がヘッダを表すものなら , 記事の終わりまでのデータを一度に読 み込み , そのあとで記事の内容を保存 ファイルに書き出す という逐次処理的なアプローチのふたつが 考えられる。 awk のスクリプトとしては前 者のようにパターンを多用したほうが自然 だが , 実行速度を考えると , 後者のように 記事を一度に読み込んて、一度に書き出した ほうが速い可能性があるのて、 , 今回は両方 のスクリプトを書いてみた。どちらのスク リプトの作成も , C と比べるとはるかに容易 て、あった。 長さは後者のほうが短いが , 読みやすさ については , どちらが優れているかは容易 には判断て、きない。 C のプログラマには後者 のほうがわかりやすいだろうと思うが , イ べントドリプンの考え方に慣れている人に は前者も馴染みやすいかもしれない awk て、は , 入力データの各行に対して処 理が行われるのて、 , 入力ファイル全体の処 理の前後て、 logrc の読み書きを行うには , BEGIN , END というスペシャルバターンを 使うことになる。 ヘッダの認識も , 正規表現を使って簡潔 に書けた。ただ訒識したヘッダからニュ ースグループ名などを取り出すには substr な どの関数を使う必要があるのが , ややわず らわしい。 awk には連想配列があるのて、 , C のように リスト構造を定義する必要はなく , history 情報や記事の文字列は , 連想配列を読み書 きするだけて、扱える。これもこの種のスク リプトを書く際には非常に便利て、ある。 perl のスクリプト (List 12 ) も awk と同 様 , 短時間て、作ることがて、きた。 awk のような BEGIN,END のスペシャル 13 : } List 9 145 : 146 : 147 : 148 : 149 : 150 : 151 : 152 : 153 : 154 : 155 : 156 : 157 : 158 : 159 : 160 : 161 : 162 : 163 : 164 : 165 : 166 : 167 : 168 : 169 : 170 : 171 : 172 : 173 : 174 : 175 : 176 : 177 : 178 : ・ 179 : 180 : 181 : 182 : 183 : 184 : 185 : 186 : 187 : 188 : 189 : 190 : 191 : 192 : 193 : 194 : 195 : 196 : 197 : 198. 0 lastarticle ニ lastarticle->nextarticle = makearticleentry(newsline) : if (fgets(newsline, NEWSLINE-hAX, infi (e) = NULL) break; do { newsgroup, art i C 1 eno, resno) ; printf( ” Newsgroup %s, Article %d, Response Xd lastarticle ニ lastarticle->nextarticle ニ makearticleentry(newsline) : fputs(thisarticle- 〉 string, logfile) ; do { thisarticle = article; if ((logfile = fopen(lastlogfile,"a")) = NULL) exit(l); lastlogfile : thishistory->logfile; if (lastlogfile ! = NULL) fclose(logfile); if (thishistory->logfile ! = lastlogfile) { if (thishistory->nextresno = = resno) { s trcmp ( th i sh i story¯〉 newsgroup, newsgroup) if (thishistory->articleno = ニ articleno & & do { thishistory ニ history; / * 必要ならこの記事を保存する * / } while (strcmp(newsline, "NOTES>> }n") ! = の ; ニの { thisarticle->nextarticle) ! = NULL) ; } while ((thisarticle th i sh i s tory- >nextresno + + : puts("archived") : } else { thishistory = NULL; break ; } whi le ((thishistory ニ thishistory- 〉 nexthistory) ! = NULL) ; if (thishistory = NULL) puts("skipped") ; / * 記事文字列を消す * / thisarticle = article; do { deletearticleentry(thisarticle) : } while ((thisarticle = thisarticle- 〉 nextarticle) ! = NULL) ; if (lastlogfile ! ニ NULL) fclose(logfile); fclose(infile); / * history データを dump する * / if ((logrc = fopen(LOGRC, "w")) = NULL) exit(l) ; while (history) { fclose(logrc) : history ニ history- 〉 nexthistory; free(history) : history->nextresno, history->logfile) : fprintf(logrc, "Xs#tXd*tXd}tXs#n", history->newsgroup, history- 〉 articleno' awkC 記述した logarchl . awk ( 多ノヾターン型 ) List 1 0 0 close(logrc) logfile [ $ 1 , $ 2 ] ニ $ 4 nex tresno [ $ 1 , $ 2 ] = $ 3 ニ 4 ) { if (NF while ( (getl ine く logrc) logrc logrc ” 4 : BEGIN { 3 : # history データを undump する 1 : # logarchl. awk ー BBS のログファイルを保存する ( 多パターン型 ) 12 : 11 : 10 : 9 : 8 : 7 : 6 : 5 : 2 : 46 C MAGAZINE 1991 9
特集フレタ系言語研究 つまり , Fig. 3 ( 49 頁参照 ) のような記事が 入っていると , その内容が emtexjpn. 10g と いうファイルに追加されていく。 1 ogrc の 内容 (history と呼ぶことにする ) は , logarch の起動時に読み込まれ , 終了時に更新され る 己事のヘッダの判定方法は , BBS に依存 こを書き換えれば , ほかの しているが , システムに対応させることもて、きるだろう。 なお , sed て、は複数のファイルを動的にオ ープンすることや , 配列を使うことがて、き ないのて、 , この例による比較は C , awk,perl のみについて行った。 List 9 0 73 : Char **argv ; FILE *infile,*logrc,*logfile; 75 : struct history *history, *thishistory; struct article *article,*lastarticle,*thisarticle; 77 : 78 : char newsl ineCNEWSLINE_MAX + 1 ] ; 79 : char *lastlogfile = NULL; 80 : / * history データを undump する * / 82 : h i s tory = NULL : if ((logrc = fopen(LOGRC, "r")) ! = NULL) { 83 : 84 : struct history *lasthistory; if ((thishistory = malloc(sizeof(struct history))) = NULL) exit(l); 85 : while ( !feof(logrc)) { 86 : i f (fscanf ( 1 ogrc, ” XsXdXdXs" 88 : thishistory->newsgroup, &thishistory- 〉 articleno, &thishistory->nextresno, thishistory- 〉 logfile) 89 : if (history = NULL) 90 : 91 : history = thishistory; 92 : else 93 : lasthistory->nexthistory = thishistory; 94 : lasthistory = thishistory; if ((thishistory = 1 loc(sizeof(struct history))) = NULL) exit(l); 95 : 96 : if (history ! = NULL) lasthistory->nexthistory = NULL; 98 : free(thishistory) ; 99 : fclose(logrc) : 100 : 101 : 102 : 103 : infile ニ stdin; if (argc 〉 = 2 ) { 104 : if ((infile = fopen(argvC1), ” r ” ) ) = NULL) exit(l) ; 105 : 106 : 107 : / * 入力ファイルを検索する * / 108 : while (fgets(newsline, NEWSLINE MAX, infile) ! = NULL) { 109 : char newsgroup CNGNAME MAX + 1 ] : 110 : int articleno; 111 : 112 : char *S, *t; / * 記事へッダかどうかチェックする * / 113 : if (strncmp(s = newsl ine, ” N0te", 5 ) ニ 0 & & 114 : = skipspace(s + 5 ) ) & & 115 : (s ニ getint(s,&articleno)) & & 116 : (s ニ skipspace(s)) & & 117 : 118 : (t = strchr(s,')')) 119 : tC1] ニ 120 : 121 : 122 : 123 : 124 : 125 : 126 : 127 : 128 : 129 : 130 : 131 : 132 : 133 : 134 : 135 : 136 : 137 : 138 : 139 : 140 : 141 : 142 : 143 : 144 : 0 1 三ロ 開発効率 スクリプトの長さは ,Table 7 ( 49 頁参照 ) のとおりとなった。やはり C のソース (List 9 ) が圧倒的に長い history 情報や記事の文字列を保存してお くリスト構造の定義や , それらのデータの 挿入・検索・削除などの処理にコードのか なりの部分が費やされている。これらの記 述にはかなり頭を使うこともあって , C 言語 て、の開発効率は著しく下がっている。 また , 正規表現によるパターンマッチが 使えないのもこのような応用て、は問題とな る。 lex などのほかのツールを併用すれば , 正規表現を使うこともて、きるが , すべて C て、 己述するとなると , 今回行ったように ・文字を検索する strchr ( ) ・文字列を比較する strcmp ( ) ・空白を読み飛ばす skipspace( ) ・整数を認識する getint ( ) といった関数を , それぞれ必要に応じて定 義したうえて、 , 明示的に呼び出すことにな るだろう。これにも手間がかかる。 次に , awk の場合を見よう。 awk て、この 種のスクリプトを書く場合 , List 10 のよう この文字列がヘッダから記事の終わり 特集フィルタ呆言語研究 45 int resno; memcpy(newsgroup, S + 1 , t ー S newsgroup [ t article = lastarticle = makearticleentry(newsline ・ if (fgets(newsline, NEWSLINE_MAX, infile) = = NULL) break; if (strncmp(s = newsl ine, " 〔 BASENOTE with", 15 ) = = skipspace(s + 15 ) ) (s = getint(), NULL)) strncmp(), ” Res ]}n", 6 ) = = の { resno = 0 : ) else if ・ (!(strnc 叩 (s = newsline,"[ RESPONSE:",II) = = 0 = skipspace(s + 11 ) ) (s = getint(), &resno)) (s = skipspace(s)) 報 strncmp(), " 0f ” , 2 ) = 0 = skipspace(s + 2 ) ) & & (s = getint(), NULL)) s trncmp( s, deletearticleentry(article) : cont inue ; 1 一一 1 ロ / * 記事へッダだった * / 0
ニュアル ) ( ハードディスクから処理系をロードす る時間も含めて計測するため , ディスク キャッシュは無効にした ) 結果は Table 6 のとおりて、ある。 単純な置換処理を得意とする sed の速さが 目立つ。 sed. exe のサイズが小さいこともあ って , 処理系のロードを含めても , ほとん ど C と変わらない実行時間て、ある。 awk , perl は , このような単純な処理て、はあまり速い とはいえない。この差を , 「 3 倍もの差」と考 えるか , あるいは「 ( この程度のデータ量な ら ) たった 6 秒の差」と考えるかは場合や人に よるだろうか , この例て、は , sed が使える環 境て、あれば , あえて awk , perl を使うメリッ トはないといえる。 20 : ) ; 26 : ) ; 33 : { より複雑な 心例による比較 次に , 単純な置換て、はなく , もっと複雑 な , 複数のファイルを読み書きするような 処理を考える。ここて、は , BBS のログの中 から必要なものをファイルに保存するスク リプト (logarch) を作ってみた (List 9 , 10 , 11 , 12 ) 。 たとえば , logrc というファイルに comp. msdos 270 104 emtexJpn.log などという設定を書いておき , ログの入っ たファイルを入力ファイルとしてこのスク リプトを起動する。このログファイルに comp. msdos というニュースグループの 270 番目のノートの 104 番目からのレスポンス , List 9 C 言語て記述した logarch. c 39 : / * 整数文字列を検索し , iptr ト NULL なら数値を *iptr に入れる * / return(s) : dO ( s + + : } while (isspace(*s)); if ( ! isspace(*s)) return(NULL) : Char *S : 31 : char *skipspace(s) * そうでなければ NULL を返す * それらを読み飛ばし , その次の位置を返す * 28 : / * s の先頭が 1 個以上の空白なら char *string; struct article *nextarticle; struct article { 22 : / * 記事文字列用線形リスト * / char logfi Ie[FILENAME_MAX + 1 ] : int nextresno; int articleno; char newsgroupCNGNAME MAX + 1 ] ; struct history *nexthistory; struct history { 13 : / * history データ用線形リスト * / 11 : #define FILENAME_MAX 256 10 : #define NEWSLINE_MAX 1000 9 : #define NGNAME_MAX 40 8 : #define LOGRC "-logrc ” 6 : #include く string. h 〉 5 : #include く ctype. h 〉 4 : #include く stdlib. h> 3 : #include く stdio. h 〉 1 : / * logarch. c ー BBS のログファイルを保存する * / 2 : 7 : 12 : 14 : 15 : 16 : 17 : 18 : 19 : 21 : 23 : 24 : 25 : 27 : 29 : 30 : 32 : 34 : 35 : 36 : 38 : 41 : 42 : 43 : { 45 : 46 : 48 : } 49 : 51 : 52 : 54 : 55 : 56 : 58 : 59 : 60 : 62 : 65 : 66 : { 67 : 68 : 69 : ) 71 : int argc; int main(argc, argv) free(thisarticle); free(thisarticle—>string) ; struct article *thisarticle; 64 : VOid *deletearticleentry(thisarticle) 63 : / * 記事文字列リストのエントリを削除 * / return(thisarticle) ; thisarticle->nextarticle = NULL; memcpy(thisarticle->string, s, strlen(s) + 1 ) : if ((thisarticle- 〉 string = malloc(strlen(s) + 1 ) ) ー if ((thisarticle = malloc(sizeof(struct article))) struct article *thisarticle; Char *S : struct article *mkearticleentry(s) 50 : / * 記事文字列リストのエントリを作成する * / return(s) : do ( s + + : } while (isdigit(*s)); if (iptr ! = NULL) *iptr = atoi(s); if ( ! isdigit(*s)) return(NULL) : int *iptr; char *S : 40 : char *getint(), iptr) = NULL) exit(l); = NULL) exit(l); TabIe 6 実行結果 実行時間 3.1 秒 3.7 秒 9.3 秒 6.9 秒 44 処理系 sed awk pe 日 C MAGAZINE 1991 9
しの直前の箇所に , system 関数によって chk dsk コマンドを起動するようなコードを挿入 し , その際に表示される使用可能メモリ量 を , スクリプトの実行前に chkdsk て、調べた 際の量から差し引いた量を , スクリプトの 実行時に消費されるメモリ量とした。 この結果として得られたメモリ使用量は , TabIe 9 のようになった。処理系の大きさが そのまま現れた結果といえる。とくに , perl の場合 , 日本語 FEP を登録してから実行し てみたところ , 残りメモリはたった 36K バイ トしかなく , ギリギリの状態て、動いていた。 この状態て、は , 内部に大きな配列を取った り , 外部プログラムを起動するのは困難て、 ある。 日本語 FEP を EMS メモリに置くといった 工夫が必要になるだろう。 済むのて、 , 当然ながらメモリ効率は非常に 必要最小限のランタイムライプラリだけて、 ず , プログラム本体以外のオーバヘッドは C の場合 , 実行時に処理系がロードされ 比較結果 や perl を使ったほうが , 複雑な処理の場合の ディスクやメモリが十分あるならば , awk ない場合が多いだろう。 わずに , C て、プログラムを書かなければなら れるような応用て、は , フィルタ系言語を使 用やファイルのサイズなどが厳しく制限さ ある程度複雑な処理の場合 , メモリの使 以上の複雑な処理を行わせるのは困難て、あ ら ) 短くて済む。ただし , sed て、単純な置換 よい。速度も速いし , 記述も ( 単純な処理な 単純な置換程度て、あれば , sed を使うのが と思われる。 語の処理系を次のように使い分けるとよい 以上の比較から , DOS て、はフィルタ系言 List List 32 : 31 : 30 : 29 : 28 : 26 : 25 : 24 : 23 : 22 : 21 : 20 : 19 : 17 : 14 : 12 : } 11 : 10 : 9 : 8 : 4 : 2 : 56 : 55 : 54 : 53 : 52 : 51 : 48 : 47 : } 46 : 45 : 44 : 42 : 41 : 40 : 39 : 38 : 37 : 36 : 35 : 34 : 33 : 32 : 31 : 30 : 29 : 28 : 27 : 26 : 25 : 24 : 23 : 22 : 21 : 20 : 1 1 getline if ( $ 0 ー / 響 [ BASENOTE with[ *t] + [0ー9] + Res \ ] $ / ) resno = 0 if (((newsgroup, articleno) in nextresno) & & # 必要ならこの記事を保存する article = article $ 0 } while ( $ 0 ! ー /NOTES>>C }t] + /) getline article ニ article $ 0 ” \ n ” do { printf "Newsgroup Xs, ArticIe Xd, Response Xd # 記事へッダだった next else resno ニ $ 3 , newsgroup, iC 1 eno, (nextresno[newsgroup, articleno] = = resno)) { if (logfile[newsgroup, articleno] ! = lastlogfile) { if (lastlogfile ! ニ ' ) close(lastlogfile) lastlogfile = logfile[newsgroup, articleno] print article 〉〉 lastlogfile nextresno[newsgroup, articleno] 十十 print "archived ” } else { print ” skipped ” 49 : # history データを dump する 50 : END { ln nextresno) { spl it(indices, ind, SUBSEP) printf ” Xs#t%dYtXd#tXs}n ” ind[l], indC2],nextresnoCindices], logfile[indices] 〉 logrc close(logrc) perlC 記述した logarch.pl 1 : # logarch. pl ー BBS のログファイルを保存する 3 : $logrc = logrc ・ 5 : # history データを undump する 6 : open(logrc) ; 7 : while ( く logrc 〉 ) { $nextresno{"$l, $ 2 " } = $ 3 ; $logfile{"$1,$2"} = $ 4 ; 13 : close(logrc); 15 : # 入力ファイルを検索する 16 : while ( ◇ ) { # 記事へッダかどうかチェックする $newsgroup ニ $ 2 ; $articleno = $ 1 ; $article ニ $ if ( / 響 [ BASENOTE with}s + #d + Res \ ] $ / ) { $resno ニ 0 ; $resno = $ 1 ; } else { nex t : # 記事へッダだった pr i nt "Newsgroup $newsgroup, Art ic 1 e $art ic 1 eno, Res ponse $ resno do { 1 2 48 C MAGAZINE 1991 9
List 1 0 22 : } 31 : } 49 : } 52 : ) 13 : } 14 : 15 : 16 : 17 : 19 : 20 : 21 : 23 : 24 : 26 : 27 : 28 : 29 : 30 : 32 : 33 : 34 : 35 : 36 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 48 : 50 : 51 : 54 : 56 : 59 : 60 : 62 : 63 : 64 : 0 = 1 & & / 響 [ BASENOTE withC + [0ー9] + Res \ ] $ / { ニ 2 # ヘッダの 2 行目なので , つぎから記事本体 state ー resno state state resno state S tate ニ state state ー = $ 3 ニ 2 # ヘッダの 2 行目なので , つぎから記事本体 ニ 0 # ヘッダではなかった newsgro 叩 = substr($NF, 2 , length($NF)-2) articleno = $ 2 firstline ニ $ 0 state ニ 1 # 記事の 1 行目だった state printf "Newsgroup xs, Article ー” , newsgroup' articleno• resno # 必要ならこの記事を保存する if (((newsgroup, articleno) in nextresno) & & ニ resno)) { ()e xtres no [newsgroup, ar t ic leno) if (logfile[newsgroup, articleno) ! = lastlogfile) ' " ) close(lastlogfile) if (lastlogfile ト lastlogfile = logfile[newsgroup, articleno] print firstline 〉〉 lastlogfile # 2 行目は次のパタ ーンで保存される nextresnocnewsgroup, articleno]+ 十 print ” archived ” state ニ 3 # この記事を保存する } else { print ” skipped ” state = 4 # この記事は保存するしない state print 〉〉 lastlogfile 53 : /NOTES> 〉 [ + / { = 0 # この記事は終り state 57 : # history データを dump する 58 : END { for (indices in nextresno) { split(indices, ind, SUBSEP) printf "Xs#tXd#tXd*tXs*n" ind[l], ind[2],nextresno[indices], logfile[indices] 〉 logrc close(logrc) List 1 1 awkC 記述した logarch2. awk ( 少バターン型 ) 1 : # logarch2. awk ー BBS のログファイルを保存する ( 少パターン型 ) 19 : 18 : 17 : 14 : 12 : 11 : 10 : 9 : 8 : 7 : 6 : 5 : 2 : ニ 1 ) { article ニ $ 0 "}n ” articleno = $ 2 newsgroup = substr($NF, 2 , length($NF)-2) 15 : # 記事へッダかどうかチェックする close(logrc) logfileC$1,$2J = $ 4 nextresno[$l, $ 幻ニ $ 3 if (NF ニ 4 ) { while ((getline く logrc) logrc = —logrc 4 : BEGIN ( 3 : # history データを undump する 特集フレタ系言語研究 パターンがなく , すべての処理を逐次的に 書く点て、は , perl はむしろ C に似ている。 のほうが記述の自由度は高いが , 今回のよ うな応用て、は , どちらの書き方て、もあまり 開発効率は変わらないと思われる。 perl て、便利なのは , 正規表現の際に ( ) て、 指定した部分を , あとて、 $ 1 , $ 2 , ・・・など を読むことによって取り出すことがて、きる 点だ。 awk のように substr などを使う必要は ない。これによって開発の手間は awk より も省けたといえる。 連想配列が使えることについては awk と 実行速度 同様て、ある。 特集フィルタ系言語研究 47 今回は , 各スクリプトて , logrc の書き出 の量も比較の際に考慮する必要があるだろ データを処理する場合 , 使用されるメモリ 複数のファイルをオープンしながら大量の このような大きなスクリプトを使って , メモリ使用量 logarch2. awk のほうが速度的に有利なよう 今回のような応用には , awk らしくない めだと思われる。 についてマッチング条件がテストされたた 量のパターンを使ったために , すべての行 logarchl. awk はかなり遅くなっている。大 logarch2. awk もそれほど悪くはないが , 行時間は , C にまったくひけを取らないだろ perl. exe のロード時間を差し引いた純粋な実 このような複雑な処理て、は perl が速い 条件は Table 6 の条件と同じ ) 。 8 ( 50 頁参照 ) のとおりて、ある ( ほかの測定 ログを logarch に読み込ませた結果は , Table ある BBS にアクセスした際の 38K バイトの
List 33 : 34 : 35 : 36 : 38 : 39 : 40 : 41 : 42 : 45 : 46 : 47 : 48 : 49 : 50 : 51 : 52 : } 53 : 57 : 58 : 59 : 1 2 $article . ニ◇ ; } until (/NOTES 〉 >}s + /) ; $article . # 必要ならこの記事を保存する "$newsgroup, $articleno" ・ $indices 0 if ($logfile($indices} ne $lastlogfile) ( if (defined($lastlogfile)) { close(logfile); ) open(logfile, ”〉〉 $logfile{$indices}"); $lastlogfile ニ $logfile($indices); print logfile $article; $nextresno{$indices) + + ; pr i nt "arch ived}n" } else { print "skipped}n" ・ 54 : # history データを dump する 55 : open(logrc, " 〉 $ logrc ” ) ; 56 : foreach $indices (keys Xnextresno) { = split(/,/,$indices); ($newsgroup, $articleno) print logrc if (defined($nextresno{$indices}) & & $nextresno($indices) - = $resno) { '$newsgroup*t$articleno}t$nextresno{$indices)}t$logfile($indices)*n" ・ Fig. 3 loga 「 ch の入力ファイルの例 Table 7 スクリプトの長さ NOTES > > hideki JaWaTeX を意識してかどうか , JemTeX もバージョン 2 が出るそうです。 Bytes : 78 Date TitIe : emTeX 日本語化計画連絡ノート [ RESPONSE : 104 of 104 ] Note 270 MS-DOS (comp. msdos) 処理系 awk awk perl 9 : 55pm 4 / 26 / 91 Author : PCS29498 (hideki) ファイルサイズ 5979 バイト ( ソース ) 8682 バイト ( オプジェクト ) 1760 バイト 1457 バイト 1 551 バイト loga 「 ch. 可 logarch2. awk logarchl . awk logarch. exe logarch. c ファイル名 特集フレタ系言語研究 開発効率ははるかに高い。とくに perl は , C に劣らない実行速度が得られるほか , きわ めて機能が豊富なのて、 , このようなスクリ プトの作成には最適て、あろう。今まて、は , このような応用には awk を使うのが一般的 だったが , これからは perl の普及が予想され 関連ユーティリティ 0 特集フィルタ系言語研究 49 ファイルを作って解決することもてきるが , 上記のコマンドラインを実行するバッチ 起動て、きたほうが好ましい 通常のコマンドファイルて、あるかのように はり , スクリプトは , このようにあたかも 定したときに /usr/bin/sed が起動される。や 立てておけば , そのスクリプトの実行を指 などと書いてファイルの実行許可ビットを #!/usr/bin/sed 合に , スクリプトファイルの先頭に 多くの UNIX システムて、は , このような場 覚えなければならないのもつらい 系ごとに異なる起動オプションをユーザが らないのは非常にめんどうて、あるし , 処理 指定をキーポードから打ち込まなければな スクリプトを実行するたびにこのような なる。 タ系言語のインタブリタを起動することに ト名をパラメータとして指定して , フィル のように , コマンドラインから , スクリプ sed -f program. sed トを書くと , そのプログラムを起動する際 一般にフィルタ系言語を使ってスクリプ 起動させる畑 スクリプトをコマンドとし 紹介する。 にあたって便利なユーティリティをふたっ 最後に , DOS 上て、フィルタ系言語を使う