最近经常见到 :
这个 Shell built-in, 比较好奇, 于是 StackOverflow 了一下.
过程中偶然看到了这个技巧, 为了阅读方便把代码转载了过来,
有少许改动 (让程序实际可执行):
#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function() { console.log('hello world'); }
#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function() { console.log('hello world'); }
如你所见, 这段代码既是合法的 Shell 程序又是合法的 JS 程序, 把它当作 Shell 程序执行的话它的唯一功能是用 Node.js 重新执行一遍自己, 于是以下调用它的方式都能正常工作:
./foo
sh ./foo
node ./foo
结合语法高亮的提示, 不难发现其中关键在于 :
相当于一个 true
即空操作,
因为 //
不是 Shell 的注释所以 Shell 会直接去执行 exec
然后自己就变成 Node
了. 而 Node 会无视 shebang 行 (即使 #
不是合法的 JS), //
又是 JS 注释,
这样 ':'
就相当于一个它不认识的 use strict
一样的东西了, 也被无视.
于是剩下的内容就是一个 IIFE 了, 会被立即执行, 整体的流程比较简单.
那么我们来写一个 Python 版的吧!
#!/bin/sh
':' #; exec "$(command -v python)" "$0" "$@"
print('hello world')
嗯… 这样不太合适, 因为 #
既是 Python 注释也是 Shell 注释, 这样整个第一行在
Shell 看来就没有 exec
了! 但是 Python 只有 #
一种注释,
用字符串的话因为引号数量要平衡, 也不能让这一行在 Python 和 Shell 解释不一致,
那怎么办呢?
有没有想到一种同样经常使用在文件开头的 Python 字符串?
对了! 就是它! 文档字符串!
#!/bin/sh
# -*- coding: utf-8 -*-
''':' ; exec "$(command -v python)" "$0" "$@"
'''
print('hello world')
#!/bin/sh
# -*- coding: utf-8 -*-
''':' ; exec "$(command -v python)" "$0" "$@"
'''
print('hello world')
Shell 没有三撇的说法, ''':'
在 Shell 看来是两撇 ''
加上一个 ':'
,
于是我们同样成功地让 Shell 在第一行就把自己换成 Python 了. 而在 Python 看来,
整个第一行到下一行的三撇都是一整个字符串, 直接无视, 于是我们达到了同样的效果!