ichuan.net

自信打不死的心态活到老

windows下路径长度限制问题

问题

昨日志旭在做一个测试时发现在 windows 下无法删除一个目录,报错如下:

enter image description here

在资源管理器左侧展开查看,发现这是个嵌套很深的目录:

enter image description here

底部红色标注的目录其实还有子目录,但是展示不出来了。在 cmd.exe 下用 del /S /Q e:\test 或者在 powershell 中用 Remove-Item e:\test -recurse 同样无法删除改目录。

原因

查了半天,原来是 windows 的路径长度限制问题导致的。据说 ANSI 版本的 windows API 能操作的最大路径长度是 260 个字符,但我在本机测试时,这个值是 247:

>>> import os
>>> x = 'e:\\'
>>> x += 'e' * (247 - len(x))
>>> len(x)
247
>>> os.mkdir(x)
>>> os.mkdir(x + 'e')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
WindowsError: [Error 206] : 'e:\\eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
>>>

windows 下的 python 文件操作也是调用的系统 API,所以是受限制的。那之前的深层目录是如何创建的呢?原来是一个 java 程序创建的。java 程序是原生支持长路径名的。

解决方法

根据微软官方文档,要操作大于 260 字符的路径名,就需要使用 Unicode 版本的 windows API,那个限制是 32767,近乎无限制了。并且,需要在目录名前面加特殊字母:\\?\

我们先来重现一个无法在资源管理器和普通 python 代码里删除的深层目录。需注意两点:

  1. 使用 ctypes 里导出的 windll.kernel32 直接调用 Unicode 版本的创建目录 API:CreateDirectoryW
  2. 目录名前加特殊标识:\\?\

代码如下:

import os
import ctypes

mkdir = ctypes.windll.kernel32.CreateDirectoryW

cur = u'\\\\?\\e:\\'

for i in xrange(100):
    cur += u'test\\'
    mkdir(cur, None)

执行后会在 E 盘创建一个删不掉的 test 文件夹。如果你把上面的 mkdir 替换成 os.mkdir,会发现报错。

接下来尝试删除刚创建的嵌套目录。除了需要加特殊标识,还有个要注意的是:windows 下没有 API 提供类似 rm -rf 的功能,只有删除单个文件和删除空目录的 API,我们需要自己实现一个 rm -rf

import os, ctypes

rmdir = ctypes.windll.kernel32.RemoveDirectoryW

def rm_rf(dirname):
    for i in os.listdir(dirname):
        j = os.path.join(dirname, i)
        if os.path.isfile(j):
            os.remove(j)
        else:
            rm_rf(j)
    rmdir(dirname)

rm_rf(u'\\\\?\\e:\\test\\')

执行后会发现刚才的顽固目录被删除了。同样的,如果你把上面的 rmdir 换成 os.rmdir,也会发现报错。

你发现没?上述代码里的 os.listdiros.path.isfileos.path.join 都没换成 windows Unicode API,但也没问题,何解?原来部分 os 模块的接口是支持 unicode 文件名的:

>>> os.path.supports_unicode_filenames
True

但可惜的是 os.rmdir 不支持,不然就不用费心转用 windows API 了。

其他

linux 下对目录长度有限制吗?据说也有:linux 下目录长度限制是 4096 字节,文件名长度限制是 255 字节。

【已更新】python 中线程的 join 会延迟重定向后的标准输出

好久没写博客,原因是比较浮躁,心一直静不下来。今日先用一个小技术文章打破这个僵局。

先看下面程序:

#!/usr/bin/env python
# coding: utf-8
# yc@2011/12/26

import threading, time

def log(msg):
        print msg

def worker(s):
        while True:
                time.sleep(s)
                log('I am alive')

log('Creating worker...')
t = threading.Thread(target=worker, args=(2, ))
t.setDaemon(True)
t.start()
log('Worker started')

t.join()
log('Worker ended')

把这个程序保存为 test.py,然后执行 python test.py,会看到如下输出;

Creating worker...
Worker started
I am alive
I am alive
...

每两秒那个线程会向标准输出打印一句 I am alive。没问题吧?重新来,这样执行:python test.py >/tmp/x,将输出重定向到某文件,之后在另一 shell 窗口中执行 tail -f /tmp/x,预想结果应该是每两秒有输出,然而没有。再看 /tmp/x 文件,竟然是空的。

何解?

原因在于那个 `t.join()` 延迟了主线程重定向后的标准输出

经小 pan 同学点拨,不是 join() 的问题,而是重定向后的文件句柄的缓冲区导致的。解决方法是在 print msg 后调用 sys.stdout.flush()

那把 print msg 换成 sys.stdout.write(msg) 可以么?不行,还是需要刷新缓冲区。不过向标准错误输出写东西是会自动刷新的,可以试试把 print msg 换成 sys.stderr.write(msg)

计划.IN 开源

如同之前承诺的那样,现在将这个项目开源了,欢迎 fork:

https://github.com/ichuan/jihua

jihua.in 这个网站会一直运行,我本身就一直在用。

有个 mm 图在 doc/design.mm,是那晚简单做的一个设计。其他介绍就不写了,都在 github 那里。

计划.IN 上线

最近一周都没写博客,因为我在做一个网站。现在基本成型了,于是放出来,欢迎使用。

计划.IN —— 简单有效的工作计划表

从上周五买域名开始,到刚才把网站部署上去,总共花去了我 10 天的“空闲”时间。平时要上班,哪有那么多空闲时间啊。嗯,我基本都是在熬夜写代码的。10 天里熬夜了几天都记不清了,每次都是最早凌晨 5 点睡,那时睡的话要设 4 个间隔 10 分钟的闹钟,以防上班迟到。期间有天要出差,早上 7 点 50 的火车,那天就没睡,在火车上补的觉。

这么疯狂的原因一个是这是我喜欢的事,自然有无穷的动力去做;另一个也是做这个网站的初衷:我想确信自己还没老去,能力还没消失,我还不是个废人。

这期间又开始喝咖啡了。有些熬夜的晚上,抬起头会有奇异的感觉。后来也看到某百度员工猝死云云,甚惊恐,怕自己哪个晚上也会不小心驾鹤西去,一命呜呼。自毕业后我已有好久没通宵过了,之前玩游戏时偶尔通宵一次也会累个半死,第二天下午睡醒后有世界末日般的感觉。这次还好。

说正题。计划.IN(我这么叫它)其实就是个 todo list + 备忘录工具,具体的介绍可以看帮助页面,刚写的,还热乎着。开发过程可以简单概括为:买域名;用 freemind 列需求和设计;用 mockups 画原型图;实现 ui;实现后台逻辑。

前端 ui 实现占用了我 80% 的开发时间。因为我对细节有洁癖,而且自认有些审美能力,所以一直在细调。logo 是照着 twitter 的用 ps 画的;首页的预览图也是截图后用 ps 旋转得到的。js 代码局部重构了好几次,因为我要的既能运作又看起来漂亮的代码。为了想实现一种动画效果,把 css3 的 transform 尝试了遍,最后以失败告终。前端用的 twitter 的 bootstrap css 框架和核武器 backbone.js MVC 框架,后者的源码也几乎被我看完了。

后台逻辑实现比较简单,主要是一个 RESTful 接口。这个项目里我写的 python 代码远没有写的 js 代码多。现在发现 django 太庞大了,这次又学到很多以前没用过的功能。

因为比较关心数据安全性,于是申请了 StartSSL 的免费 SSL 证书,折腾一会给装上了。

促成我开始做这个站的最初原因是徐东同学说他有 todo list 的需求,然后我想起了我荒废很久的 MyPDC2.0 计划,其中就包含一个类似 计划.IN 的记事功能。于是先把这个实现了,要等想法都成熟才开始干是不明智的。

还有些没实现的功能,但我等不下去了,先让功能可用的它上线吧,我得休息下了。

命令行下的spark画图

创意来源:https://github.com/holman/spark

python 实现:https://github.com/ichuan/yc-lib/blob/master/python/spark.py

使用方法:

将数值以参数传入

admin@test: ~$ python spark.py 0 30 55 80 33 150  
▁ ▂ ▃ ▅ ▂ █

通过管道传入

admin@test: ~$ curl http://earthquake.usgs.gov/earthquakes/catalogs/eqs1day-M1.txt --silent|sed '1d' |cut -d, -f9 |tr "\n" ',' |sed 's/ //g' |python spark.py
▁ ▂ ▂ ▁ ▂ ▁ ▁ ▂ ▁ ▃ ▂ ▂ ▁ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▅ ▂ ▁ ▂ ▁ ▁ ▁ ▅ ▁ ▂ ▂ ▅ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▁ ▃ ▁ ▁ ▁ ▁ ▄ ▂ ▃ ▁ ▄ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▅ ▁ ▁ ▁ ▄ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▂ ▂ ▂ ▂ ▁ ▁ ▁ ▂ ▅ ▁ ▁ ▁ ▂ ▅ ▄