免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123
最近访问板块 发新帖
楼主: FH
打印 上一主题 下一主题

关于 bash 模拟 sh 时针对环境变量的讨论 [复制链接]

论坛徽章:
0
21 [报告]
发表于 2007-01-10 02:28 |只看该作者
原帖由 FH 于 2007-1-9 19:19 发表
楼上的确很细致,佩服!
俺的是RHEL-AS4,俺也是man了以后找到的这个方法。
俺试了一下,如果直接在shell下面执行sh,是能够继承PS1的,但是进入了vi就不行了,从其它程序访问sh应该也是这样(推理),但是同样 ...


写个程式测看看不就知道了呼 ? 下面测试环境的 ENV 变数都没有指定任何内容。


  1. Linux:kendlee@~/tmp> cat test.c
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/wait.h>

  7. int main()
  8. {
  9.   pid_t child_p;

  10.   printf("getenv(\"PS1\") return %s\n\n",getenv("PS1"));

  11.   child_p = fork();

  12.   if (child_p == 0) {
  13.     printf("Executing sh program !\n");
  14.     execlp("sh","sh",NULL);
  15.   }

  16.   else {
  17.     waitpid(child_p,NULL,0);
  18.     printf("end... see you !\n");
  19.   }

  20.   return 0;
  21. }

复制代码


看一下执行结果,这是有是先把 PS1 设定为 export variable:


  1. Linux:kendlee@~/tmp> ./test
  2. getenv("PS1") result: Linux:\u@\w>

  3. Executing sh program !

  4. Linux:kendlee@~/tmp>
  5. Linux:kendlee@~/tmp> echo $PS1
  6. Linux:\u@\w>
复制代码


看起来 child process 还是有继承到。

再看看另外一个执行结果,这是有是先把 PS1 设定为一般变数,也就是取消非 export  variable 性质:


  1. Linux:kendlee@~/tmp> ./test
  2. getenv("PS1") result: (null)

  3. Executing sh program !

  4. sh-3.1$
复制代码


PS1 不是 export variable,所以 child 没有 PS1 变数继承下来,所以最终显示 sh$ 提示字元。

所以这边讨论来看,当初我最前面提到文章内部份的描述并没错误,这就是我会问你是否没有把 PS1 设定为 export variable 的原因点,只是你没正面回应我,只是一直告诉我搞不清楚何谓 interactive mode ... 因为实际一般测试方式就是在命令列输入执行 sh 来测试,为 export variable 时是会继承下去。

另外拿 mc 来测试一下好了...... 结果与上面也相同。

那问题拉回来看,是真的为变态的 bash 的问题吗?老实说我认为问题搞不好与 vi 本身有关系。

然后我去看了一下 vim7 的 source code :


  1.     if (execl("/bin/sh", "sh", "-c", cmd, NULL) == -1)
复制代码


发现原来是使用 /bin/sh -c 方式来执行命令,来看 bash manpage:


  1. An  interactive  shell  is one started without non-option arguments and
  2.        without the -c option whose standard input and error are both connected
  3.        to  terminals  (as determined by isatty(3)), or one started with the -i
  4.        option.  PS1 is set and $- includes i if bash is interactive,  allowing
  5.        a shell script or a startup file to test this state.
复制代码


所以因为有传 -c,这时候不是 interactive mode,简单说就是为 non-interactive mode。

那再看一下 bash info page 文件:


  1. Alternatively, startup scripts may examine the variable `PS1'; it is
  2. unset in non-interactive shells, and set in interactive shells.  Thus:

  3.      if [ -z "$PS1" ]; then
  4.              echo This shell is not interactive
  5.      else
  6.              echo This shell is interactive
  7.      fi

复制代码


所以说 non-interactive 时 PS1 会被 unset 而消失掉,所以不会有复制继承功能。

FH 兄你说会继承,这下真的奇怪了...... 若是真的会继承,那可能原因大概只有这点: "你系统用的 vi 程式码内容写法不一样",比方用 execlp 直接呼叫外部程式,不透过 sh -c 来达成,那我想这样 PS1 就真的会复制继承下去而不会被 unset 掉。

关于最后论点,其他人不知道有其他想法呼?

--

[ 本帖最后由 kenduest 于 2007-1-10 03:22 编辑 ]

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
22 [报告]
发表于 2007-01-10 02:50 |只看该作者
不妨讓我們做做實驗,來進一步釐清問題所在。

如下是版本資訊:
kenny@x40:~> bash --version
GNU bash, version 3.1.17(1)-release (i586-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

實驗一:如下是針對 bash 的測試:
kenny@x40:~> echo "$PS1"
$(ppwd \l)\u@\h:\w>                         #這裡是原 PS1 值
kenny@x40:~> echo $ENV
/etc/bash.bashrc                        #這是原 $ENV 路經
kenny@x40:~> PS1="xxx: "        #修改 PS1,但沒有輸出為 env var
xxx: bash                                        #進入 bash
kenny@x40:~> exit                        #prompt 被 reset 了,看來是被 .bashrc 修改了
exit                                                #退出
xxx: bash --norc                                #再次進入 bash,但使用了 --norc 選項
bash-3.1$ exit                                # 結果 prompt 設為 bash 的 default 值
exit                                                #退出
xxx: export PS1                                #將 PS1 輸出為 env var
xxx: bash                                        #進入 bash
kenny@x40:~> exit                        #似乎 export 前後沒產生變化
exit                                                #退出
xxx: bash --norc                                #使用 --norc
xxx: exit                                        #注意!這次不是 default 值,
                                                        #而是跟 parent 一致,這說明了 env var 是可以繼承的
exit                                                #退出

實驗二:為了還原環境,開一個新的 bash sehll 來測試 sh :
kenny@x40:~> echo "$PS1"
$(ppwd \l)\u@\h:\w>                         #原設 PS1 定值
kenny@x40:~> echo $ENV
/etc/bash.bashrc                        #這是原 $ENV 路經
kenny@x40:~> PS1="xxx: "        #修改 PS1,但沒有輸出
xxx: sh                                                #進入 sh
kenny@x40:~> exit                        # 跟 bash 一樣的結果
exit                                                #退出
xxx: sh --norc                                #嘗試 --norc
kenny@x40:~> exit                        #也一樣,沒影響
exit                                                #退出
xxx: export PS1                                #將 PS1 輸出為 env
xxx: sh                                                #進入 sh
kenny@x40:~> exit                        #還是被 reset ,應是執行 ENV 路徑的關係
exit                                                #退出
xxx: sh --norc                                #使用 --norc
kenny@x40:~> exit                        #還是不受影響,ENV 依然有效
exit

結論一:
由上面的測試,可以看到如下的結論:
1)bash 在 --norc 選項下,并不執行 .bashrc
2)sh 有沒 --norc 選項,都會展開 $ENV 并執行之
3)無論如何,當 parent shell 的 PS1 被輸出為 env var 之後,
        就會被 sub shell 繼承,只是最後結果又被 .bashrc 或 $ENV 的 script 修改了。
        換句話來說:
                當 new shell 沒有 PS1 之賦值時(可從 .bashrc 或 $ENV 的 script 來設定),
                shell prompt 會採用 default 值:\s-\v\$
        * 我相信這就是 FH 兄整個問題的原因所在了。

實驗三:為了驗證上面第 3 點,以下專門針對 ENV 的有無進行測試:

kenny@x40:~> echo "$PS1"
$(ppwd \l)\u@\h:\w>                         # 這是原 PS1 值
kenny@x40:~> unset ENV                # 取消 ENV
kenny@x40:~> echo "$ENV"
                                                        #確認 ENV 已被取消
kenny@x40:~> PS1="xxx: "        #修改 PS1,但不做輸出
xxx: bash                                        #進入 bash
kenny@x40:~> exit                        #看來不受 ENV 影響
exit                                                #退出
xxx: bash --norc                                #使用 --norc
bash-3.1$ exit                                #被設為 bash default,看來也與 ENV 無關
exit                                                #退出
xxx: export PS1                                #將 PS1 輸出
xxx: bash                                        #進入 bash
kenny@x40:~> exit                        #一樣,看來還是被 .bashrc 改了
exit                                                #退出
xxx: bash --norc                                #不用 rc
xxx: exit                                        #注意!這次從 env 裡繼承了 PS1 值
exit

結論二:
從上面看來,bash 根本就不受到 $ENV 的影響,但卻受到 .bashrc 影響。
實驗一跟實驗三基本上沒有差異。

實驗四:下面驗證 sh:

kenny@x40:~> echo "$PS1"
$(ppwd \l)\u@\h:\w>                         #這是原 PS1 值
kenny@x40:~> unset ENV                #取消 ENV
kenny@x40:~> echo "$ENV"
                                                        #確認取消
kenny@x40:~> PS1="xxx: "        #修改 PS1,但不輸出
xxx: sh                                                #進入 sh
sh-3.1$ exit                                        #被設為 default
exit                                                #退出
xxx: sh --norc                                #使用 --norc
sh-3.1$ exit                                        #看來沒影響
exit                                                #退出
xxx: export PS1                                #輸出 PS1
xxx: sh                                                #進入 sh
xxx: exit                                        #繼承了 PS1!
exit                                                # 退出
xxx: sh --norc                                #使用 --norc
xxx: exit                                        #還是繼承 PS1!
exit

結論三:
看來, sh 的確是受到 ENV 的影響而改變了 PS1 的值,但卻不受 .bashrc 影響。

實驗五:專門針對 PS1 的賦值來測試:
kenny@x40:~> unset ENV                #取消 ENV
kenny@x40:~> export PS1="xxx: "        #修改 PS1,并一同輸出
xxx: bash --norc                                #以 --norc 進入 bash,避免 PS1 被修改
xxx: exit                                        #繼承了 PS1
exit                                                #退出
xxx: sh                                                #進入 sh
xxx: exit                                        #繼承了 PS1
exit                                                #退出
xxx: unset PS1                                #取消 PS1,這是測試重點!!
bash --norc
bash-3.1$ exit                                #bash 的 PS1 設為 default
exit                                                #退出
sh
sh-3.1$ exit                                        #sh 的 PS1 也為 default
exit

所以,這印證了結論一第三點,也就是:
---如果 PS1 為 env,那就繼承;如果沒賦值,就使用 default。

實驗六:再來針對 FH 兄關於 vi 的疑問:
kenny@x40:~> unset ENV                #取消 ENV
kenny@x40:~> export PS1="xxx: "        #修改 PS1,并一同輸出
xxx: set | grep 'PS[0-9]'                #抓取 PS 相關的值
PS1='xxx: '                                        #確認 PS1 被修改
PS2='> '
PS4='+ '

xxx: vi                                        #進入 vi
                                                #然後我執行--- :!set | grep 'PS[0-9]'
PS4='+ '                                        #結果只有 PS4 (哈別去問 sony 哦)

Press ENTER or type command to continue
                                                #然後我再執行--- :!sh
sh-3.1$ exit                                #結果 PS1 設為 default,印證了結論一第 3 點。
exit                                        #返回 vi

Press ENTER or type command to continue
                                                #退出 vi
xxx:

------
好了,實驗結束。
我不敢保證我的實驗是完整而正確的,這有待其它朋友進一步來檢驗。
但從結果來看,我會認同 kenduest 兄的說法。
至於 FH 兄,在下的建議如下:
1)可能兄台平時在清茶跟人吵架習慣了,大可不必將此習性帶到技術版面裡。
2)謙虛永遠是受益的,這跟年紀或經驗無關。
3)嚴謹的實驗是有必要的,如果只是一兩個現象就過早歸納結論,有時會有錯漏。
4)假如我批評得不對,請您原諒。

謝謝!

论坛徽章:
0
23 [报告]
发表于 2007-01-10 08:44 |只看该作者
to 楼上:
export PS1俺是绝对不会疏忽遗忘的,因此才有变态一说。
刚刚在SUSE LINUX Enterprise Server 9 (i586) - Kernel 2.6.5-7.97-bigsmp上试验,结果一如RHEL-AS4。

xxx@linux:~> echo $ENV

xxx@linux:~> vi
:!sh
sh-2.05b$ exit
:q!
xxx@linux:~> echo $PS1
\u@\h:\w>
xxx@linux:~> export PS1
xxx@linux:~> vi
:!sh
sh-2.05b$ exit
:q!

[ 本帖最后由 FH 于 2007-1-10 08:46 编辑 ]

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
24 [报告]
发表于 2007-01-10 10:10 |只看该作者
前面我們已經給出結論了:
---如果 PS1 為 env,那就繼承;如果沒賦值,就使用 default。

kenduest 兄也驗證出 vi 所調用的 shell 不是 interactive,因為有 -c 選項。
因此 vi 的 shell 并沒有 PS1, 也就是“如果沒賦值,就使用 default”。
這樣你明白了嗎?

论坛徽章:
1
白银圣斗士
日期:2015-11-23 08:33:04
25 [报告]
发表于 2007-01-10 22:36 |只看该作者
改版咯...好像回到以前的界面了..好熟悉啊

论坛徽章:
0
26 [报告]
发表于 2007-01-10 22:56 |只看该作者
原帖由 FH 于 2007-1-10 08:44 发表
to 楼上:
export PS1俺是绝对不会疏忽遗忘的,因此才有变态一说。
刚刚在SUSE LINUX Enterprise Server 9 (i586) - Kernel 2.6.5-7.97-bigsmp上试验,结果一如RHEL-AS4。


你可以把你说的 AIX 与 HPUX 环境测试 vi 执行使用 :!sh 后情况也贴出来一下吗?这样好判断问题点.

--

[ 本帖最后由 kenduest 于 2007-1-10 23:10 编辑 ]

论坛徽章:
0
27 [报告]
发表于 2008-02-16 22:13 |只看该作者
感觉网中人的实验结论正确但不太全面,有一些疑惑:
1、$BASH_ENV 和 $ENV 是一样的吗?
2、sh的--norc参数有没有意义?


以下个人的一些理解之后的总结,如果有偏差还望指点:

BASH

1、交互式的登录shell
载入的信息:
/etc/profile
~/.bash_profile( ->  ~/.bashrc  ->  /etc/bashrc)
~/.bash_login
~/.profile

2、非交互的登录shell
载入的信息:
/etc/profile
~/.bash_profile ( ->  ~/.bashrc  ->  /etc/bashrc)
~/.bash_login
~/.profile
$BASH_ENV

3、交互式的非登录shell
载入的信息:
~/.bashrc ( ->  /etc/bashrc)

4、非交互式的非登录shell
载入的信息:
$BASH_ENV


SH

1、交互式的登录shell
载入的信息:
/etc/profile
~/.profile

2、非交互的登录shell
载入的信息:
/etc/profile
~/.profile

3、交互式的非登录shell
载入的信息:
$ENV

4、非交互式的非登录shell
载入的信息:
nothing

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
28 [报告]
发表于 2008-02-17 20:52 |只看该作者
感謝 wwy 兄的指教與補充。
不過,您的問題,我一時沒能驗證。且憑直覺回答如下:
1)應該不一樣。如果帶了 BASH_ 的字眼,應該只有 bash 才會參考。就算 sh 是 bash 的 link,也不參考。
2)這個還需進一步在不同的版本測試。不敢妄答。

论坛徽章:
0
29 [报告]
发表于 2008-02-17 21:27 |只看该作者
学到了不少东西,收藏了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP