1. 改行を含む文字列を渡したい
Pythonスクリプトを作成しています。
このスクリプトはコマンドライン引数からテキストを受取りprintする。このテキストは改行「\n」を含めることが可能で、print時に改行して印字したい。
この簡単な要件に対応することに結構苦労しました。
せっかくなので苦労したこと、調べたこと、理解したことを共有します。
下記の順でご説明します。
-
失敗例
-
Pythonの問題ではない
-
本当はshellの問題
-
改行を含む文字列を渡す方法 on bash
-
問題を解決するまでに右往左往した経緯
ちなみに私はUbuntuのbashで処理を実行しています。
2. 失敗例
簡単なPythonスクリプトを作成しました。
import sys
args = sys.argv
print(args[1])
実際に改行を含む文字列を指定して実行してみます。
$ python sample.py "aaa\nbbb" aaa\nbbb
改行されず\nと印字されてしまいました。
3. Pythonの問題ではない
色々調べてPythonの問題では無いことが分かりました。
情報源はこちらです。
This is really a shell question since the shell does all the command parsing. Python doesn’t care what’s happening with that and only gets what comes through in the exec system call. If you’re using bash, it doesn’t do certain kinds of escaping between double quotes. If you want things like \n, \t, or \xnn to be escaped, the following syntax is a bash extension:
問題はPythonではありません。
ここで大事なのは
問題を解決するためにPythonのプログラムで対応しようとしないこと
です。
私はプログラムで対応しようとして少しはまってしまいました。
4. 本当はshellの問題
コマンドライン引数をpythonに渡すのはshellの役割。Pythonは渡された引数を処理するだけです。
shellが引数をどのように処理するか?
これが本当の問題です。
5. 改行を含む文字列を渡す方法 on bash
下記のようにすることで改行を渡すことができます。
$ python sample.py $'aaa\nbbb' aaa bbb
この対応法については上記の情報源を参考にしております。
python test.py $'thing1\nthing2' You can also do: python test.py "thing1 thing2"
6. 問題を解決するまでに右往左往した経緯
情報収集してPythonのプログラムで対応する方法を見つけました。
結果としては失敗してしまいます。
本来の問題ではないPythonで対応しようとしたことが失敗の原因です。
6.1. Pythonのプログラムで対応しようとして失敗
参考とした情報が下記。
引数をエンコードしてデコードするとエスケープを削除できるようです。
試してみると一見問題なく処理できているようです。
import sys
args = sys.argv
print(args[1].encode().decode('unicode-escape'))
$ python sample2.py "aaa\nbbb" aaa bbb
ここでマルチバイト文字列を引数として与えてみます。
$ python sample2.py "あああ\nいいい" ããã ã ã ã
マルチバイト文字列が文字化けしてしまいました。
Pythonが本当の問題ではありません。Python内で対応しようとしても根本的には解決できません。
成功例のsample.pyがマルチバイト文字列でも問題ないことを検証します。
$ python sample.py $'あああ\nいいい' あああ いいい
shellの問題。shellでの引数の渡し方を正すことで対応できました。
7. まとめ
今回はPythonスクリプトに改行を含む文字列を引数として指定するという簡単な要件なのに対応に苦労したことを紹介しました。
実際にはPythonスクリプトの仕様やPythonの文字列の取扱いなどの情報を色々と調査したので今回の課題を解決するまでに相当の時間を要しました。
もし私と同じような課題を抱えている方がいらっしゃるとしたら、問題解決までの時間を短くすることができれば幸いです。
今回は以上です。