Emacsやnkfを使ってFTPのアスキーモードを再現してみる(バイナリモード -> アスキーモードへのファイル変換)

Emacsやnkfを使ってFTPのアスキーモードを再現してみる(バイナリモード -> アスキーモードへのファイル変換)

「index.cgiを実行っと。あれ?500 Internal Server Errorだ...」

FTPにはBinary modeとASCII modeがあります。この2つには微妙な違いがあって、またその違いによりCGIで冒頭の「500 Internal Server Error」が発生する原因になります。

この記事ではその辺の詳細解説と、Emacsでアスキーモードを利用する方法、またシェルのコマンドレベルでアスキーモードを利用する方法を解説します。正確にはバイナリモードで転送してしまってエラーが出るファイルをアスキーモードの形に変換するという話です。それでは本論に入ります。

スポンサーリンク
スポンサーリンク

FTPはバイナリモードで行うべき?

エンジニアの人は「FTPはバイナリモードで行っておけば問題ない」的な話しを先輩から聞いたことがあるかも知れません。そしてこれは概ね正しいのですが稀に問題が置きます。

昨今のFTPソフトは自動判定モードがありますのでそれを使った場合は問題ないかとは思いますが、あえてバイナリーモードを選んだり、FTPコマンドを使った場合などに問題が起こるケースがあります。それ以外にもcgiのzipファイルをローカルで解凍してからFTPで転送するのではなく、サーバーに転送してからzipを解凍した場合にも問題が発生する場合があります。

"500 Internal Server Error"

この問題というのはCGIの場合に起こります。htmlやcss、js、phpなどでは起こりません。起こるのは改行コードを解釈する場合です。

FTPのバイナリモードとアスキーモードの違い

ここでFTPのバイナリモードとアスキーモードの違いを見ておきます。「バイナリモードはファイルを何も変更せずに転送、アスキーモードはサーバー側の環境に文字コードや改行コードを合わせて転送」とざっくり思っている方もいるかもしれませんがこれは違います。

実際には

  文字コード 改行コード
バイナリモード そのまま そのまま
アスキーモード そのまま 転送先に合わせる

こういった形です。勘違いしやすいですが「文字コードは変更されません」。変更されるのは「改行コード」のみです。

WindowからLinuxサーバーへの転送

具体例で見てみます。Windowsで作ったファイルをLinuxサーバーに転送するケースです。

この場合まずWindowsで作ったテキストファイルの文字コードは「shift_jis」で、改行コードは「CRLF」です。このファイルをFTPのアスキーモードでLinuxサーバーに転送すると文字コードは「shift_jis」のままで改行コードのみ「LF」に変更されます。

これを表で表すと

  文字コード 改行コード
Windows shift_jis CRLF

--> FTPのアスキーモードでLinuxサーバーに転送

  文字コード 改行コード
Linuxサーバ shift_jis LF

となります。

MacからLinuxサーバーへの転送

同様にMacで作成したファイルのケースも見てみます。

  文字コード 改行コード
Mac utf-8 LF

--> FTPのアスキーモードでLinuxサーバーに転送

  文字コード 改行コード
Linuxサーバ utf-8 LF

となります。

現在のmacOSは、OSX時代からUnixベースになりました。なので基本的な部分ではLinuxと似ています。なおOSX以前のMacの場合は改行コードに「CR」を使うなど現在から見ると少し変則的です。

スポンサーリンク
スポンサーリンク

改行コードをCGIで解釈できないと

改行コードをCGIで解釈できない場合に「構文エラーとなり "500 Internal Server Error"」が発生します。そしてLinuxサーバーでは文字コードは"utf-8"か"euc-jp"の場合がありますが、改行コードは「LF」です。

ですのでより具体的な解説としては「CRLF」の改行コードを持つCGIファイルをLinuxサーバーで実行させた時にエラーが発生します。

もうひとつのCGIの文字コードの問題

改行コードだけでなく文字コードの問題もあります。

もしnkf等を使って下記のようにファイルを変換してしまうと

  文字コード 改行コード
元ファイル shift_jis CRLF
変換したファイル utf-8 LF

エラーにはならないものの「文字化け」が発生したりします。これは元ファイルが"shift_jis"だった為、下記の様にhtmlレベルで文字コードにshift_jisが指定してあるだろうからです。

<!-- html5  -->
<meta charset="shift_jis" />

<!-- html -->
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"/>

ですので改行コードだけでなく文字コードまで変更してしまうと問題が起きることがあります。この辺はCGIの作者がどこまで考慮しているかにも依りますね。

シェルで改行コードの確認

改行コードを確認する方法を解説します。いくつか方法があるかと思いますが "od" コマンドが手軽です。実行結果の末尾が "\r\n" であれば改行コードは「CRLF」、"\n" であれば「LF」です。下記に例を載せておきます。

% od -c ftp-ascii-test-sjis-CRLF.cgi
0000000    #   !   /   u   s   r   /   l   o   c   a   l   /   b   i   n
0000020    /   p   e   r   l  \r  \n  \r  \n   p   r   i   n   t       "
0000040    C   o   n   t   e   n   t   -   t   y   p   e   :       t   e
0000060    x   t   /   h   t   m   l   \   n   "   ;  \r  \n   p   r   i
0000100    n   t       "   \   n   "   ;  \r  \n   p   r   i   n   t
0000120    "   <   h   t   m   l   >   \   n   "   ;  \r  \n   p   r   i
0000140    n   t       "   <   h   e   a   d   >   \   n   "   ;  \r  \n
0000160    p   r   i   n   t       "   <   m   e   t   a       c   h   a
0000200    r   s   e   t   =   \   "   s   h   i   f   t   _   j   i   s
0000220    \   "   >   \   n   "   ;  \r  \n   p   r   i   n   t       "
0000240    <   t   i   t   l   e   > 203   e 203   X 203   g   <   /   t
0000260    i   t   l   e   >   \   n   "   ;  \r  \n   p   r   i   n   t
0000300        "   <   /   h   e   a   d   >   \   n   "   ;  \r  \n   p
0000320    r   i   n   t       "   <   b   o   d   y   >   \   n   "   ;
0000340   \r  \n   p   r   i   n   t       " 202 261 202 352 202 315   C
0000360    G   I 202    ̃  **   e 203   X 203   g 202   ł  ** 267 201   B
0000400    \   n   "   ;  \r  \n   p   r   i   n   t       "   <   /   b
0000420    o   d   y   >   \   n   "   ;  \r  \n   p   r   i   n   t
0000440    "   <   /   h   t   m   l   >   \   n   "   ;  \r  \n  \r  \n
0000460

% od -c ftp-ascii-test-utf8-LF.cgi
0000000    #   !   /   u   s   r   /   l   o   c   a   l   /   b   i   n
0000020    /   p   e   r   l  \n  \n   p   r   i   n   t       "   C   o
0000040    n   t   e   n   t   -   t   y   p   e   :       t   e   x   t
0000060    /   h   t   m   l   \   n   "   ;  \n   p   r   i   n   t
0000100    "   \   n   "   ;  \n   p   r   i   n   t       "   <   h   t
0000120    m   l   >   \   n   "   ;  \n   p   r   i   n   t       "   <
0000140    h   e   a   d   >   \   n   "   ;  \n   p   r   i   n   t
0000160    "   <   m   e   t   a       c   h   a   r   s   e   t   =   \
0000200    "   U   T   F   -   8   \   "   >   \   n   "   ;  \n   p   r
0000220    i   n   t       "   <   t   i   t   l   e   >  テ  **  **  ス
0000240   **  **  ト  **  **   <   /   t   i   t   l   e   >   \   n   "
0000260    ;  \n   p   r   i   n   t       "   <   /   h   e   a   d   >
0000300    \   n   "   ;  \n   p   r   i   n   t       "   <   b   o   d
0000320    y   >   \   n   "   ;  \n   p   r   i   n   t       "  こ  **
0000340   **  れ  **  **  は  **  **   C   G   I  の  **  **  テ  **  **
0000360   ス  **  **  ト  **  **  で  **  **  す  **  **  。  **  **   \
0000400    n   "   ;  \n   p   r   i   n   t       "   <   /   b   o   d
0000420    y   >   \   n   "   ;  \n   p   r   i   n   t       "   <   /
0000440    h   t   m   l   >   \   n   "   ;  \n  \n
0000453

シェルからnkfで変換

次にFTPのバイナリモードで転送してしまったファイルを変換する方法を解説します。ASCIIモードで再度転送しなくても変換することが可能です。変換にはnkfを利用します。

文字コードは「shift_jis」で改行コードが「CRLF」のファイルを、文字コードを変えずに改行コードのみ「LF」にしてみます。

% nkf -Lu --overwrite file

or

% nkf -sLu --overwrite file

こんな感じで変換できます。パラメーターを解説すると、-s が「shift_jisで出力」、-Lu が「改行コードをUnix形式(LF)で出力」、--overwrite が「出力結果でファイルを上書き」です。

findと組み合わせれば一括置換も可能です。例えばこんな感じです。

% find . -iregex ".+\.cgi" -exec nkf -sLu --overwrite {} \;

or

% find . -iregex ".+\.cgi" | xargs nkf -sLu --overwrite
スポンサーリンク
スポンサーリンク

Emacsで改行コードの確認

同様の事をEmacsでもやってみます。

まず改行コードの確認は

M-x describe-coding-system (<f1> C) RET

or

M-x describe-current-coding-system

で行えます。

例えば文字コードが「シフトJIS」で改行コードが「CRLF」というWindowsで一般的なファイルであればこの様に表示されます。

Coding system for saving this buffer:
  S -- japanese-shift-jis-dos (alias: shift_jis-dos sjis-dos)

「japanese-shift-jis-dos」 の "japanese-shift-jis" 部分が文字コード、"dos"の部分が改行コードです。"dos"は「CRLF」を表します。

また文字コードが「utf-8」で改行コードが「LF」というLinuxやMacで一般的なファイルであればこの様に表示されます。

Coding system for saving this buffer:
  U -- utf-8-unix (alias: mule-utf-8-unix utf8-unix UTF8-unix UTF-8-unix)

同様に「utf-8-unix」の "utf-8" 部分が文字コード、"unix" 部分が改行コードです。"unix"は「LF」を表します。

Emacsで改行コードのみLFに変換

Emacsで改行コードのみ変更するには下記のように操作します。

C-x RET f (M-x set-buffer-file-coding-system) unix

変更したいファイルを開き、"C-x RET f" とタイプし "unix" とだけ入力します。これで文字コードはそのままで改行コードのみ変更されます。

ちなみに文字コードも変更したい時は同様の操作で

C-x RET f (M-x set-buffer-file-coding-system) utf-8-unix

といった感じで指定します。

[参考] EmacsでFTP

trampを使ったFTPの利用

ちなみにEmacsではtrampを使えばFTPも利用できます。ファイルを転送したり、サーバーにあるファイルを直接編集したりといった事が可能です。trampはそれなりに細かい設定も可能ですが今回はFTP部分を抜粋してみます。

;;------------------------------------------------------------------------
;; trampの設定
;; ------------------------------------------------------------------------
(require 'tramp)

;; FTPはPassiveモードを利用する
(setq ange-ftp-try-passive-mode t)

;; FTPのbinary/asciiモード
;; binary転送したいファイルを正規表現で指定
;; その他はアスキーモードで転送される
;; default: "" (ver24.1から変更された)
(setq ange-ftp-binary-file-name-regexp "^.*\\.\\(jpe?g\\|gif\\|png\\)$")

;; エラーが出る時は指定を変える
(setq dired-listing-switches "-alFh")

trampは裏でange-ftpを利用しているので、ange-ftpのパラメーターを変更することで「Binary/ASCII mode」の指定や「Active/Passive mode」の切り替えが可能です。

接続時にユーザー名とパスワードを毎回打っても可能ですが、それはあまり現実的ではない為認証情報を ~/.netrc で管理します。こんな感じで定義します。

machine         example.com
login           ftpusername
password        ftppass

なおhostsファイルを使えばmachine名に実際のホスト名ではなく任意の名称を使うことが可能です。

上記を設定した状態で "C-x C-f" (M-x find-file) 等を実行してミニバッファに下記の様にホスト名とサーバー上のファイル名やディレクトリ名を入れればOKです。EmacsからFTPでサーバー上のファイルを直接編集できます。

/ftp:example.com:/public_html/index.cgi

また同様の指定でdiredを使ったFTPでのファイル転送も可能です。

diredを使ってFTPクライアント化

ちなみにEmacsをよりFTPクライアントらしくする事も可能です。下記のパラメーターを追加します。

(setq dired-dwim-target t)

設定後 "C-x 2" (M-x split-window-below) や "C-x 3" (M-x split-window-right) などでウインドウを分割します。そしてそれぞれのウインドウで"M-x find-dired"などで「PCのディレクトリ」と「サーバのディレクトリ」を開きます。そうすると後は通常のdiredの操作でFTPクライアントの様にファイル転送が行えます。

emacs-dired-ftp.gif

例えばPC側のウインドウでファイルやディレクトリを選んで "C" を押して実行すれば、サーバー側にファイルやディレクトリを丸ごと転送可能です。ほとんどFTPクライアントライクな使い方が可能です。

なおtrampではftp以外にも、sshやscp、rsyncなども使えます。これはサーバー側のウインドウをどのメソッドで開いたかに依存します。例えば "C-x C-f /ssh:example.com:/..." とすればsshといった感じで。

一言

仕組みを理解すれば予想外のエラーにもスムーズに対応できます。それでは長くなりましたがこの辺で。

スポンサーリンク
スポンサーリンク