首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
宝塔
V2EX  ›  程序员

Bash 默认把 PATH 传给子 bash,请问是出于什么考虑?

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

    即使不 export,bash 里再开的 bash(子 bash),也能继承到 PATH 这个 shell 变量,请问,这在平常的脚本编程中有什么用途?不然,bash 为什么这样设计呢?

    第 1 条附言  ·  355 天前
    可能我表述的不明白.大家都在说"环境变量"的问题.
    PATH 在 bash 里本质上还是一个普通的 shell 变量,需要 export 才能生效. export 应该就是一个 setenv()的过程.
    但是 PATH 变量又稍微有些特殊,你在 bash 里定义一个 foo 变量,只要你不 export foo,子 bash 是不继承这个 foo 的,但 PATH 变量,不管你 export 与否,子 bash 都会继承.
    第 2 条附言  ·  354 天前
    感谢提醒,补充在 bash 上测试的试验,这个 bash 是通过-norc 运行起来的,"子 bash"也是通过-norc 运行的.

    bash-4.3$ a=1
    bash-4.3$ PATH=$PATH:xxx
    bash-4.3$ echo $PATH
    /usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:xxx
    bash-4.3$ echo $a
    1
    bash-4.3$ bash -norc
    bash-4.3$ echo $PATH
    /usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:xxx
    bash-4.3$ echo $a

    bash-4.3$

    可以看到 PATH 被传递给子 shell 了,但变量 a 没有.
    33 回复  |  直到 2018-12-10 10:36:42 +08:00
        1
    neoblackcap   355 天前 via iPhone
    不是 bash 是这样设计,是 unix 环境下,子进程默认继承父进程的环境变量
        2
    flynaj   355 天前 via Android
    不止 Linux,Windows 也会传环境变量到子进程
        3
    wd   355 天前 via iPhone
    这不是环境变量么,那在这个环境执行的命令不就会继承么?
        4
    WordTian   355 天前 via Android
    要是 path 不继承的话,那子 bash 就没有什么命令可用了
        5
    undeflife   355 天前
    bash shell 不同状态读取的配置文件优先级

    https://www.solipsys.co.uk/images/BashStartupFiles1.png
        6
    widewing   355 天前 via Android
    ???难道其他变量没有被继承吗?
        7
    binux   355 天前
    PATH 是环境变量不是 shell 变量啊
        8
    dangyuluo   354 天前
    Quote form Wikipedia:

    PATH is an environment variable on Unix-like operating systems, DOS, OS/2, and Microsoft Windows,
        9
    TonyLiu2ca   354 天前
    man bash
        10
    whileFalse   354 天前
    没有 PATH 跑不起来好吗。
        11
    gowa2017   354 天前 via iPhone
    loginshell fork child bash 采用了 cow 技术 刚开始的时候是共享内存空间的
        12
    iwtbauh   354 天前 via Android
    并没有传给子进程

    只是新的 bash 初始化时读取了配置文件然后自己创建了 PATH 变量

    请 lz 尝试:

    unset PATH
    PATH="foo"
    /bin/bash
    echo "$PATH"
        13
    lululau   354 天前
    只听说过子进程和子 Shell,子 Bash 是什么鬼
        14
    weyou   354 天前 via Android
    系统启动的时候 PATH 已经是环境变量了,你对它做出的任何修改它还是一个环境变量,所以都会被子进程继承。这有啥奇怪的呢。
        15
    chinvo   354 天前 via iPhone
    你把 foo 塞进系统环境变量里,“不 export ”也能继承。所有已经是环境变量的变量,包括 path、lc_* 等,都是这样的
        16
    cnt2ex   354 天前
    环境变量的传递不是 bash 完成的,是操作系统完成的。在创建子进程的时候操作系统把带有环境变量的那部分内存区域拷贝到子进程的内存完成了环境变量的传递。
        17
    momocraft   354 天前
    先讲清楚你的"子 bash" 是什么东西, 比如附上你用的命令, 我们才能继续讨论环境变量是如何初始化的
        18
    DinoStray   354 天前 via iPhone
    @chinvo 第一次知道还有这个区别,那怎样算放入系统环境变量呢?要放在哪个文件里?
        19
    ihainan   354 天前
    还真是这样,这个我都没注意过,习惯性都是加上 export。
        20
    rrfeng   354 天前
    楼主的总结恰好是反的

    环境变量会传递给子进程,但是 PATH 恰恰是一个很可能子进程(特别是 bash )会自己重载的环境变量
        21
    jdhao   354 天前 via Android
    @momocraft 正解,这一群回答的人基本都是在乱讲,没几个说到点子的
        22
    weyou   354 天前 via Android
    @jdhao 他都没解,哪里来的“正解”?

    “子 bash ”这个叫法虽然很奇怪,但一看就知道是一个 bash 里调用的另一个 bash 啊
        23
    jdhao   354 天前 via Android
    @weyou 怎么调的?用了什么命令?原文啥都没说
        24
    autumn2018   354 天前
    @jdhao
    bash-4.3$ a=1
    bash-4.3$ PATH=$PATH:xxx
    bash-4.3$ echo $PATH
    /usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:xxx
    bash-4.3$ echo $a
    1
    bash-4.3$ bash -norc
    bash-4.3$ echo $PATH
    /usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:xxx
    bash-4.3$ echo $a

    bash-4.3$
        25
    no1xsyzy   354 天前
    重点不是传递,而是设置 PATH 会直接修改当前环境变量

    [email protected]:~$ PATH=foo
    [email protected]:~$ bash
    Command 'bash' is available in '/bin/bash'
    The command could not be located because '/bin' is not included in the PATH environment variable.
    bash: command not found

    而且其他的只要 export 过后修改也会改变环境变量

    [email protected]:~$ export a=1
    [email protected]:~$ a=2
    [email protected]:~$ /usr/bin/env | /bin/grep "^a="
    a=2

    所以应该是和 @chinvo #15 说的一样,一个变量一但和环境产生关联就会一直绑定。
        26
    no1xsyzy   354 天前
    help export
    help declare | grep -- -x

    export 不是设置环境变量,而是设置变量类型为导出类型,即这个变量的类型是个环境变量。
    export foo=bar 只是个语法糖。
    先 export -n PATH 这一现象就会消失。

    [email protected]:~$ export -n PATH
    [email protected]:~$ PATH=foo
    [email protected]:~$ bash
    Command 'bash' is available in '/bin/bash'
    The command could not be located because '/bin' is not included in the PATH environment variable.
    bash: command not found
    [email protected]:~$ /bin/bash -c 'echo $PATH'
    /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:.
        27
    iwtbauh   354 天前 via Android
    @autumn2018 #24

    你并没有在修改 PATH 之前 unset 它,PATH 是环境变量当然会传递给子进程
        28
    weyou   354 天前 via Android
    @jdhao 不管怎么调的,调用了什么,这个问题的答案都是一样的。一个环境变量只要没有被 unset,都会被所有的子进程(本贴中子 bash 也是子进程)继承。就算被修改了也还是环境变量依然会被继承。
        29
    xiaoshenke   354 天前
    因为是环境变量啊 bash 自然是做了特殊处理了
        30
    julyclyde   353 天前   ♥ 2
    先说结论:
    因为 PATH 变量是从上一层继承过来的,默认就是环境变量,所以在 shell 这一层不需要通过 export 命令让它成为环境变量

    再说证据:
    http://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c 的 execute_disk_command 函数里执行了
    exit (shell_execve (command, args, export_env));
    其中 export_env 是从 variables.c 里边来的,被 execute_disk_command 调用 shell_execve 之前稍早几句的 maybe_make_export_env (); 写入内容的一个数组

    再看 variables.c 文件,maybe_make_export_env 调用了 make_var_export_array,后者又调用了 export_environment_candidate,而最后这个函数只有一句话:
    return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
    关键在于 imported_p。这个 imported_p 并不是个函数而是一个宏,在 variables.h 里定义的,判断条件里的 att_imported 吸引了我。再搜 att_imported,发现 initialize_shell_variables 函数执行的时候会对部分变量标记 att_imported ( att 即 attribute )

    最后说值的继承关系:
    在 systemd 为基础的 Linux 发行版里,systemd-exec(5)准备了固定值 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    检查 /proc/pid/environ 可以发现,sshd 和 getty 的 PATH 环境变量值均于此一致
        31
    julyclyde   353 天前
    看了看前边的回答,觉得有不少人理解能力有问题
        32
    Kobayashi   350 天前 via Android
    没有 PATH 变量,外部程序去哪里调用……
        33
    momocraft   345 天前
    我又看了一下,PATH 可能是默认就 export 的环境变量 (我机器上用没有.bashrc/.bashprofile 的用户 declare -p PATH 也会看到-x)。

    还没有找到这个 export 是在哪个级别决定的 (bash 固定,或哪个全局配置文件)。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   970 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 22:12 · PVG 06:12 · LAX 14:12 · JFK 17:12
    ♥ Do have faith in what you're doing.