Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

Pythonでシェルコマンドを実行する

この記事はモバイルファクトリー Advent Calendar 2018の24日目の記事です。


はじめに

メリークリスマス! エンジニアのid:Carimaticsです。

突然ですが、シェルスクリプトは便利な言語ですね。
Unix系OSであればほぼ標準で利用でき、シェルスクリプトにより移植性の高いコードを記述することができます。
また、シェルコマンド自体も強力なものが数多く用意されており、処理を簡潔に表現することも可能です。

一方で、シェルスクリプトは普段から慣れ親しんでいなければ読み書きが難しい言語でもあります。1
というのも、最近久しぶりにシェルスクリプトを書いたのですが、書き方に迷う場面が多々あったのです。
めったに使わない構文であれば仕方ないとも思えるのですが、if文やfor文を書こうとするたびに手が迷ってしまい、大変もどかしい思いをしました。
また、シェルスクリプトの規模が大きくなると、保守性も困難になりがちです。

ということで、Pythonをbetter shell scriptとして利用できると楽そうだと考えました。
Pythonであれば比較的読み書きしやすい構文で書くことができます。
他にもRubyやPerlなどがある中でどうしてPythonなのかと言うと、これは趣味です。

シェルコマンドを軽くラップできれば漸進的にスクリプトを改善していけると考えたため、本記事ではとりあえずシェルコマンドを簡単に叩けるようになるまでを目標にしました。

動作確認環境

  • Python 3.7.1

方針

  • Pythonの標準ライブラリに含まれるもののみを利用する
  • ひとまず3.x系のみ対応する

Pythonでシェルコマンドを実行する

簡単な方法

シェルコマンドを実行すること自体は簡単で、 subprocess モジュールを利用することでシェルコマンドを子プロセスとして実行できます。
subprocess モジュールでは、サブプロセスの実行は run() 関数を利用するのが推奨されています。

import subprocess

subprocess.run('ls')

注意点として、 run() で引数に文字列を渡す場合、引数なしで実行されるプログラムでなければなりません。
例えば、以下のように、コマンド引数をしているとエラーになります。

import subprocess

subprocess.run('ls tmp')

実行すると以下のエラーが出ます。

FileNotFoundError: [Errno 2] No such file or directory: 'ls tmp': 'ls tmp'

ls tmp というプログラムを実行しようとして失敗しています。

シェルコマンドに引数を渡す場合には、 run() にトークン区切りのリストを渡す必要があります。

import subprocess

subprocess.run(['ls', 'tmp'])

これで ls tmp を実行することができます。 このような場合、 shlex モジュールを用いて、コマンド文字列をリスト化できます。

import shlex
import subprocess

cmd = 'ls tmp'
tokens = shlex.split(cmd) # => ['ls', 'tmp']
subprocess.run(tokens)

トークン区切りにする以外には、 run() に対して shell=True を渡すことで、シェルコマンド文字列をそのまま実行することができます。

import subprocess

subprocess.run('ls tmp', shell=True)

しかし、この場合はシェルを実際に呼び出すことになるため、シェルインジェクションが発生する可能性があるので注意が必要です。
例えば、以下のような攻撃が発生し得ます。

import subprocess

def ls(dir):
    cmd = f"ls {dir}".
    subprocess.run(cmd, shell=True)

ls('; rm -fr tmp')

これは、シェルで ls ; rm -fr tmp を実行することになり、tmpファイルを削除されてしまいます。 shell=True を利用する場合は、外部からの入力には十分に注意しましょう。

パイプを利用する

shell=True する方法

shell=True することで、パイプを利用することは可能です。 ただし、前述の通りシェルインジェクションが発生する可能性があるため、利用には十分ご注意ください。

import subprocess

def ls(dir, filter):
    cmd = f"ls {dir} | grep {filter}"
    subprocess.run(cmd, shell=True)

ls('tmp', '.md')

shell=True しない方法

そこで、 shell=True を利用せずにパイプを利用するには、 subporcess.Popen を利用します。
上記と同等の結果を得るには、以下の用に書きます。

import subprocess
import shlex

def ls(dir, filter):
    cmd_ls = f"ls {dir}"
    cmd_grep = f"grep {filter}"
    p1 = subprocess.Popen(shlex.split(cmd_ls), stdout=subprocess.PIPE)
    subprocess.Popen(shlex.split(cmd_grep), stdin=p1.stdout)
    p1.stdout.close()

ls('tmp', '.md')

かなり冗長になってしまいました。
Popen を利用する場合は、使いやすくスクリプトを整えてあげるか、セキュリティリスクを考慮した上で shell=True の利用を検討しても良いかも知れません。

まとめ

かなり駆け足になってしまいましたが、Pythonでシェルコマンドを利用する方法について簡単に紹介しました。
subprocess を利用する以外にも、 shutilospathlibglob などのモジュールを組み合わせることで、よりbetter shell scriptらしい可読性の高いコードが書けるようになりそうです。

最後までお読みいただきありがとうございました。
それでは良いお年を!


  1. もちろんシェルスクリプトに限った話ではありませんが、その傾向が強い言語だと思っています。

初めての新人育成から学んだこと

モバイルファクトリー Advent Calendar 2018 最終日25日目担当の id:tsukumaru です。


はじめに

私は今年の8月くらいからチームの新卒育成のポジションを任されているのですが、育成に関する経験はこれが初めてでこの4ヶ月間さまざまな学びがありました。

1on1においてメンティーから話を引き出すことは重要ですが、メンター側からの問いかけ方次第で、引き出せる話に違いがあることに気づきました。 ここでは、その気づきについて書きたいと思います。

※メンティーには内容の公開許可をもらっています。

学んだこと

大丈夫そう?は使わない

後輩「〇〇のときどうすればいいかわからないです」
自分「こうすればいいかもね」
後輩「なるほど」
自分「これで大丈夫そう?」
後輩「大丈夫そうです」

私が1on1でメンティーになにかをティーチングしたり、目標の進捗について確認した後、「これで大丈夫そう?」とよく聞いていました。伝えたことに対してわかったかどうかを確認するためです。

するとだいたい「大丈夫そうです」と返って来ます。それで本当に大丈夫ならいいのですが、私の経験では次の週になっても同じ悩みを抱えていることがありました。

「これでいい?」という類のYes/Noな質問を迫られた時、Yesと答えてしまう人は多いと思います。これには「この人が言うなら」とか「せっかく教えてくれたし」のような気持ちがあったり、本当に自分では問題ないと思ってYesと答えている場合があります。前者は新卒社員が3年目の先輩から言われればなおさらですし、後者はメンティーの見えてない部分をメンター側からティーチングしてあげる必要があると思います。

どちらにせよ、メンター側がメンティーの気持ちを汲み取ってあげることが大事になってきます。

よりよいやり方

後輩「〇〇のときどうすればいいかわからないです」
自分「こうすればいいかもね」
後輩「なるほど」
自分「次〇〇なことが起こったらどうする?」
後輩「△△してxxしてみます」
自分「よさそう」

伝えたことを実践するための具体的なプロセスを聞くといいです。曖昧な部分を曖昧にせず、メンティーに具体化させるようにすると、メンティーとしては次やることが自然と見えてきますし、メンターとしても伝わったことが確認できます。

もし実践できていなくても、「△△まではできた?」など問題をさらに分解していけます。

やることは指示するのではなく聞き出す

自分「今回話したことから、来週は〇〇と△△をやっていこう」
後輩「はい、わかりました」
..
自分「先週話したことの進捗どう?」
後輩「まだできてないです」

1on1をして最後に次やることを確認する際、メンター側が主導してしまったことが多々ありました。

メンター側としては1on1を仕切って後輩を指導している気持ちになり満足してしまいます。メンティー側としてもその場ではわかったつもりになり「はい」と答えますが、きちんと理解できておらず実行に移せなくなってしまいます。

よりよいやり方

自分「今回話した次やることを確認してみよう」
後輩「〇〇をやってから△△をやります」
自分「がんばろう」
..
自分「先週話したことの進捗どう?」
後輩「〇〇まではできました」

前項の「大丈夫そう?」のときとほぼ同じですが、メンター側が仕切るのではなく、メンティー側に確認させるとよいです。自分でやることを言葉に出すことで、理解を促進させることができると思います。

自分は伝えたと思っていたけど、実際は相手に伝わっていなかったということは1on1ではけっこうあるので、常に意識していくといいと思います。

慣れない間は1on1の流れを形式化させる

自分「今週やったことは?」
後輩「〇〇をやりました」
自分「そこでわかったことは?」
後輩「△△という知見を得ました」
自分「なるほど。それを踏まえて次なにする?」
後輩「△△の知見を使ってxxな部分は効率化できそうなのでやってみます」

まだメンターとして1on1の経験がないとき、1on1をどう進行していけばわからないという状態になりました。私なりにメンティーの話の中で曖昧な部分があったら深掘りするということは意識していましたが、手探りで行なっている感じがいつまでも消えませんでした。

そこで見つけたのが、YWTという振り返りのフレームワークです。やったこと、わかったこと、次にすることの3つの頭文字をとってYWTとなっています。

https://hisa-magazine.net/blog/manabutikara/ywt/

このYWT、なにがすごいかというと、3つを順番に聞いていくことでメンティーに自然と内省を促すことができてしまいます。メンターとしても最初のうちはこのフレームワークにのっとり、3ステップで聞いていくと進行のリズムが掴みやすくなるかと思います。

YWTを導入するときはいきなりやり始めるのではなく、メンティーに「メンターとしてまだ1on1に慣れてないから、こういうフレームワークを使いたい」と合意をとった方がいいです。 私は勝手に導入してやり始めてしまったため、自分が期待しているような会話ができず、メンティーの内省につながらないことが多々ありました。

よりよい方法

後輩「〇〇ということがあって大変だったんですよ」
自分「へ〜、そこでなにか学べたことはある?」
後輩「△△ということは学びとしてありました。」
自分「その学びは次に生かしていけそうだね。具体的なイメージある?」

ずっと3ステップのままだと1on1が作業になってしまいますし、メンティー側の主体性(話したいこと)を奪ってしまう可能性もあります。慣れてきたらYWTを意識をするだけにしておき、何気ない会話の中で深掘りしたい時に使うようにします。

目標は自己評価基準で考えてもらう

自分「目標進捗どんな感じ?」
後輩「いまこんな感じです」
自分「あまり進捗よくないね。どこからやろうか」
後輩「これとかなら...」

私のメンティーは目標をあまり自分ごととして捉えられていませんでした。会社から言われたから目標設定をして、無理やり目標について考えている感じでした。

これは珍しいことではなく、実際私も新卒の頃は自分ごととして捉えられず、苦労して目標設定をしましたし業務中もあまり目標を意識できていませんでした。

自分ごととして捉えられていない原因として、私は「会社からどう評価されるか」を意識していることが原因だと考えています。

もちろん設定した目標の達成度を評価するのは上長ですし、会社に属している以上会社に対して貢献していくことは重要なことです。しかし今回のような場合、新卒メンバーにとって大事なのは目標をまず自分ごととして捉えてもらい、その次のステップとして会社を意識していくことが大事だと考えました。

よりよいやり方

自分「目標進捗どんな感じ?」
後輩「いまこんな感じです」
自分「自己評価はどのくらい?」
後輩「まぁまぁですね」
自分「その自己評価あげるにはどうすればいいと思う?」
後輩「これはまだできてないのでやってみようと思います」

目標の進捗を聞く時は「どうすれば達成できるか」ではなく「どうすれば自己評価があがりそうか」を聞くとよいです。 他者からの評価ではなく、「自分としての評価はどうか」を常に意識することで目標に対する意識を少しずつ自分に近づけていけると思います。

ただ、すぐに効果があるわけではないので、気長にサポートしていくことが大事です。

何度伝えても改善しない場合、自分の感情を伝える

後輩「本を読んでアウトプットするという目標を立てたので本を読みます」
自分「がんばろう」
..
自分「本どんな感じ?」
後輩「読めてないです。今週で少し読めるようにがんばります。」
..
自分「本どんな感じ?」
後輩「勉強会など忙しくて読めてないです。会社に朝早く来てよみます」
..
自分「本どんな感じ?」
後輩「読めてないです。今年中のアウトプットも厳しくなりました」
自分「そっか〜...」

今週までになにかをしよう等、1on1で伝えても実行されず、気づいたら査定の時期になっていたということがありました。

目標の変更や社内勉強会の時間で輪読会をすることも提案しましたが、自分で読みきりたいという気持ちを持っていたためそのまま継続する形になりました。

メンターとしていろいろサポートしてきたつもりでしたが、うまくサポートできず「約束したことを守れなかった」という状態になってしまったことがとても悔しかったです。

よりよいやり方(かもしれない)

自分の感情を素直に伝えてみましょう。 悲しい、悔しい、ショック、などなど。ある程度関係性ができている人から悲しいなどと言われれば、やらないとという気持ちになる可能性があります。

このやり方は「単純にやる気がなかった」という場合に効きそうなものであり、もしかしたら原因は他にあるかもしれません。深掘りにもう少し時間を使うべきだったとは感じています。

実際この方法で効果が見えたわけではなく、いまでもよりよいやり方を模索中です。

まとめ

育成担当として、1on1をするなかで学んだことをご紹介しました。これから育成に取り組む方、すでに取り組んでいる方のお役に少しでも立てたら幸いです。


モバイルファクトリー Advent Calendar 2018 を最後までお読みいただき、ありがとうございました! 🎉