
/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 年 7 月 26 日 via Android 因为他 exec 启动 py 来执行当前脚本,第二行是一坨字符串直接被 py 忽略,然后 py 就正常往下执行 |
2 ruanimal 2022 年 7 月 26 日 其实就是用 shell 启动了 python $0 是当前文件路径 $@ 是所有命令行参数 "exec" 这一行是字符串,在 python 解释器中是没有效果的 |
3 AoEiuV020CN 2022 年 7 月 26 日 > 之后的代码还能接着执行呢? shell 进程就到此为止了,没有接着执行, exec 创建了 python 进程才真正执行后面的代码, 就是为了强制使用"$(dirname $0)/python3.10",不用管 python 到底在哪里吧, |
4 qwq11 2022 年 7 月 26 日 via Android |
5 BeautifulSoap 2022 年 7 月 26 日 论 shell 脚本有多丑多难看,但又多好用 |
6 geelaw/a> 2022 年 7 月 26 日 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 年 7 月 26 日 via iPhone 实在是骚 学会了 |
9 hsfzxjy 2022 年 7 月 26 日 via Android 妙啊 |
10 kokutou 2022 年 7 月 26 日 |
11 ysc3839 2022 年 7 月 26 日 大部分 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 年 7 月 26 日 其实我更好奇 OP 用的是什么发行版,什么包管理器。 |
14 24bit 2022 年 7 月 26 日 在另一个脚本语言的某个脚本中见过这种写法,挺巧妙的 |
15 Nitroethane 2022 年 7 月 26 日 via iPhone @chenxytw 至少不是 Arch ,Arch 默认安装的 python 中的 pip 是纯 py 代码 |
16 qbqbqbqb 2022 年 7 月 26 日 @ysc3839 Python 不会从 coding: utf-8 之后执行。 Python 能正确跳过上面那行 bash 的原因,是因为编写的时候故意加了双引号,Python 就把它当成字符串了。单写一个字符串,但又不赋值给变量,也不写在表达式或函数里,当然对程序执行流程没有影响了。 不然的话,如果仅仅是编写 bash 脚本,“exec”没必要加双引号。 |
17 Firxiao 2022 年 7 月 26 日 #!/bin/sh "exec" "$(dirname $0)/python3.10" "$0" "$@" 与 #!/usr/bin/env python3.10 等效 其实就是定义下去哪加载 Python 楼主可以想下 如何用 shell 运行 Python 脚本 比如 ./hello.py 该怎么搞? 哈哈 |
18 ysc3839 2022 年 7 月 26 日 via Android @Firxiao 两者并不等效,dirname $0 是取当前脚本文件所在目录,执行的是和脚本文件同目录的 python3.10 。而 env 会查找 PATH 环境变量来执行对应程序。 |
20 lovelylain 2022 年 7 月 26 日 via Android 这种写法应该是改进版,可以随意放置 python 目录,方便打包发布。我记得几年以前我用 pyvenv 生成的执行环境,第一行是写死的 python 完整路径,这就导致打包到其他机器必须保持路径完全一样,但 |
21 lovelylain 2022 年 7 月 26 日 via Android 这种写法应该是改进版,可以随意放置 python 目录,方便打包发布。我记得几年以前我用 pyvenv 生成的执行环境,第一行是写死的 python 完整路径,这就导致打包到其他机器必须保持路径完全一样,局限性很大,改成这种跟 shell 结合的写法,就不受这个限制了。妙! |
22 xujinkai 2022 年 7 月 26 日 想起来有个同事把升级包数据拼接在一个 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 年 7 月 27 日 骚操作,学到了 |