シェルスクリプトは、エンコーディングや改行コードに敏感ですか?
質問
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 スタイルの改行コードを使用するようにファイルを変換することです。これを実現する方法はいくつもあります。
-
これは
dos2unix
というプログラムを使って行うことができます。dos2unix filename
-
ファイルを開くには できる のできるテキストエディタ (Sublime、Notepad++、Notepad ではありません) でファイルを開き、Unix 行終端でファイルを保存するように設定します。例えば Vim では、(再) 保存する前に次のコマンドを実行します。
:set fileformat=unix
-
もし、あなたがバージョン
sed
をサポートするユーティリティがあれば-i
または--in-place
オプション、例えば、GNUsed
であれば、以下のコマンドを実行することで、末尾のキャリッジリターンを取り除くことができます。sed -i 's/\r$//' filename
他のバージョンの
sed
を使えば、新しいファイルに書き込むために出力リダイレクトを使うことができます。リダイレクト先には必ず別のファイル名を使用してください(後で名前を変更することができます)。sed 's/\r$//' filename > filename.unix
-
同様に
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 がそのようなファイルをどのように解釈しようとするかを示すことは価値があると思います。
関連
-
[解決済み] シェルスクリプトでブール変数を宣言して使用するにはどうすればよいですか?
-
[解決済み] シェルコマンドの実行と出力のキャプチャ
-
[解決済み] シェルコマンドを実行しながらエコーする方法
-
[解決済み] を付けるべきでしょうか?(shebang)を付けるべきか、またどのような形で付けるべきか?
-
[解決済み] 整数の合計を1行に1つずつ表示するシェルコマンド?
-
[解決済み] 特殊なドル記号のシェル変数とは何ですか?
-
[解決済み] シェルで複数のコマンドを1行で実行する
-
[解決済み】シェルスクリプトでmongoのコマンドを実行するには?
-
[解決済み] bashのifブロックの中でboolean変数を評価する方法は?重複
-
[解決済み] SSH接続をチェックするbashスクリプトを作成するには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Bash スクリプトと /bin/bash^M: bad interpreter: そのようなファイルまたはディレクトリがない [重複] [重複
-
[解決済み] '\r': command not found - .bashrc / .bash_profile [duplicate].
-
[解決済み] bash sh - command not found [重複].
-
[解決済み] ./configure : /bin/sh^M : 不正なインタプリタ [重複].
-
[解決済み] env: bash そのようなファイルやディレクトリはありません [重複].
-
[解決済み] Bashでファイル名を一括変更する
-
[解決済み] シェルのワイルドカード文字展開を停止しますか?
-
[解決済み] ディレクトリ内のファイルに対して、ファイル名のみ(パスなし)をエコーします。
-
[解決済み] ファイルツリー図の作成に使用するツールについて【終了しました
-
[解決済み] BashやShellスクリプトで関数の前方宣言を行うには?