小站会根据您的关注,为您发现更多,

看到喜欢的小站就马上关注吧!

下一站,你会遇见谁的梦想?

小站头像

Linux Powered

A few shortcuts, a little time saver, long live open source.

RSS 归档

站长

24人关注

站长在关注

2014 / . 04 / . 12

Shell的并行化处理神器——Parallel

parallel属于GNU工具的一员,在现在并行化计算编程大行其道的今天,
使用parallel工具可以让shell也能轻松实现并行化(多核)处理工作。

相比较其他老牌GNU工具而言,parallel很年轻(2011年出现),所以知道的人应该还是有限,但这么强大的工具应该推荐给更多人知道.

示例讲解,请各位看官移步:

http://tieba.baidu.com/p/2979673753

2014 / . 03 / . 19

制作/发布便携(发行版独立)软件纲领

如果你在想让自己开发/编译的软件在大多数发行版上可以原封不动的直接运行,需要考虑这些问题

1 - 如果能直接编译成静态的那自然是比较省事了,但很多时候没办法这样,需要动态的,那就接着看吧

2 - 在相对比较老的发行版上编译,这样是用老GLIBC在新的发行版上也能运行,反之则不行。

3 - 附带共享库用RPATH,别套脚本用LD_LIBRARY_PATH。编译的时候考虑是用-Wl,-rpath='$ORIGIN/../lib'传递给 linker类似的方法,这样执行文件会在自己的相对路径去找so库文件。$ORIGIN指代的是真正的执行文件的位置,所以即使软链接到别的地方也没问 题,而用LD_LIBRARY_PATH环境变量则不行。用ldd查看可以看到效果。

4 - 其实也可以是用patchelf来hack已编译好的执行文件的RPATH,记得so库之间有互相依赖的话也要改。

5 - 系统的库如libc或libm是没什么必要也打包的。

2014 / . 03 / . 19

网页资源嵌入html实现单文件

比如,用firefox保存完整网页,除了html文件意外还会有一个_files结尾的文件夹用来保存网页里面用到的图片, CSS等。这样传到手机或平板上看有点不太方便,于是我搞了个小工具把这些额外的文件都嵌入到html文件里面,做成单文件版。
这虽然有点类似ie的mht(firefox通过扩展也可以保存mht)或safari的webarchive,但有着本质的不同,前者是私有格式,而html是开放格式,是个现代浏览器都支持。
实现:(注意为了保持格式,制表符已被换成全角空格)

#!/usr/bin/env python2.7

from bs4 import BeautifulSoup as bs
import base64 as b64
import urllib
import os
import re
import unicodedata
import inspect

cssContainer = '<style type="text/css"><!--\n{0}\n-->\n</style>'
jsContainer = '<script type="text/javascript"><!--\n{0}\n-->\n</style>'
imgSrcContainer = r'data:image/{0};base64,{1}'
cssUriContainer = r'data:image/png;base64,{0}'
cssImgPattern = re.compile(r'''url\((["']?)(.+?\.png)\1\)''', flags=re.I)

def printpath(path):
    path, lengthList = terminalWidth(path)
    #print lengthList
    if sum(lengthList) > 76:
        total = 0
        for i in xrange(len(lengthList)-1, -1, -1):
            if total <= 73:
                total+=lengthList[i]
            else:
                cutoffset = i+2
                break
        path = u'...' + path[cutoffset:]
    print path

def terminalWidth(string, encoding='utf-8'):
    if str in inspect.getmro(string.__class__):
        string = string.decode(encoding)
    charWidthList = [2 if unicodedata.east_asian_width(i) in ('W', 'F') else 1
            for i in string ]
    return (string,charWidthList)

def get_encoding(soup):
    encod = soup.meta.get('charset')
    if encod == None:
        encod = soup.meta.get('content-type')
        if encod == None:
            content = soup.meta.get('content')
            match = re.search('charset=(.*)', content)
            if match:
                encod = match.group(1)
            else:
                encod = 'utf-8'
    return encod

def embedCSS(soup, rootpath, encoding):
    for e in soup(['style', 'link']):
        try:
            if e.name == 'style':
                if e['type'] == 'text/css':
                    path = os.path.join(
                            rootpath,urllib.unquote(e['src'].encode(encoding)))
                    del e['src']
                    #e.extract()
                else:
                    continue
            elif e.name == 'link':
                if 'stylesheet' in e.get('rel', '') or \
                    e.get('type', '') == 'text/css':
                    path = os.path.join(
                            rootpath, urllib.unquote(e['href'].encode(encoding)))
                    del e['href']
                    #e.extract()
                else:
                    continue
        except KeyError:
            continue
        try:
            cssrootpath = os.path.dirname(path)
            csslines = []
            with open(path) as cssf:
                for line in cssf:
                    while True:
                        urlpattern = cssImgPattern.search(line)
                        if urlpattern is None:
                            break
                        s, e = urlpattern.regs[2]
                        pngpath = os.path.join(cssrootpath,urlpattern.group(2))
                        try:
                            pngdata = cssUriContainer.format(
                                    b64.b64encode(open(pngpath, 'rb').read()))
                        except IOError:
                            pngdata = ''
                        line = line[:s] + pngdata + line[e:]
                    csslines.append(line)
            printpath(path)
            soup.head.append(bs(cssContainer.format(''.join(csslines))).style)
        except IOError:
            continue


def embedJS(soup, rootpath, encoding):
    for e in soup('script'):
        try:
            path = os.path.join(
                    rootpath, urllib.unquote(e['src'].encode(encoding)))
            del e['src']
            #e.extract()
        except KeyError:
            continue
        try:
            soup.head.append(
                    bs(jsContainer.format(open(path, 'rb').read())).script)
            printpath(path)
        except IOError:
            continue

def embedImage(soup, rootpath, encoding):
    for img in soup.body.findAll('img'):
        imgpath = urllib.unquote(img['src'].encode(encoding))
        imgpath = os.path.join(rootpath, imgpath)
        imgtype = os.path.splitext(imgpath)[1].lstrip('.')
        try:
            imgb64data = b64.b64encode(open(imgpath, 'rb').read())
        except IOError:
            pass
        else:
            img['src'] = imgSrcContainer.format(imgtype, imgb64data)
            printpath(imgpath)

def main(pathOfFile):
    filename, fileext = os.path.splitext(pathOfFile)
    rootpath = os.path.dirname(pathOfFile)
    soup = bs(open(pathOfFile, 'rb').read())
    encoding = get_encoding(soup).lower()
    embedCSS(soup, rootpath, encoding)
    #print soup.body
    embedJS(soup, rootpath, encoding)
    embedImage(soup, rootpath, encoding)
    open(filename+'_single_'+fileext, 'wb').write(soup.prettify(encoding))

if __name__ == '__main__':
    import sys
    htmlfile = sys.argv[1]
    main(htmlfile)

2014 / . 03 / . 19

挂载MTP的android设备的工具----go-mptfs

在Linux下,不能原生挂载MTP设备,OSX下的Android File Transfer也是BUG成堆。
基于libmtp的mtpfs或基于adb的adbfs利用fuse挂载,性能很差还不稳定。

无意间发现了个霸道的项目
https://github.com/hanwen/go-mtpfs
用Go实现的mtpfs,使用了感觉性能很好,可以满足了。
作者应该是只为Linux开发的,不过我试验了,在OSX下用homebrew安装了libusb和osxfuse以后,也可以编译通过和正常使用,不过貌似性能没有Linux下好?也许是fuse的问题?

2013 / . 04 / . 12

How to connect to a wireless network via commandline, the generic methods.

在图形界面下,无论用什么发行版,无论什么桌面环境,一般都会使用NetworkManager服务来连接无线网络。如果选轻量级一点的还有wicd。

那么在纯console环境下,尤其连接wpa/wpa2加密形式的AP的话,不同的发行版也给出了不同的简便连接方案,例如arch使用了wifi-menu(netcfg的一个前端),centos可以用system-config-network-tui。但是毕竟这种工具换了一个发行版就不能用了,所以还是最好掌握一个最一般化,非发行版特异的连接方式。

这里说的连接方法主要还是参考的arch的wiki,但是的确是非发行版特异的,例如我刚刚在slax上也成功连接。

ip link

##列出你所有被系统识别的网络设备,确定你的无线网卡的借口的名字,一般是wlan0

##如果lsusb或lspci里能找到你的网络设备,但是ip link里没有,就是网卡没有驱动。

ip link set wlan0 up

##这里已经开始假定你的网络接口是wlan0,首先需要启动设备

iw dev wlan0 scan | less

##扫描可用的AP,主要看它的ESSID,和加密格式

iw dev wlan0 scan | grep -A46 -B8 apName

##如果已经知道了AP的名字(apName),可以这样查看,过滤掉其他不需要的信息。

wpa_passphrase apName apPassword >> /etc/wpa_supplicant.conf

##生成指定AP名和密码的配置文件,保存到/etc/wpa_supplicant.conf

##应该是这样:

##network={

##        ssid="apName"

##     #psk="apPassword"

##   psk=5e81cd631532134cde68e054ed7a3c1de5514d061e6a2093629debad9ac2

##}

##一般来讲这样就可以了,但是如果AP配置特殊的话需要额外参数可以参考

##man wpa_supplicant.conf,里面提供了很多模版。

wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf

##连接AP,如果显示连接成功,就Ctrl+C取消掉,然后再加个-B选项让其后台运行。

##如果连接不成功,可能是密码不对,或者驱动不对,上面的命令默认相当于省略了

## -Dwext,在man wpa_supplicant的DRIVERS一节可以查到有哪些驱动可用。

##例如,你可以多加几个驱动 -Dnl80211,wext

iw dev wlan0 link

##确认是否连接成功,如果成功会显示AP信息,没有连接显示not connected。

ip addr show dev wlan0

##检查是否获得了IP地址,如果没获得,需要进一步配置IP。

dhcpcd wlan0    或     dhclient wlan0

##DHCP自动获取IP不同发行版有少许区别,看你的带哪个工具就用哪个。

ip addr add 192.168.0.2/24 dev wlan0

ip route add default via 192.168.0.1

##如果AP不是自动获取IP的话(一般这种情况很少),就用上面的命令手动配置IP和网关。

##最后再编辑一下/etc/resolv.conf来配置一下DNS就大功告成了。

 

这就是一般化的WPA加密无限网络的连接方式(WEP加密的google随便一搜,介绍连接方法的都一大把,就不提了这里)。这样你在需要迁移发行版的时候也能快速的把无限网络配置好。这个流程不保证绝对成功,碰到隔路的硬件还是看看man手册需找解决方案。如果需要开机自动配置好的话,把上面的命令保存到/etc/rc.local里(当然,需要再提供一些错误检查功能),或者使用systemd的话,则需要写一个.service。

整个配置主要使用iproute和wireless-tools工具,摒弃过时的net-tools。

你要是现在还成天用ifconfig这类早已不再维护的工具的话,你的知识确实该更新一下了。

扔掉ifconfig,netstat,迁移到ip,ss。

2013 / . 03 / . 24

Installing Archlinux on Macbook, the Essential Note

本来也不打算写一个完整的教程,因为会装Archlinux的人水平自然也不会太差,所以我只说一些wiki里没提到或这提到但是与实际情况有出入的地方。

 

首先自然是下载archlinux的最新版iso镜像,然后用最简单粗暴的办法来制作USB启动盘,

dd if=/path/to/the/iso of=/dev/disk1

disk1要根据实际情况看究竟是多少号。

接着要给archlinux在硬盘上腾出地方来才能安装,在mac下进到disk utility里,再多创建的分区就可以(一个就可以了,回过头来在linux的安装过程中再把它删掉,重新分多个分区)

然后重启时按住option不方,应该会出现启动菜单,第三项一个黄色的硬盘图表就是U盘启动。

然后跟正常的安装Arch的流程一样,生疏的可以参考wiki上的Beginner's Guide。

大致流程就是,先配置联网,编辑软件源列表,然后分区,因为毕竟macbook用的是gpt分区表,所以最好用parted或cgdisk来分区。分区的时候除了传统的/boot,/,/var,/home之类的分区以外记得还要再多创建一个分区,100M以内即可,最好格式化成hfs+或vfat格式,挂载点设置到/boot/efi这里,系统引导的时候需要用到。

分区完后,需要按照真正系统的挂载点挂到/mnt下,比如你有root,home,var,boot等分区,需要这样的结构

root ---- /mnt

boot ---- /mnt/boot

efi ---- /mnt/boot/efi

home ---- /mnt/home

var ---- /mnt/var

我个人偏好只分三个分区,efi,boot和一个lvm物理卷。再在卷组里分各个分区这样好处是以后调整大小十分容易。

挂载好后,就可以用pacstrap把需要的基本包(base,base-devel)装到/mnt下,重启后再进一步安装和配置图形界面之类的,不过我为了图省事直接把X和KDE之类的也都装上了,也没什么问题。

装好后chroot到/mnt下,开始配置hostname,timezone,locale等等。然后重新mkinitcpio一遍initramfs,如果用的lvm记得把这个module填上。

最后开始装引导了,这是重中之重。

记住不要安装bios版的grub2包,如果是x86_64位系统,安装grub-efi-x86_64,如果是32位系统,安装grub-efi-i386包。然后写入引导到硬盘

zai

z

# modprobe dm-mod
# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=arch_grub --recheck /dev/sda4
# mkdir -p /boot/grub/locale
# cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale/en.mo

这里的/dev/sda4依情况而定,编号既可以是你的efi分区,也可以是boot分区,总是让它不起作用就行了。
装完grub2后就可以exit回live系统,umount掉分区,然后重启。
当然,现在还不能进入到arch下,而是回到mac。
去 http://www.rodsbooks.com/refind/ 这里下载引导。
解包后,打开中断运行里面的 ./install.sh
默认会把引导装到你的mac系统根分区,当然你也可以添加 --root /Volume/{efi partition}参数,把它装到你的efi分区里,
这样以后mac分区如果有问题被格掉了,也不会影响双系统启动。
最后再一次重启,就可以看到Arch的选项了。在接下来怎么装就完全取决于你怎么看wiki了。
2013 / . 03 / . 03

How can it be done that organizing iPhone/iPad contents under Linux?

苹果出的iOS内容管理软件iTunes只有Windows版和Mac OS X版,而且对于这两个平台它又是必不可少的。

即使你有iTools或iFunbox之类的第三方工具,也依然需要iTunes里带的驱动才能正常使用。

但是在Linux下又该如何管理这些内容呢?

我之前的文章提到果iFuse工具,它可以将iOS文件系统像U盘一样挂载,这样虽然你不能同步音乐,iBook什么的,

但是至少可以为你的应用备份存档,导出照片,导入文件,优化系统什么的,总之还是非常有用的。

今天介绍另一个工具------ideviceinstaller

顾名思义,有了它就可以给iOS设备安装应用或者备份应用。

ideviceinstaller的功能还是很丰富的,不仅仅局限于安装应用。

How can it be done that organizing iPhone/iPad contents under Linux?

可以看到有一系列的工具,

 

How can it be done that organizing iPhone/iPad contents under Linux?

把我得iPad插上电脑,输入idevice_id -l,出现了设备序列号,说明能够正常识别了。

 

How can it be done that organizing iPhone/iPad contents under Linux?

输入ideviceinstaller -i [ipa文件],就可以安装应用了。

 

How can it be done that organizing iPhone/iPad contents under Linux?

用ideviceinstaller -l 就可以打印出所有已安装的应用,找到你需要处理的应用的APPID(减号分割的前半部分),然后就可以备份或者卸载应用了。

 

How can it be done that organizing iPhone/iPad contents under Linux?

备份功能的相关选项很多,甚至可以连你的存档一块备份出来。如果你得应用是正版的话,连你得账户信息也会备份出来,如果你有多个iOS设备用同一帐号的话,就可以直接还原到另外一个设备里完全正常使用。

 

How can it be done that organizing iPhone/iPad contents under Linux?

例如我现在备份了一个叫国际象棋大师的应用,打包成ipa格式并存储到了电脑的HOME目录下。回过头来还可以用-i选项再将ipa文件还原回去(等价于安装新应用,这跟用-r选项还原是有区别的)。

2013 / . 02 / . 21

extract session cookie of xunlei.com

迅雷离线下载我一直以来用的是xunlei-lixian(https://github.com/iambus/xunlei-lixian)这个命令行工具外加自己写的图形界面。

不过貌似最近以来,迅雷离线的登录机制可能发生了点变化,在xunlei-lixian里配置了帐号密码后还是会现实登录失败,github上的issues列表里有人说怀疑是验证码通不过的原因。

作者迟迟也不更新,去解决一下,有人提出用浏览器登录迅雷的cookie替换xunlei-lixian所保存的cookie配置,就能正常登陆了。

我试着摸索了一下,在firefox里装个了cookie manager的扩展,找到xunlei.com的domain,找到lsessionid,然后打开xunlei-lixian的cookie配置文件,替换lx_sessionid部分。发现确实可以登录了。

不过每次一旦出现登录失败的时候都要重复一遍上述流程实在太麻烦了,所以干脆还是用python写个自动替换工具,一键搞定。

extract session cookie of xunlei.com

##########################################

#!/usr/bin/env python2.7
import os, sys
import ConfigParser as cfgp
import sqlite3 as sql
import cookielib
import pdb

def getDefaultProfile(profilesIniFile):
    profilesCfg = cfgp.ConfigParser()
    profilesCfg.read(profilesIniFile)
    if len(profilesCfg.sections())==2:
        return profilesCfg.get('Profile0','Path')
    for i in profilesCfg.sections():
        if profilesCfg.has_option(i, "Default") and \
            profilesCfg.getint(i, "Default") == 1:
            return profilesCfg.get(i, "Path")

def parseSession(cookieFile=None):
    cookieCon = sql.connect(cookieFile)
    cookieCur = cookieCon.cursor()
    for row in cookieCur.execute('''select value from moz_cookies where baseDomain="xunlei.com" and name="lsessionid"'''):
        return row[0]

def writeCookie(lxCookieFile, lsessionid):
    cookiejar = cookielib.LWPCookieJar()
    cookiejar.load(lxCookieFile, ignore_discard=True, ignore_expires=True)
    session_ck = cookielib.Cookie(version=0, name='lx_sessionid', value=lsessionid, port=None, port_specified=False, domain='.vip.xunlei.com', domain_specified=True, domain_initial_dot=True, path='/', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={}, rfc2109=False)
    cookiejar.set_cookie(session_ck)
    cookiejar.save(lxCookieFile, ignore_discard=True)

if __name__=='__main__':
    #pdb.set_trace()
    dirDispatch = {'posix': lambda:(os.environ['HOME'], '.mozilla'),
            'win32': lambda:(os.environ['APPDATA'],'Mozilla')}
    profileRoot, mozilla = dirDispatch[sys.platform.lower()]()
    profilesIniFile = os.path.join(profileRoot, mozilla, 'firefox', 'profiles.ini')
    profilesFolder = getDefaultProfile(profilesIniFile)
    if os.path.isabs('profilesFolder'):
        profilesPath = profilesFolder
    else:
        profilesPath = os.path.join(profileRoot, mozilla, 'firefox', profilesFolder)
    cookieFilePath = os.path.join(profilesPath, 'cookies.sqlite')
    print cookieFilePath
    raise RuntimeError('shit')
    lsessionid = parseSession(cookieFilePath)
    xunleiCookieFile = os.path.join(profileRoot, '.xunlei.lixian.cookies')
    writeCookie(xunleiCookieFile, lsessionid)
################################################

firefox的xunlei的session的cookie是存在配置文件的cookies.sqlite里,需要先确定火狐用得哪套profile才能定位cookies.sqlite然后用SQL提取,再用cookielib把xunlei-lixian的配置文件解析出来,替换值,再保存回去。

我还特意写成了linux和win下通用的,结果在win下试验一下竟然说cookies.sqlite是加密的或不不是数据库,不知为何。不过暂时算了不研究,反正也是个残废。在linux下好用就足够了。

2013 / . 02 / . 19

Hola: A free tool help you cross over the fμcking GFW

Hole是个免费的Firefox/Chrome扩展,安装好之后可以访问一些被GFW关照的视频和社交网站。虽然支持的网站数目很是有限,跟真正的代理是没法比了,但是聊胜于无,再说也是安装简单,所以在这里推荐一下。

http://hola.org/

Hola: A free tool help you cross over the fμcking GFW

Windows和android平台由客户端,除了梵蔷还有视频加速功能,但是我特意开了个windows虚拟机试了一下,其实就是在IE设置里加了个PAC做自动代理。半天竟然什么都没打开(不知道它原理是什么,我用的GUEST连接HOST的虚拟网卡,再用防火墙转发到物理网卡,可能跟这个有关?)。其它平台如linux或mac就只能用firefox或chrome的扩展来实现了。

浏览器版什么都不需要配置,装好了直接生效。

试了一下葫芦,能打开(话说revenge真是越来越没意思了),不过等到播放视频的时候会报错,说视频流服务只在美国使用,结果还是播不了。。。。

Hola: A free tool help you cross over the fμcking GFW

然后是非死不可,也能打开:

Hola: A free tool help you cross over the fμcking GFW

注意我的autoproxy是关着的,所以hola确实是起作用的。

 

2013 / . 02 / . 17

Understanding PAC file syntax

 

常在网上逛,哪能不梵蔷。

一般来讲,无论你使用的代理是http或者ssh,在使用浏览器上网的时候你都会需要一个自动切换的机制----获取gfw的所有黑名单,如果你访问的网站在其中,就使用代理,如果不在其中,那么直接也连接。

这样的自动机制的好处是,不是所有网站的访问一概全用代理,反之在访问国内网站时很有可能速度会很慢。

Firefox的Autoproxy,chrome的SwitchySharp都是这样的机制。而在此基础上,如果你发现漏网之鱼还可以自行添加网站。不过在用户体验上还没有出autoproxy其右者。

然而对于如Opera在内的更多的浏览器并没有这样的扩展,或者干脆不支持扩展,又该怎么办?那你就只好用PAC文件了。

Understanding PAC file syntax

 

 

pac(proxy auto-config)顾名思义,即代理自动配置。说白了就是个文本文件,里面定义了代理服务器的地址,以及一大堆用通配符或者正则表达式来进行匹配的网址,你在上网的时候,如果网站网址配上了pac中的规则定义,就使用代理访问,如果没有匹配,则直接打开。

用google搜索”gfw pac“就能找到这种文件(不过你搜这个的话一定会连接被重置,所以需要“用代理来搜代理”,讽刺吧),下载下来放到固定地方,简单设置下代理的ip,再在浏览器里把代理设置为pac即可。

不过,我前面说过,这种列表总会有很多的漏网之鱼,很多时候需要自己添加网址,所以对pac语法的了解还是有必要的。

这里就已“go马赛克ent”提供的pac文件来做一下说明:

Understanding PAC file syntax

Understanding PAC file syntax

需要解释的地方,我都用中文注释做了标注。

不过其实“go马赛克ent”使用的pac文件还是相对特殊了一点,因为所有使用代理的返回值是硬编码的,这样改起来会不太方便,更好的做法是把一个或多个代理地址用变量表示,然后在函数里的所有返回使用代理的值都用变量表示,这样一旦代理地址发生变动,只要修改一处便可以了:Understanding PAC file syntax

例如这样,在文件开头预定义了代理的变量,这样返回值使用return http_prox,

而不需要硬编码为 return "PROXY localhost:8888"

2013 / . 02 / . 10

One click mount iOS devices

首先,你的系统需要有ifuse,然后在/etc/fuse.conf里添加上一行“user_allow_other”。最后,由于我用的是KDE,所以用的kdialog来提示是否挂载成功(如果非终端下,而是在文件管理器里运行的话,终端下任意桌面皆可)。

#############################################

#!/bin/bash -
set -o nounset                      # Treat unset variables as an error
mpoint="$HOME/iOS_fs"

message() {
    if [ $TERM == "dumb" ];then
        kdialog --msgbox "$1"
    else
        echo "$1"
    fi
}

[ -d $mpoint ] || mkdir $mpoint
if ! mount|grep "$mpoint" >/dev/null;then
    ifuse --root -o allow_other,sync $mpoint || {   
    message "Can't mount iOS device."
    exit 1
}
    message "iOS device mounted"
    exit 0
else
    fusermount -u $mpoint || {
    message "Can't umount iOS device."
    exit 1
}
    message "iOS device umounted."
    rmdir $mpoint || message "Unable to remove mount point."
    exit 0
fi
##############################################

保存成文件,添加执行权限,插上iOS设备,在文件管理器里点击运行。如果成功,会提示“iOS device umounted.”,然后到HOME下的iOS_fs文件夹下访问。

再次运行则会卸载设备。

One click mount iOS devices

2013 / . 02 / . 08

The fast way switch to external screen as the main display with custom resolution and turn off internal screen when a laptop connected to a monitor.

笔记本外接显示器时,可以都多种显示方式,如mirror方式,笔记本屏幕和外接显示器显示同一个桌面,其分辨率以低的那个为准。也可以并排显示,即两块屏幕横向或竖向并排组成一个更大的桌面。不过对于第二种方式来说,笔记本和显示器很难并排放到一起,所以我个人习惯还是把笔记本屏幕关掉,只用外接显示器(外接显示器也比笔记本分辨率高很多)。

尽管一般来说可以在桌面的系统设置里来调整(例如KDE的设置就很详细了),不过写一个shell script来一键快速设置也未尝不可。

流程如下,说明我已经添加了注释

###########################################

#!/bin/bash
width=1920          #宽
height=1200         #高
refresh=50           #刷新率
device=VGA-1      #外接显示器设备名
offdevice=LVDS-1  #笔记本显示屏设备名
modeline=$(cvt $width $height $refresh| awk 'NR==2{$1="";$0=$0;$1=$1;gsub("\"","",$1);print}')       #获取指定分辨率的调整参数
mode=$(awk '{print $1}' <<<$modeline)        #获取该分辨率的模式名称

xrandr --dpi 111x114 || exit 1                          #调整文字的DPI,不过开源驱动只能设置正方形,矩形无效
xrandr --output $device --left-of $offdevice       #一定要关闭mirror模式,把外接显示器放在笔记本屏幕的左边或者右边,否则外接显示器分辨率超过内置的太高,直接导致黑屏
xrandr --output $offdevice --off || exit 1           #关闭笔记本屏幕
hasmode=$(xrandr|awk '{if($1=="'$device'"){p=1;getline}if(p==1 && $0~/^ / && $1~/'$mode'/){print 1;exit}}')         #检查指定的分辨率是否已经有了,防止该script被多次重复运行会报错
echo hsdmode is $hasmode
if ((hasemode=1));then            #如果已经有了,就删掉重新添加
    xrandr --delmode $device $mode &>/dev/null
    xrandr --rmmode $mode &>/dev/null
fi

xrandr --newmode $modeline
xrandr --addmode $device $mode
xrandr --output $device --mode $mode || exit 1

# 添加新分辨率,并启用该分辨率显示

############################################

需要说的是使用的是xrandr来调整设置,所以只适用于开源驱动。如果使用的是闭源驱动的话,例如nvidia,则在驱动自带的面板中进行设置。

搞定收工。

2013 / . 01 / . 20

rename batch downloaded 163 open course videos from random charactors to their real names

网易公开课虽然在每个课程的主页直接提供下载地址,这样可以直接通过下载工具批量下载下来,省得一个一个拽,但是下载下来的mp4格式文件,都是该课程的一个编码外加一串随机字符。所以批量下载下来之后,你根本不知道哪个是哪一集,这点太缺德了。

于是只好自己写个script来parse出文件可课程名称,然后再批量重命名。

我用downthemall,选择下载网页上的全部链接,过滤器选择video,这样就选中了所有课程文件。

rename batch downloaded 163 open course videos from random charactors to their real names

下载完毕后,需要再将该课程的主页下载下来,保存时选择text files。

写个函数:

ocParseName() {

iconv -f gb2312 -t utf8 "$1" |

awk '{if($0~/\[第[0-9]+集/){name=$0;getline;getline;split($0,shit,/\/|>/);print shit[9]"\t\""name".mp4\""}}'|

sort|

uniq ;}

然后用它来parse下载的text格式网页

rename batch downloaded 163 open course videos from random charactors to their real names

视频文件名和它对应的课程名就出来了。接着可以利用这个来所批量重命名:

ocParseName 斯坦福大学公开课:iPad和iPhone应用开发\(iOS5\)_ 全10集_网易公开课.html |

while read x;do eval "mv $x"; done

搞定收工。

2013 / . 01 / . 19

conceptual implementation of binary executable bundled shell script.

支付宝的Linux下的安全控件,从官网上下载下来是一个shell的安装script,其中嵌入了二进制的so文件,通过自己读取自己来将so文件提取出来,从而安装到火狐的目录。(当然不止是这个,很多不开源的免费或商业软件也都是以这种形势来发布的,如nvidia驱动,vmware等等。)

其实现方法在这两行:

    ARCHIVE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' "$0"`
    tail -n+$ARCHIVE "$0" | tar xzvm -C $TMP_DIR > /dev/null 2>&1 3>&1

原理是通过awk读取script中标记为__ARCHIVE_BELOW__文字的位置,记录它的下一行的行数,然后用tail来提取从那一行开始整个script剩下的信息,其实就是二进制的so文件,这个在文本编辑器里是乱码。

但是我觉得这个其实用一个awk就可以完全实现了,多一步tail没有多大必要。

    awk '{if(flag==1){print;exit};if($0~/^__BIN_STARTS_HERE__$/){RS="__THIS+WILL+NEVER+APPEAR__";flag=1;next}}' "$0" > tempfile

通过设置一个flag,当flag为0的时候什么也不输出,当找到__BIN_STARTS_HERE__,flag设为1,再将行分隔符设为一个永远不可能出现的字符串,这样从这里开始到文件末尾会被认为是一行,awk的环境变量在当前行设置后总是在下一行才开始奏效的,所以不会有问题,直接next跳到下一行,flag由于已经变成1了,所以从这里开始输出文件内容。

这个实现方法和前者唯一一个小区别是导出的文件末尾会多出一个换行符,但是无伤大雅,不会影响它的运行或调用,不过如果需要如md5校检之类的则会失败。当然,其实print可以用printf $0代替,这样就完全一样了。