subprocess.Popen(cmd)包含中文怎么办

在windows中通过subprocess调用cmd命令行,命令中包含中文是很令人头痛的事。
由于cmd控制台用的是gbk编码,而python用的是utf-8。utf-8的字符串,在gbk编码的控制台上运行,当然会运行不了。
假如再要你兼容繁体版的windows,此时更麻烦了。还好python提供了本地化接口
 
  • 本地化
>>> import locale
>>> locale.getdefaultlocale()
('zh_CN', 'cp936')
 
  • 示例
import locale
cmd = cmd.encode(locale.getdefaultlocale()[1])
subprocess.Popen(cmd)
 
净土大经科注2014 百度网盘地址

http://pan.baidu.com/s/1gfaHvwv

python获取自己的路径

exe路径

>>> sys.executable
'C:\\Python27\\python.exe'

lib路径

>>> sys.prefix
'C:\\python27'

 

windows上subprocess.Popen的参数close_fds=True与管道不能共存

  • 运行命令
subprocess.Popen(cmd, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
  • 出现错误
ValueError: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr
 
  • 原因
在windows上subprocess.Popen的参数close_fds=True与stdin/stdout/stderr不能共存
  1. close_fds=True表示子进程将不会继承父进程的输入、输出、错误管道。
  2. windows上不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)
 

python多行字符拼接使用小括号

* 多行字符串拼接使用小括号
 
s = ('select *'
     'from atable'
     'where id=888')
print s, type(s)

#输出
select *from atablewhere id=888 <type 'str'>
 
* python遇到未闭合的小括号,自动将多行拼接为一行,相比三个引号和换行符,这种方式不会把换行符、前导空格当作字符。
 

32位python的bug:os.system返回码一直为0

32位python在windows上调用命令行(os.system或subprocess.Popen)。执行后,如果返回码太大,python取得的返回值也是0。此时无法判断执行成功还是失败,这个是32位python的bug。
 
以时间同步命令w32tm位例子

在cmd上执行

C:\WINDOWS\system32>w32tm /resync
发生下列错误: 服务尚未启动。 (0x80070426)

C:\WINDOWS\system32>echo %errorlevel%
-2147023834

在64位python上执行

>>> os.system("w32tm /resync")
发生下列错误: 服务尚未启动。 (0x80070426)
-2147023834

在32位python上执行

>>> os.system("w32tm /resync")
发生下列错误: 服务尚未启动。 (0x80070426)
0
注意:此时命令执行错误的返回码也是0。
通常成功返回码才是0,这里执行错误,返回码却是也0。当要判断执行成功还失败时,这里便是个坑。
  • os.system文档
https://docs.python.org/2/library/os.html
 

apscheduler提示maximum错误

起因

在tornado中用apscheduler实现计划任务,出现错误提示
 
2015-12-04 19:10:22,227 - apscheduler.scheduler - WARNING - Execution of job "TaskHandle.progress_job (trigger: date[2015-12-04 19:10:22 CST], next run at: 2015-12-04 19:10:22 CST)" skipped: maximum number of running instances reached (1)

分析

缺乏资料,所以根据提示,分析apscheduler源码
  • 记录异常位置
在源码目录下,搜索关键字maxinum,找到记录异常的位置
#apscheduler/schedulers/base.py
if run_times:
    try:
        executor.submit_job(job, run_times)
    except MaxInstancesReachedError:
        self._logger.warning(
            'Execution of job "%s" skipped: maximum number of running instances reached (%d)',
            job, job.max_instances)
    except:
        self._logger.exception('Error submitting job "%s" to executor "%s"', job, job.executor)
  • 异常抛出位置
 
继续看submit_job函数,找到异常抛出位置
#apscheduler/executors/base.py
def submit_job(self, job, run_times):
    """
    Submits job for execution.

    :param Job job: job to execute
    :param list[datetime] run_times: list of datetimes specifying when the job should have been run
    :raises MaxInstancesReachedError: if the maximum number of allowed instances for this job has been reached
    """

    assert self._lock is not None, 'This executor has not been started yet'
    with self._lock:
        if self._instances[job.id] >= job.max_instances:
            raise MaxInstancesReachedError(job)

        self._do_submit_job(job, run_times)
        self._instances[job.id] += 1
  • _instances变量作用
在submit_job(提交任务)时加1,在_run_job_success(任务运行成功)时减1。 当self._instances[job.id]大于job.max_instances抛出异常。
max_instances默认值为1,它表示id相同的任务实例数。

解决

通过设置max_instances参数
sched.add_job(child_job, max_instances=10, trigger=DateTrigger(), id="123")

重现脚本

import time
import tornado.ioloop
from apscheduler.triggers.date import DateTrigger
from apscheduler.schedulers.tornado import TornadoScheduler

sched = TornadoScheduler()

def child_job():
    print "start"
    time.sleep(60)
    print "end"

def main_job():
    sched.add_job(child_job, trigger=DateTrigger(), id="123")

sched.add_job(main_job, 'interval', seconds=5)

sched.start()
tornado.ioloop.IOLoop.instance().start()
  • 输出
job_id: 7279209ab6c2498698f2117bb97e18a1, instances: 0, max_instances: 1
job_id: 123, instances: 0, max_instances: 1
start
job_id: 7279209ab6c2498698f2117bb97e18a1, instances: 0, max_instances: 1
job_id: 123, instances: 1, max_instances: 1
WARNING:apscheduler.scheduler:Execution of job "child_job (trigger: date[2015-12-07 15:27:11 CST], next run at: 2015-12-07 15:27:11 CST)" skipped: maximum number of running instances reached (1)
job_id: 7279209ab6c2498698f2117bb97e18a1, instances: 0, max_instances: 1
 

python paramiko实现ssh远程登录

  • 介绍
paramiko是python的一个模块,遵循SSH2协议。 paramiko的功能:1、通过ssh执行命令; 2、通过ssh传输文件。
  • 示例
import paramiko

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname="xxx.xxx.xxx.xxx", port=22, username="xxx", password="xxx")
stdin, stdout, stderr = ssh.exec_command("vmstat 1 1")
content = stdout.readlines()
for r in content:
    print r
ssh.close()
 

gettext国际化用法示例

  • 安装gettext
sudo yum install gettext
  • gettext工具
gettext: 进行translate。
 
xgettext: 从程序中抽取调用gettext进行本地化的字符串,生成一份.po结尾的配置文件。
 
msgfmt: 将配置好的本地化配置文件进行转换成gettext使用的格式。
  • 准备demo.py
#encoding=utf-8
import gettext

# demo对应mo文件名,locale为locale目录地址,zh_CN为locale目录下目录名
zh = gettext.translation("demo", "locale", languages=["zh_CN"])
# 激活_()
zh.install(True)
print _("hello world")
  • 生成po文件
 
从程序文件中抽取,使用gettext的字符串,生成po文件
xgettext -L python -o zh_CN.po demo.py
  • 编辑po文件
...
#修改编码为utf-8
"Content-Type: text/plain; charset=utf-8\n"
...

#加上翻译
#: demo.py:6
msgid "hello world"
msgstr "你好,世界"
  • 创建locale目录
mkdir -p locale/zh_CN/LC_MESSAGES
 
* 编译po文件
msgfmt -o ~/locale/zh_CN/LC_MESSAGES/demo.mo zh_CN.po
  • 查看结果
➜  ~  python demo.py
你好,世界
 

supervisor用法

以什么方式运行进程?将它做成服务,再以"service xxx start/stop"方式运行。
或者以"nohup xxx &"方式运行,需要停止时,先ps获得进程id,然后kill掉.
有什么好点的办法?supervisor正好解决这个问题。
  • 安装
sudo pip install supervisor
  • 创建配置文件
echo_supervisord_conf > /etc/supervisord.conf
  • 取消注释
打开/etc/supervisord.conf,取消下面两行注释,并修改files内容.
[include]
files = /etc/supervisor/*.ini
  • 新建配置目录
新建supervisor目录及test.ini文件
➜  /etc  tree supervisor
supervisor
└── test.ini
  • 配置内容
test.init内容
[program:test]
command=python -m SimpleHTTPServer 8000
directory=/home/wyq/
  • 运行
运行后,在浏览器访问8000端口
supervisord 
 
调试运行,此时需要先将test.ini中directory注释掉.
supervisord -d
supervisor也可指定配置文件
 
supervisord -c xxxx
  • 查看日志
tail -f supervisord.conf
  • 查看运行状态
➜  ~  supervisorctl status
test                             RUNNING   pid 4922, uptime 0:00:07
  • 停止任务
启动与重启参数为start/restart
 
➜  ~  supervisorctl stop test
test: stopped
➜  ~  supervisorctl status   
test                             STOPPED   Jul 09 04:32 PM
  • 停止
supervisorctl shutdown
 

利用setuptools的entry_point参数实现模块动态导入

setuptools提供了entry_points参数,允许在安装时,动态导入模块. 下面是简单示例.
  • 目录结构  
建立如下文件
➜  book  tree
.
├── book
│   ├── add.py
│   ├── __init__.py
│   ├── remove.py
│   └── update.py
└── setup.py
  • add.py内容  
remove.py、update.py与add.py相同
def make():
    print "add"
  • setup.py内容
from setuptools import setup, find_packages

setup(
    name = "book",
    version = "0.1",
    packages = find_packages(),
    entry_points={
        "book":[
            "add=book.add:make",  #add=模块:函数/类
            "update=book.update:make",
            "remove=book.remove:make",
        ]
    }
)
  • 编译成egg包
python setup.py bdist_egg
  • 安装到系统路径  
上面编译命令可以不用,直接用install安装
python setup.py install
  • 用法
from pkg_resources import iter_entry_points

for r in iter_entry_points("book"):
    m = r.load()
    m()