星期六, 2月 10, 2007

shell script裡使用python的問題

lloyd大大教了我一招: 
把python code寫在環境變數裡面,再用python -c "$code"

不過現在換我解決他的問題了,
他的問題其實說起來很簡單,
就是如何在shell裡不換行透過pipe符號跟python程式溝通?
(把python當unix的tools來用的意思,大部分unix tools都支援stdin入stdout出)

簡單舉例的話就是比如去讀取/etc/passwd裡root的預設shell,
(只是舉例, 當然我們都知道可以直接open('/etc/passwd').read()而不必透過cat,
主要只是為了凸顯程式裡要有stdin跟stdout的使用,
重點是, 環境裡大部分的程式邏輯都是bash寫的, 只有少部份需要使用到python,
所以也不想另外寫.py檔案)

這已經是我能想到最簡單而且還具有可讀性的解法了:

透過python的sys.stdin跟print

cat /etc/passwd | python -c "import sys;print [line for line in sys.stdin if 'root:*' in line][0].split(':')[6].strip()"

re版本:

cat /etc/passwd | python -c "import re,sys;print [line for line in sys.stdin if re.search('^root:\*',line)][0].split(':')[6].strip()"

果然, 要'bash為體,python為用' 還是會遇到幾個小問題:

1.regex 沒內建在python語言(而是在標準庫內). 會不想用.
2.為了少寫那個分行要用的 \ 跟 """ ,不分行方法的python不得不使用比較不清晰的方式.
(這當然是相對於ruby來說)
3.而不換行, 會有一些語法不能用, 因為在python語法裡, 換行跟縮排是有意義的.

ruby有內建的全域變數$stdin, 這是相對應ruby版本中的一種:
cat /etc/passwd | ruby -e 'print $stdin.readlines().grep(/^root:\*/)[0].split(":")[6]'

perl...也有很多寫法,我這個寫法基本上已經接近像是sed的用法了:
cat /etc/passwd | perl -aF: -ne 'print "$F[6]" if /^root:\*/' | head -n 1

恩... 果然...
python真的不是拿來這樣用的 :P
我的理想解答其實是: "放棄shell script,改用python去呼叫shell吧" :P

4 則留言:

Unknown 提到...

果然python还是不能用来写shell中的tool吗?

经典的pipe方式是多么的好啊,把自己用python写的小工具和sed,grep一起用多么有吸引力...

Unknown 提到...

囧掉了,刚才试了一下发现直接用"python foobar.py"就可以,在foobar.py里面用sys.stdin和sys.stdout,插到一堆pipe中间也是没有问题的

使徒提姆 !? 提到...

"""
重點是, 環境裡大部分的程式邏輯都是bash寫的, 只有少部份需要使用到python,
所以也不想另外寫.py檔案"""""""
你可能誤會我的意思了, 這篇的重點是不另外寫個獨立的.py檔, 如果是另外寫.py當然是可以. 單純寫個python script並使用pipe沒有任何問題.

Unknown 提到...

哦,原来如此,确实是搞错了,我原来是抱着以python写unix shell tool的目的Google的,然后就找到这里了:)