/usr/bin/pip3.10
:
#!/bin/sh
"exec" "$(dirname $0)/python3.10" "$0" "$@"
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())
个人理解是,上述脚本通过 exec ,用指定位置的 python 直接替换掉了当前的 shell 进程,那么为什么 "exec" "$(dirname $0)/python3.10" "$0" "$@"
之后的代码还能接着执行呢?
以及,为什么要这样写呢,拆开写不好吗?
1
qwq11 2022-07-26 17:07:21 +08:00 via Android
因为他 exec 启动 py 来执行当前脚本,第二行是一坨字符串直接被 py 忽略,然后 py 就正常往下执行
|
2
ruanimal 2022-07-26 17:10:00 +08:00 1
其实就是用 shell 启动了 python
$0 是当前文件路径 $@ 是所有命令行参数 "exec" 这一行是字符串,在 python 解释器中是没有效果的 |
3
AoEiuV020CN 2022-07-26 17:10:51 +08:00
> 之后的代码还能接着执行呢?
shell 进程就到此为止了,没有接着执行, exec 创建了 python 进程才真正执行后面的代码, 就是为了强制使用"$(dirname $0)/python3.10",不用管 python 到底在哪里吧, |
4
qwq11 2022-07-26 17:13:34 +08:00 via Android
|
5
BeautifulSoap 2022-07-26 17:19:02 +08:00
论 shell 脚本有多丑多难看,但又多好用
|
6
geelaw 2022-07-26 17:19:36 +08:00
https://en.wikipedia.org/wiki/Polyglot_(computing)
好处是在 shell 脚本里你既可以写 pip foo 也可以写 python pip foo ,前者的效果就是 python pip foo 。后面的代码当然没有“接着”执行,因为 shell script interpreter 进程已经被替换了,替换后的进程执行了其他代码,而这个其他的代码,刚好就是同一份,而且替换后的进程是按 Python 解读这份代码。 |
7
MiketsuSmasher OP |
8
webcape233 2022-07-26 19:24:33 +08:00 via iPhone
实在是骚 学会了
|
9
hsfzxjy 2022-07-26 19:26:09 +08:00 via Android
妙啊
|
10
kokutou 2022-07-26 19:36:38 +08:00
|
11
ysc3839 2022-07-26 19:43:32 +08:00 1
大部分 shell 是逐行解析的,所以只需要在解析到别的语言的代码前结束运行就不会出现错误。同时另一种脚本语言要有某种机制跳过开头的脚本,一般是想办法让其解析成注释。楼主给的例子,有可能是 Python 会从 coding: utf-8 之后执行,跳过之前的代码。
举个例子,cmd 脚本和 PowerShell 脚本写在同一个文件内,主要用于解决 PowerShell 脚本不能直接运行的问题: ``` <# : @echo this is from cmd! @powershell -NoProfile -Command "Invoke-Expression (${%~f0} | Out-String)" @pause @exit /b #> Write-Host this is from powershell! ``` 原理是利用 cmd 允许(其实大部分 shell 也都允许)重定向出现在一行中的任意位置,开头的 <# : 经过处理后去掉了重定向,就只剩下一个冒号了。而冒号在 cmd 中是标签,不会执行任何动作,于是第一行什么事都不会做,也符合语法。最后 exit 退出,cmd 就不会继续解析后面的代码了。到了 PowerShell 执行,开头这块 <# #> 是注释,就直接跳过了。 |
12
chenxytw 2022-07-26 20:58:36 +08:00
其实我更好奇 OP 用的是什么发行版,什么包管理器。
|
14
24bit 2022-07-26 21:19:04 +08:00
在另一个脚本语言的某个脚本中见过这种写法,挺巧妙的
|
15
Nitroethane 2022-07-26 21:57:34 +08:00 via iPhone
@chenxytw 至少不是 Arch ,Arch 默认安装的 python 中的 pip 是纯 py 代码
|
16
qbqbqbqb 2022-07-26 22:11:46 +08:00 3
@ysc3839 Python 不会从 coding: utf-8 之后执行。
Python 能正确跳过上面那行 bash 的原因,是因为编写的时候故意加了双引号,Python 就把它当成字符串了。单写一个字符串,但又不赋值给变量,也不写在表达式或函数里,当然对程序执行流程没有影响了。 不然的话,如果仅仅是编写 bash 脚本,“exec”没必要加双引号。 |
17
Firxiao 2022-07-26 22:20:48 +08:00
#!/bin/sh
"exec" "$(dirname $0)/python3.10" "$0" "$@" 与 #!/usr/bin/env python3.10 等效 其实就是定义下去哪加载 Python 楼主可以想下 如何用 shell 运行 Python 脚本 比如 ./hello.py 该怎么搞? 哈哈 |
18
ysc3839 2022-07-26 22:24:38 +08:00 via Android
@Firxiao 两者并不等效,dirname $0 是取当前脚本文件所在目录,执行的是和脚本文件同目录的 python3.10 。而 env 会查找 PATH 环境变量来执行对应程序。
|
19
Firxiao 2022-07-26 22:37:59 +08:00
@ysc3839 等效指的是 都是去指定 Python 当然如你所说指定的不一样而已 不过 第二种方式楼主应该会比较好理解 😄
|
20
lovelylain 2022-07-26 23:34:44 +08:00 via Android
这种写法应该是改进版,可以随意放置 python 目录,方便打包发布。我记得几年以前我用 pyvenv 生成的执行环境,第一行是写死的 python 完整路径,这就导致打包到其他机器必须保持路径完全一样,但
|
21
lovelylain 2022-07-26 23:36:22 +08:00 via Android
这种写法应该是改进版,可以随意放置 python 目录,方便打包发布。我记得几年以前我用 pyvenv 生成的执行环境,第一行是写死的 python 完整路径,这就导致打包到其他机器必须保持路径完全一样,局限性很大,改成这种跟 shell 结合的写法,就不受这个限制了。妙!
|
22
xujinkai 2022-07-26 23:41:30 +08:00
想起来有个同事把升级包数据拼接在一个 shell 脚本后边,做成了一个自执行的升级程序
|
23
MiketsuSmasher OP @Nitroethane 这个你真说错了,我给的代码就是来自 Arch 的 python-pip 包的 /usr/bin/pip
|
24
MiketsuSmasher OP @Nitroethane 不好意思,刚刚看错了,Arch 的 pip 确实是纯 py
|
25
MiketsuSmasher OP @chenxytw 这个 python 是从这里下载的: https://github.com/indygreg/python-build-standalone/releases
|
26
rev1si0n 2022-07-27 10:34:56 +08:00
骚操作,学到了
|