(Linuxコマンドの一覧はコチラ 中級者のためのLinuxコマンド入門)
何に使うかわからないrevコマンドですが、もっと活用する術はないかと、revコマンドのソースを確認してみました。
takk~$ apt source util-linux takk~$ cat -n util-linux-2.33.1/text-utils/rev.c 1 /*- 2 * Copyright (c) 1987, 1992 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Modified for Linux by Charles Hannum (mycroft@gnu.ai.mit.edu) 34 * and Brian Koehmstedt (bpk@gnu.ai.mit.edu) 35 * 36 * Wed Sep 14 22:26:00 1994: Patch from bjdouma <bjdouma@xs4all.nl> to handle 37 * last line that has no newline correctly. 38 * 3-Jun-1998: Patched by Nicolai Langfeldt to work better on Linux: 39 * Handle any-length-lines. Code copied from util-linux' setpwnam.c 40 * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL> 41 * added Native Language Support 42 * 1999-09-19 Bruno Haible <haible@clisp.cons.org> 43 * modified to work correctly in multi-byte locales 44 * July 2010 - Davidlohr Bueso <dave@gnu.org> 45 * Fixed memory leaks (including Linux signal handling) 46 * Added some memory allocation error handling 47 * Lowered the default buffer size to 256, instead of 512 bytes 48 * Changed tab indentation to 8 chars for better reading the code 49 */ 50 51 #include <stdarg.h> 52 #include <sys/types.h> 53 #include <errno.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <signal.h> 59 #include <getopt.h> 60 61 #include "nls.h" 62 #include "xalloc.h" 63 #include "widechar.h" 64 #include "c.h" 65 #include "closestream.h" 66 67 static void sig_handler(int signo __attribute__ ((__unused__))) 68 { 69 _exit(EXIT_SUCCESS); 70 } 71 72 static void __attribute__((__noreturn__)) usage(void) 73 { 74 FILE *out = stdout; 75 fprintf(out, _("Usage: %s [options] [file ...]\n"), 76 program_invocation_short_name); 77 78 fputs(USAGE_SEPARATOR, out); 79 fputs(_("Reverse lines characterwise.\n"), out); 80 81 fputs(USAGE_OPTIONS, out); 82 printf(USAGE_HELP_OPTIONS(16)); 83 printf(USAGE_MAN_TAIL("rev(1)")); 84 85 exit(EXIT_SUCCESS); 86 } 87 88 static void reverse_str(wchar_t *str, size_t n) 89 { 90 size_t i; 91 92 for (i = 0; i < n / 2; ++i) { 93 wchar_t tmp = str[i]; 94 str[i] = str[n - 1 - i]; 95 str[n - 1 - i] = tmp; 96 } 97 } 98 99 int main(int argc, char *argv[]) 100 { 101 char const *filename = "stdin"; 102 wchar_t *buf; 103 size_t len, bufsiz = BUFSIZ; 104 FILE *fp = stdin; 105 int ch, rval = EXIT_SUCCESS; 106 107 static const struct option longopts[] = { 108 { "version", no_argument, NULL, 'V' }, 109 { "help", no_argument, NULL, 'h' }, 110 { NULL, 0, NULL, 0 } 111 }; 112 113 setlocale(LC_ALL, ""); 114 bindtextdomain(PACKAGE, LOCALEDIR); 115 textdomain(PACKAGE); 116 atexit(close_stdout); 117 118 signal(SIGINT, sig_handler); 119 signal(SIGTERM, sig_handler); 120 121 while ((ch = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1) 122 switch(ch) { 123 case 'V': 124 printf(UTIL_LINUX_VERSION); 125 exit(EXIT_SUCCESS); 126 case 'h': 127 usage(); 128 default: 129 errtryhelp(EXIT_FAILURE); 130 } 131 132 argc -= optind; 133 argv += optind; 134 135 buf = xmalloc(bufsiz * sizeof(wchar_t)); 136 137 do { 138 if (*argv) { 139 if ((fp = fopen(*argv, "r")) == NULL) { 140 warn(_("cannot open %s"), *argv ); 141 rval = EXIT_FAILURE; 142 ++argv; 143 continue; 144 } 145 filename = *argv++; 146 } 147 148 while (fgetws(buf, bufsiz, fp)) { 149 len = wcslen(buf); 150 151 if (len == 0) 152 continue; 153 154 /* This is my hack from setpwnam.c -janl */ 155 while (buf[len-1] != '\n' && !feof(fp)) { 156 /* Extend input buffer if it failed getting the whole line */ 157 /* So now we double the buffer size */ 158 bufsiz *= 2; 159 160 buf = xrealloc(buf, bufsiz * sizeof(wchar_t)); 161 162 /* And fill the rest of the buffer */ 163 if (!fgetws(&buf[len], bufsiz/2, fp)) 164 break; 165 166 len = wcslen(buf); 167 } 168 if (buf[len - 1] == '\n') 169 buf[len--] = '\0'; 170 reverse_str(buf, len); 171 fputws(buf, stdout); 172 } 173 if (ferror(fp)) { 174 warn("%s", filename); 175 rval = EXIT_FAILURE; 176 } 177 fclose(fp); 178 } while(*argv); 179 180 free(buf); 181 return rval; 182 } 183 takk~$
102行目のwchar_t *bufのbufという変数に、
135行目と160行目でxmalloc/xreallocでメモリを確保し、
148行目のfgetwsにて文字列を読み込んで、
170行目のreverse_strにて左右反転させています。
reverse_strでは一文字ずつの反転になっているので、日本語でも正しくひっくり返るというわけですね。
また、このrev.cのソースを読んで分かるのが、fgetwsを使うためロケールでの使用文字かどうか確認できる点です。
LANGの設定を変更してrevの結果を確認してみます。
takk~$ echo $LANG ja_JP.UTF8 takk~$ echo あいうえお > a.txt takk~$ rev a.txt おえういあ takk~$ LANG=ja_JP.SJIS takk~$ rev a.txt rev: a.txt: Invalid or incomplete multibyte or wide character takk~$
予想通り左右反転させようとしている文字列がLANGと食い違うため、エラーが発生しました。
次は、LANGの方はUTF8のままで、a.txtをSJISにして確認してみましょう。
takk~$ LANG=ja_JP.UTF8 takk~$ echo あいうえお | nkf -s > b.txt takk~$ nkf --guess b.txt Shift_JIS (LF) takk~$ rev b.txt rev: b.txt: Invalid or incomplete multibyte or wide character takk~$
こちらも予想通りですが、現在のLANGと異なるためエラーとなりました。
では、UTF8のテキストに文字ではないコードを入れたらどうなるでしょうか。
takk~$ echo -e "あいうえ\x80お" > c.txt takk~$ nkf --guess c.txt EUC-JP takk~$ cat c.txt あいうえ?お takk~$ rev c.txt rev: c.txt: 無効または不完全なマルチバイトまたはワイド文字です
revコマンドではエラーとなりました。つまり、revコマンドを使えば、壊れた日本語(ワイド文字)テキストのチェックに使えることになります。
他の数値を埋め込んで確認してみます。
takk~$ echo -e "あいうえ\x11お" > d.txt takk~$ rev d.txt あ?えういあ
さきほど0x80を埋め込んだ時はエラーになったのに、0x11ではエラーになりませんでした。
それもそのはず。0x11が、改行コードやタブも含まれている制御コードの範囲0x00〜0x1Fに含まれているからです。
逆にこのコードが含まれているからといってエラーを返してはなりません。
さらにASCIIコードが0x00〜0x7Fですので、同様にrev実行時にエラーを返してはなりません。
revで、文字コードチェックに使えることは分かりましたが、実際どのように使えば良いでしょうか。
revの結果でif文分岐できるのが理想でしょう。
しかしrevはエラー発生時、標準エラー出力をしてしまいますので、このままでは出力が邪魔で使えません。
加えて、標準出力の方も左右反転文字を使うわけではないため不要です。標準出力も標準エラー出力も非表示にする必要があります。
と言っても、邪魔な出力をしないようにすることは簡単です。
出力先を画面ではなく、ファイルにしてしまえば良いからです。
まず、標準出力を画面に出力しないようにするには、
rev a.txt > trash.txt
分かりやすくtrash(ゴミ箱)というファイル名にしました。
では標準エラー出力も同じ要領でファイルにリダイレクトします。標準エラー出力をリダイレクトするには、2>という記述をします。
rev a.txt 2> trash.txt
次に、標準出力と、標準エラー出力、両者を合わせてリダイレクトします。
rev a.txt > trash.txt 2> trash.txt
一応これで画面上には表示されなくなりますが、trash.txtというファイルが作られてしまいます。
回りくどくなりましたが、実はこのようなテンポラリファイルは必要ありません。データをただ捨てたい場合は、スペシャルファイルを使います。
/dev/nullは、リダイレクトすると何でも捨てられてしまうスペシャルファイルです。これを使ってみます。
rev a.txt > /dev/null 2> /dev/null
少しすっきりしました。
さあ、revをif分岐に使ってみましょう。確認のための関数を作成します。
takk~$ f(){ > if rev $1 > /dev/null 2> /dev/null;then > echo OK; > else > echo NG; > fi > }
さきほど作成したファイル、a.txtが正しい日本語のファイル、c.txtが異常ファイルでした。念の為確認してみます。
takk~$ rev a.txt おえういあ takk~$ rev c.txt rev: c.txt: 無効または不完全なマルチバイトまたはワイド文字です takk~$
ではこれらのファイルを使って先ほどの関数のチェックをしてみます。
takk~$ f a.txt OK takk~$ f c.txt NG
上手く分岐されたようです。
まだいろいろ確認することはありそうですが、revを使って日本語ファイルのチェックができそうな気がします。revの使い道が一つ増えましたね。
ちなみに、スペシャルファイルへリダイレクトする書き方として、
> /dev/null 2> /dev/null を、
1> /dev/null 2> /dev/null としても、
1> /dev/null 2>&1 としても、結果は同じです。
コメント