V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
LinkedIn
MiketsuSmasher
V2EX  ›  Linux

看 pip 的启动脚本看得我人都傻了,还能把 shell 和 py 揉到一起写

  •  
  •   MiketsuSmasher ·
    nukemiko · 64 天前 · 4157 次点击
    这是一个创建于 64 天前的主题,其中的信息可能已经有所发展或是发生改变。

    /usr/bin/pip3.10

    #!/bin/sh
    "exec" "$(dirname $0)/python3.10" "$0" "[email protected]"
    # -*- 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" "[email protected]" 之后的代码还能接着执行呢?

    以及,为什么要这样写呢,拆开写不好吗?

    26 条回复    2022-07-27 10:34:56 +08:00
    qwq11
        1
    qwq11  
       64 天前 via Android
    因为他 exec 启动 py 来执行当前脚本,第二行是一坨字符串直接被 py 忽略,然后 py 就正常往下执行
    ruanimal
        2
    ruanimal  
       64 天前   ❤️ 1
    其实就是用 shell 启动了 python

    $0 是当前文件路径
    [email protected] 是所有命令行参数

    "exec" 这一行是字符串,在 python 解释器中是没有效果的
    AoEiuV020CN
        3
    AoEiuV020CN  
       64 天前
    > 之后的代码还能接着执行呢?
    shell 进程就到此为止了,没有接着执行,
    exec 创建了 python 进程才真正执行后面的代码,

    就是为了强制使用"$(dirname $0)/python3.10",不用管 python 到底在哪里吧,
    qwq11
        4
    qwq11  
       64 天前 via Android
    至于为什么混在一起写,我猜是因为,分成两个文件(pip.shpip.py)会比较迷惑人。本来 py 版本管理就乱,一个 py 还整两个 pip 入口
    BeautifulSoap
        5
    BeautifulSoap  
       64 天前
    论 shell 脚本有多丑多难看,但又多好用
    geelaw
        6
    geelaw  
       64 天前
    https://en.wikipedia.org/wiki/Polyglot_(computing)

    好处是在 shell 脚本里你既可以写 pip foo 也可以写 python pip foo ,前者的效果就是 python pip foo 。后面的代码当然没有“接着”执行,因为 shell script interpreter 进程已经被替换了,替换后的进程执行了其他代码,而这个其他的代码,刚好就是同一份,而且替换后的进程是按 Python 解读这份代码。
    MiketsuSmasher
        7
    MiketsuSmasher  
    OP
       64 天前
    @qwq11
    @ruanimal
    @AoEiuV020CN
    @qwq11
    感谢解惑

    @geelaw 是我见识少了,C & bash & PHP 的杂糅让我叹为观止🤣
    webcape233
        8
    webcape233  
       64 天前 via iPhone
    实在是骚 学会了
    hsfzxjy
        9
    hsfzxjy  
       64 天前 via Android
    妙啊
    ysc3839
        11
    ysc3839  
       64 天前   ❤️ 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 执行,开头这块 <# #> 是注释,就直接跳过了。
    chenxytw
        12
    chenxytw  
       64 天前
    其实我更好奇 OP 用的是什么发行版,什么包管理器。
    chenxytw
        13
    chenxytw  
       64 天前
    @chenxytw 这个内容和印象中正常途径打包出来的不一样....多了 sh 处理的部分 Orz
    24bit
        14
    24bit  
       64 天前
    在另一个脚本语言的某个脚本中见过这种写法,挺巧妙的
    Nitroethane
        15
    Nitroethane  
       64 天前 via iPhone
    @chenxytw 至少不是 Arch ,Arch 默认安装的 python 中的 pip 是纯 py 代码
    qbqbqbqb
        16
    qbqbqbqb  
       64 天前   ❤️ 3
    @ysc3839 Python 不会从 coding: utf-8 之后执行。

    Python 能正确跳过上面那行 bash 的原因,是因为编写的时候故意加了双引号,Python 就把它当成字符串了。单写一个字符串,但又不赋值给变量,也不写在表达式或函数里,当然对程序执行流程没有影响了。

    不然的话,如果仅仅是编写 bash 脚本,“exec”没必要加双引号。
    Firxiao
        17
    Firxiao  
       64 天前
    #!/bin/sh
    "exec" "$(dirname $0)/python3.10" "$0" "[email protected]"

    #!/usr/bin/env python3.10
    等效

    其实就是定义下去哪加载 Python
    楼主可以想下 如何用 shell 运行 Python 脚本 比如 ./hello.py 该怎么搞? 哈哈
    ysc3839
        18
    ysc3839  
       64 天前 via Android
    @Firxiao 两者并不等效,dirname $0 是取当前脚本文件所在目录,执行的是和脚本文件同目录的 python3.10 。而 env 会查找 PATH 环境变量来执行对应程序。
    Firxiao
        19
    Firxiao  
       64 天前
    @ysc3839 等效指的是 都是去指定 Python 当然如你所说指定的不一样而已 不过 第二种方式楼主应该会比较好理解 😄
    jobmailcn
        20
    jobmailcn  
       64 天前 via Android
    这种写法应该是改进版,可以随意放置 python 目录,方便打包发布。我记得几年以前我用 pyvenv 生成的执行环境,第一行是写死的 python 完整路径,这就导致打包到其他机器必须保持路径完全一样,但
    jobmailcn
        21
    jobmailcn  
       64 天前 via Android
    这种写法应该是改进版,可以随意放置 python 目录,方便打包发布。我记得几年以前我用 pyvenv 生成的执行环境,第一行是写死的 python 完整路径,这就导致打包到其他机器必须保持路径完全一样,局限性很大,改成这种跟 shell 结合的写法,就不受这个限制了。妙!
    xujinkai
        22
    xujinkai  
       64 天前
    想起来有个同事把升级包数据拼接在一个 shell 脚本后边,做成了一个自执行的升级程序
    MiketsuSmasher
        23
    MiketsuSmasher  
    OP
       64 天前
    @Nitroethane 这个你真说错了,我给的代码就是来自 Arch 的 python-pip 包的 /usr/bin/pip
    MiketsuSmasher
        24
    MiketsuSmasher  
    OP
       64 天前
    @Nitroethane 不好意思,刚刚看错了,Arch 的 pip 确实是纯 py
    MiketsuSmasher
        25
    MiketsuSmasher  
    OP
       64 天前
    rev1si0n
        26
    rev1si0n  
       64 天前
    骚操作,学到了
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3166 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 65ms · UTC 04:37 · PVG 12:37 · LAX 21:37 · JFK 00:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.