1. ホーム
  2. bash

シェルスクリプトは、エンコーディングや改行コードに敏感ですか?

2023-11-16 10:10:45

質問

MacでNW.jsのアプリを作っているのですが、アイコンをダブルクリックして開発モードでアプリを動かしたいのです。まず最初に、私のシェルスクリプトを動作させようとしています。

WindowsでVSCodeを使って(時間を稼ぎたかったので)。 run-nw ファイルをプロジェクトのルートに作成し、これを含んでいます。

#!/bin/bash

cd "src"
npm install

cd ..
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &

と入力しても、このような出力になります。

$ sh ./run-nw

: command not found  
: No such file or directory  
: command not found  
: No such file or directory  

Usage: npm <command>

where <command> is one of:  (snip commands list)

(snip npm help)

[email protected] /usr/local/lib/node_modules/npm  
: command not found  
: No such file or directory  
: command not found

本当に理解できない。

  • は、空行をコマンドとして受け取っているようです。私のエディタ(VSCode)で \r\n\n (万が一 \r が問題を起こす場合)、何も変わりません。
  • の有無にかかわらず、フォルダが見つからないようです。 dirname 命令の有無に関わらず) フォルダを見つけられないか、あるいは cd コマンドを知らないのでしょうか?
  • を理解できていないようです。 install への引数 npm
  • を実行しても、アプリが実行されることです。 npm install を手動で実行した場合)...

正しく動作させることができず、ファイル自体が何か変だと思ったので、今度は vim を使用して Mac で直接新しいものを作成しました。まったく同じ手順を入力すると、...今度は何の問題もなく動作しました。

2 つのファイルの diff は、まったくゼロの違いを明らかにします。

何が違うのでしょうか?何が最初のスクリプトを動作しないようにすることができますか?どうすればそれを知ることができるでしょうか。

更新

受付回答者の推奨に従い、間違った改行が復活した後、複数のことを確認しました。その結果、私がコピーした ~/.gitconfig をWindowsマシンからコピーしたため autocrlf=true になっていたので、Windows で bash ファイルを修正するたびに、行末を \r\n .

そのため dos2unix (の実行(MacではHomebrewを使ってインストールする必要があります)に加えて、Gitを使っている場合は、設定を確認してください。

どのように解決するのですか?

はい、Bashスクリプトです。 であり、スクリプト自身と処理するデータの両方で行末を意識します。つまり、各行はラインフィード文字 (10進数10、ASCIIの16進数0A) で終了する必要があります。

スクリプト内の DOS/Windows 行終端記号

WindowsまたはDOS形式の改行では、各行はキャリッジリターンとラインフィード文字で終了します。の出力では、この目に見えない文字を見ることができます。 cat -v yourfile :

$ cat -v yourfile
#!/bin/bash^M
^M
cd "src"^M
npm install^M
^M
cd ..^M
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M

この場合、キャリッジリターン( ^M をキャレット表記にしたり \r は C のエスケープ表記) は空白文字として扱われません。 Bashはshebangの後の最初の行(キャリッジリターン文字1つからなる)を実行するコマンド/プログラム名として解釈します。

  • という名前のコマンドは存在しないので ^M と表示されます。 : command not found
  • という名前のディレクトリは存在しないので "src"^M (というディレクトリはありません(もしくは src^M ) の場合、次のように表示されます。 : No such file or directory
  • それは install^M の代わりに install を引数として npm の引数として使用することで npm に文句を言わせる。

入力データにおける DOS/Windows の改行コード

上記のように、キャリッジリターンを含む入力ファイルがある場合。

hello^M
world^M

と記述すると、エディタや画面への書き込みでは全く正常に見えますが、ツールではおかしな結果になる場合があります。例えば grep は明らかにそこにある行を見つけるのに失敗します。

$ grep 'hello$' file.txt || grep -x "hello" file.txt
(no match because the line actually ends in ^M)

追加されたテキストは、キャリッジリターンがカーソルを行頭に移動させるため、代わりにその行を上書きします。

$ sed -e 's/$/!/' file.txt
!ello
!orld

画面への書き込み時に文字列が同じに見えても、文字列の比較に失敗しているように見える。

$ a="hello"; read b < file.txt
$ if [[ "$a" = "$b" ]]
  then echo "Variables are equal."
  else echo "Sorry, $a is not equal to $b"
  fi

Sorry, hello is not equal to hello

ソリューション

解決策は、Unix スタイルの改行コードを使用するようにファイルを変換することです。これを実現する方法はいくつもあります。

  1. これは dos2unix というプログラムを使って行うことができます。

    dos2unix filename
    
    
  2. ファイルを開くには できる のできるテキストエディタ (Sublime、Notepad++、Notepad ではありません) でファイルを開き、Unix 行終端でファイルを保存するように設定します。例えば Vim では、(再) 保存する前に次のコマンドを実行します。

    :set fileformat=unix
    
    
  3. もし、あなたがバージョン sed をサポートするユーティリティがあれば -i または --in-place オプション、例えば、GNU sed であれば、以下のコマンドを実行することで、末尾のキャリッジリターンを取り除くことができます。

    sed -i 's/\r$//' filename
    
    

    他のバージョンの sed を使えば、新しいファイルに書き込むために出力リダイレクトを使うことができます。リダイレクト先には必ず別のファイル名を使用してください(後で名前を変更することができます)。

    sed 's/\r$//' filename > filename.unix
    
    
  4. 同様に tr 翻訳フィルタは、入力から不要な文字を削除するために使うことができます。

    tr -d '\r' <filename >filename.unix
    
    

Cygwin バッシュ

Cygwin 用の Bash の移植版では、カスタムの igncr オプションがあり、行末のキャリッジ リターンを無視するように設定できます (おそらく、ユーザーの多くはテキスト ファイルを編集するためにネイティブの Windows プログラムを使用しているため)。 これを有効にすると 現在の を実行することで、現在のシェルで有効にすることができます。 set -o igncr .

このオプションを設定すると 現在の シェルプロセスのみに適用されるので、次のような場合に便利です。 ソーシング という環境変数を設定することで、余計なキャリッジリターンがあるファイルをソースするときに便利です。 DOS 行終端を持つシェルスクリプトに定期的に遭遇し、 このオプションを永久に設定したい場合は、環境変数 SHELLOPTS (すべて大文字) を含めるようにします。 igncr . この環境変数は、Bashが起動時に(起動ファイルを読み込む前に)シェルオプションを設定するために使用されます。

便利なユーティリティ

この file ユーティリティは、テキストファイルの中でどの改行コードが使われているかを素早く確認するのに便利です。以下は、各ファイル タイプについて表示される内容です。

  • Unix の改行コード。 Bourne-Again shell script, ASCII text executable
  • Mac の改行コード。 Bourne-Again shell script, ASCII text executable, with CR line terminators
  • DOS の改行コード。 Bourne-Again shell script, ASCII text executable, with CRLF line terminators

GNU 版の cat ユーティリティには -v, --show-nonprinting オプションがあり、印刷されない文字を表示します。

dos2unix ユーティリティは、特にテキストファイルを Unix、Mac、DOS の行末の間で変換するために書かれています。

便利なリンク

ウィキペディアには 優れた記事 に、テキスト行の終わりをマークする多くの異なる方法、そのようなエンコーディングの歴史、そして異なるオペレーティングシステム、プログラミング言語、インターネットプロトコル (例: FTP) で改行がどのように扱われるかを網羅した、優れた記事があります。

Mac OS の古典的な改行コードを持つファイル

クラシックな Mac OS (OS X 以前) では、各行はキャリッジ リターン (10 進数 13、ASCII の 16 進数 0D) で終了していました。このような行終端を持つスクリプト ファイルが保存された場合、Bash にはこのような長い行が 1 行だけ表示されます。

#!/bin/bash^M^Mcd "src"^Mnpm install^M^Mcd ..^M./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M

この一本の長い行は八分音符で始まるので ( # ) で始まるため、Bash はこの行 (およびファイル全体) を 1 つのコメントとして扱います。

注意: 2001 年、Apple は BSD 由来の NeXTSTEP オペレーティング システムをベースとしたものです。その結果、OS XでもUnixスタイルのLFのみの改行が使われるようになり、それ以来、CRで終わるテキストファイルは非常に珍しくなっています。それでも、Bash がそのようなファイルをどのように解釈しようとするかを示すことは価値があると思います。