linux的shell究竟有什么神奇之处。。。
https://blog.youkuaiyun.com/liuyiy/article/details/8063817
Linux登陆主机后,在执行Script之前,其实我们已经处于一个shell中,即Login shell。它是将来要执行Script的父shell。如root账号默认为/bin/bash。
Linux下每个账号都可以自定义Login shell,在/etc/passwd文件中。Login shell定义在第七个字段,如果这个字段的shell程序不存在、不合法,或执行失败,则无法登陆主机。
当在执行一个Shell Script时,父shell会根据Script程序的第一行#!之后指定的shell程序开启一个子shell环境,然后在子shell中执行此Shell Script,一旦执行完毕,子shell结束,回到父shell,不会影响原父shell的环境。
Linux执行Script有三种执行方式:
1.source filename 或者 . filename
注意. Filename中,. 和filename之间有个空格。
此命令式在当前shell环境下读取并执行filename中的命令。该filename文件可以无执行权限。通常用于重新执行刚修改的初始化文档。source命令(从 C Shell 而来)是bash shell的内置命令。 点命令,就是个点符号,(从Bourne Shell而来),就是顺序的执行文件里的命令而已。
2.sh filename 或者 bash filename
此命令是在当前相应的bash环境下新建一个子shell读取并执行FileName中的命令,该filename文件可以无执行权限。
3. ./filename
此命令打开一个子shell来读取并执行filename中命令,该文件必须必须有可执行的权限。chmod +x filename。
三者的区别:
1.当shell脚本具有可执行权限时,用sh filename与./filename执行脚本是没有区别得。./filename是因为当前目录没有在PATH中,所有"."是用来表示当前目录的。
2.sh filename 重新建立一个子shell,在子shell中执行脚本里面的语句,该子shell继承父shell的环境变量,但子shell新建的、改变的变量不会被带回父shell,除非使用export。
3.source filename:这个命令其实只是简单地读取脚本里面的语句依次在当前shell里面执行,没有建立新的子shell。那么脚本里面所有新建、改变变量的语句都会保存在当前shell里面。
举例:
1.新建一个test.sh脚本,内容为:A=1。
2.然后使其可执行chmod +x test.sh。
3.运行sh test.sh后,echo $A,显示为空,因为A=1并未传回给当前shell。
4.运行./test.sh后,也是一样的效果。
5.运行source test.sh 或者 . test.sh,然后echo $A,则会显示1,说明A=1的变量在当前shell 中。
举例:
1.新建一个sh01.sh脚本,内容为
#!/bin/bash
echo $SHLVL (输出当前shell的层次)
2.然后使其可执行chmod +x sh01.sh。
3.运行sh sh01.sh后,输出3。
4.运行./ sh01.sh后,也是一样的效果。
5.运行source sh01.sh 或者 . sh01.sh,输出2。
举例:
1.登录主机后,打开一个终端,输入echo $SHLVL,输出2。
2.输入bash,进入一个子shell,然后再输入echo $SHLVL,输出3。
3.输入exit,然后再输入echo $SHLVL,输出2。
4.输入exit,退出终端。
============
https://blog.youkuaiyun.com/a600423444/article/details/6451111
Login Shell
登录主机后,在执行Bash Script之前,其实我们已经处于一个BashShell中。
这个Shell叫login Shell,是将来我们执行任何Script的上层环境。又叫父SHell
其实每个帐号都可以自定义loginShell。以Linux来说,帐号的login Shell定义在/etc/passwd这个文件中。
/etc/passwd的每一行代表一个帐号,共有7个字段,之间用:隔开。
帐号:x:UID 使用者代码:GID 群组代码:用户信息:主目录:login shell路径
第二栏x为密码栏,基于系统安全考虑,编码后的密码已经被放入/etc/passwd文件中。
login Shell定义在第7个字段,如果这个字段的Shell程序不存在、不合法,或执行失败,则无法登录主机。
父Shell、子Shell
当在执行一个Shell Script时,父Shell会根据Script程序的第一行#!之后指定的Shell程序开启一个子Shell环境,然后在子Shell中执行此Shell Script。一旦子Shell中的Script执行完毕,此子Shell随即结束,回到父Shell中,不会影响父Shell原本的环境。
子Shell环境拥有与父Shell相同的环境变量、标准输入、输出、错误等。
例如:
test.sh文件内容
#!/bin/bash
cd /var/www/html
命令行:chmod +x /test.sh
命令行:./test.sh
执行完脚本后还原到父Shell,并且父Shell并没有进入/var/www/html目录。
注:这是因为当执行Shell文件时,父Shell会创建子Shell,各自独立。
如果需要使用父Shell来执行此脚本,可以使用:
命令行:. ./test.sh
注意.与./之间有一个空格符
子Shell继续开启子Shell
与父Shell启动子Shell方式一样,继续调用下去,即子Shell开启子Shell。
通过$SHLVL变量,可以知道当前所在Shell的层次
=================
当我们在一个 shell 里运行一个脚本程序时,该 shell 就会 fork 出一个新进程,从而启动了另一个命令解释器(由脚本中第一行的 #!/bin/xxx 指定,如 bash shell)来解释运行我们这个脚本。也就是说,这个新进程是一个子 shell,而之前的 shell 是个父 shell 。
在我们所运行的脚本里,我们还可以启动新的子 shell 进程,这些子 shell 进程使脚本并行地运行着多个子任务。一般而言,在一个脚本里执行一个外部命令(普通的可执行文件)时,shell 会 fork 出一个子进程,然后再用 exec 来执行这个程序;但是,bash shell 的内置命令(builtin)却不会这样,它们是直接执行的。所以,等价的内置命令的执行速度会比执行外部命令要来的快。
在一对括号 (...) 里可以放置一组指令,这些指令是在一个子 shell 里执行的。在子 shell 里的变量不能被这段子 shell 外的代码直接访问,也就是说子 shell 里的变量不能被父 shell 所存取,实际上它们是局部变量。
这里可以参考:(( ))和 [[ ]] 和 shell 与 命令的执行 这两篇文章。
下面用一段代码进行测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#!/bin/bash
echo
"Subshell level = $BASH_SUBSHELL"
outer_variable=Outer
outer_variable2=Outer2
(
echo
"Subshell level INSIDE subshell = $BASH_SUBSHELL"
inner_variable=Inner
outer_variable2=Outer_var_changein_subshell
echo
"From Subshell,\"inner_variable\"=$inner_variable"
echo
"From parent shell,\"outer\"=$outer_variable"
echo
"From parent shell, \"outer\"=$outer_variable2"
)
echo
"In parent shell, check \"outer_variable\" value:$outer_variable"
echo
"In parent shell, check \"outer_variable2\" value:$outer_variable2"
echo
echo
"Subshell level OUTSIDE subshell = $BASH_SUBSHELL"
echo
if
[ -z
"$inner_variable"
]
then
echo
"inner_variable undefined in main body of shell"
else
echo
"From main body of shell,\"inner_variable\"=$inner_variable"
fi
exit
0
|
运行输出:
引用
beyes@debian:~/shell$ ./subshell.sh
Subshell level = 0
Subshell level INSIDE subshell = 1
From Subshell,"inner_variable"=Inner
From parent shell,"outer"=Outer
From parent shell, "outer"=Outer_var_changein_subshell
In parent shell, check "outer_variable" value:Outer
In parent shell, check "outer_variable2" value:Outer2
Subshell level OUTSIDE subshell = 0
inner_variable undefined in main body of shell
在上面的代码中,
BASH_SUBSHELL 是一个环境变量,它表示进入子 shell 的层级,比如处于当前 shell 时,该变量值为 0;当在当前 shell 派生的子 shell 里时,该变量值为 1;如果该子 shell 又派生出一个子 shell,那么该变量在此间的值就为 3,以此类推。
在代码中,( ) 里的代码段是在子 shell 里执行的,而 inner_variable 作为局部变量,它的值可以在 ( ) 这段代码里 echo 出来,但是一旦返回到父shell 时,它就是未定义的,所以会输出“ inner_variable undefined in main body of shell”。也就是说,局部变量不能被外部代码所访问。
从输出可以看到,在子 shell 中和父 shell 中变量 outer_variable 的输出值是一样的;相对应的 outer_variable2 变量即使在子 shell 中进行了修改,但是当返回到父 shell 对其输出时,它却还是父 shell 中原来所赋的值。从这里可以看出,子 shell 可以 “感知” 父 shell 中的变量,但它不能修改它。其本质的原因和 fork() 函数的原理有关。在 UNIX/LINUX 中,fork 出来的子进程实际上是对父进程的一种拷贝,而子 shell 就是父shell fork 出来的一个子进程,所以它理所当然的有了父shell 中的一片拷贝。所以,子 shell 里的 outer_variable 和 outer_variable2 变量虽然和父 shell 的同名,但它们并不是同一个变量,而是父 shell 里的一个副本。
说到父shell 和 子 shell,那么会想到 export 这个命令。export 也是 bash 的一个内置命令。它主要是用来将父 shell 里的变量导出供子 shell 使用。它有如下特征:
1. 用 export 导出的变量放在“导出变量列表”中,它可以被子 shell (子 shell 的子 shell 也是如此)拷贝并使用。
2. 被 export 出来的变量虽然可以被子 shell 使用,但它也只是一个拷贝,而不会影响到父 shell 中的值以及其它子 shell 中的值。
看下面示例;
1. 先在当前 shell 里 export 一个变量:
引用
beyes@debian:~/shell$ export exp8temp="hello world"
beyes@debian:~/shell$ echo $exp8temp
hello world
2. 运行一个脚本 echo 此变量(该脚本只有一句话即 echo $exp8temp ):
引用
$ ./exp8.sh
hello world
由上可见,父 shell 里 export 的变量可以被子 shell 读取。
3. 测试一下子 shell 更改此变量是否会影响父 shell 里的值,子 shell 代码如下:
1
2
3
4
5
|
#!/bin/bash
exp8temp=
"hello shell"
echo
$exp8temp
|
检验上面的情景:
引用
beyes@debian:~/shell$ ./exp8.sh
hello shell
beyes@debian:~/shell$ echo $exp8temp
hello world
可见子 shell 对父 shell 里 export 出来的变量进行修改并不能影响到父 shell。这说明了,子 shell 只是在“导出变量列表“里对该变量进行了一个拷贝。但反过来,父shell再次更改此变量时,子 shell 再去读时,读到的是新值,而不是原来的值。
4. 如果在子 shell 里 export 出的变量,父 shell 是否能读到呢?
先将下面一段代码放在后台运行:
1
2
3
4
5
6
7
|
#!/bin/bash
export
exp9temp=
"hello world"
sleep
30
exit
0
|
然后在在 30 秒内在父 shell 里读取一下 $exp9temp 的值,发现输出为空。所以我们得出结论,export 出来的变量不能导出到父进程或者是父进程的环境里。一个自己称可以继承父进程的东西,而不能反过来去影响父进程。
那么子 shell 有什么办法可以向父 shell 传递自己的变量吗?下面方法可以考虑:
1. 通过一个中间文件进行:
1
2
3
4
5
6
7
8
9
10
|
#!/bin/bash
(
subvar=
"hello shell"
echo
"$subvar"
> temp.txt
)
read
pvar < temp.txt
echo
$pvar
|
运行输出:
引用
$ sh subandp.sh
hello shell
2. 通过命令替换:
引用
#!/bin/bash
pvar=`subvar="hello shell";echo $subvar`
echo $pvar
运行输出:
引用
$ ./subandp.sh
hello shell
执行命令替换符(两个反单引号)之间的命令也是在子 shell 来完成的。
3. 使用命名管道:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/bin/bash
mkfifo
-m 777 npipe
(
subsend=
"hello world"
echo
"$subsend"
> npipe &
)
read
pread < npipe
echo
"$pread"
exit
0
|
运行输出:
引用
beyes@debian:~/shell$ ./var.sh
hello world
关于有名管道创建命令 mkfifo 可参考:
http://www.groad.net/bbs/read.php?tid-3707.html
4. 使用 here 文档:
1
2
3
4
5
6
7
8
|
#!/bin/bash
read
pvar << HERE
`subvar=
"hello shell"
echo
$subvar`
HERE
echo
$pvar
|
运行输出:
引用
$ ./subandp.sh
hello shell
方法应该还有很多,这些方法的本质原理基于进程间的通信。
============
https://www.jianshu.com/p/7db79d7997b5
子shell
运行一个shell脚本时会启动另一个命令解释器. 就好像你的命令是在命令行提示下被解释的一样, 类似于批处理文件里的一系列命令.每个shell脚本有效地运行在父shell(parent shell)的一个子进程里.这个父shell是指在一个控制终端或在一个xterm窗口中给你命令指示符的进程.
shell脚本也能启动他自已的子进程. 这些子shell(即子进程)使脚本并行地,有效率地地同时运行多个子任务.
圆括号运行子shell
嵌在圆括号里的一列命令在一个子shell里运行。
例如:
(
echo abcd
echo cdef
)
输出为:
abcd
cdef
在子shell里的变量不能被这段子shell代码块之外外面的脚本访问.这些变量是不能被产生这个子shell的父进程(parent process)存取的,实际上它们是局部变量(local variables).
例如:
(a=b)
echo $a
输出为空
在子shell中的目录更改不会影响到父shell.
子shell可用于为一组命令设定临时的环境变量
进程在不同的子shell中可以并行地执行.这样就允许把一个复杂的任务分成几个小的子问题来同时地处理。
管道产生子shell
管道(|)也会产生子shell。在子shell中可以读取父shell中的变量,但是不能写这些变量。有时我们可以通过重定向输入输出的方式来传递这些变量。详细的可以参考I/O重定向
作者:Fengya
链接:https://www.jianshu.com/p/7db79d7997b5
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
===============
https://blog.youkuaiyun.com/chenguolinblog/article/details/12870587
Linux是一种用户控制的多作业操作系统,系统允许多个系统用户同时提交作业,而一个系统用户又可能用多个Shell登录,每个系统用户可以用一个Shell提交多个作业
1 子Shell
1 父子Shell是相对的,它描述了两个Shell进程的fork关系,父Shell指在控制终端或窗口给出提示符的进程,子Shell是由父Shell创建的进程,在Linux中,只有一个函数可以创建子进程,那就是fork函数
2 圆括号结构能够强制将其中的命令运行在子Shell中,它的基本格式为
(command1 command2 command....n)
上述结构表示圆括号内的n条命令在子Shell中运行
3 子Shell能够从父Shell继承得来的属性如下
1 当前工作目录
2 环境变量
3 标准输入,标准输出和标准错误输出
4 忽略的信号
5 除了环境变量和.bashrc文件中定义变量之外的Shell变量
6 未被忽略的信号处理
2 进程处理
1 进程和作业是有区别的,一个正在执行的进程称为作业,一个作业可以包含多个进程。用户提交作业到操作系统,作业的完成可能依赖于启动多个进程。因此简单的说,作业是用户层面的概念,而进程是操作系统层面的概念
2 进程是针对整个Linux系统而言的,作业是针对Shell而言的。作业有两种运行方式,前台运行和后台运行
3 前台运行的作业指能够控制当前终端或窗口,且能接收用户的输入;而后台的运行的作业则不在当前激活的终端或窗口中运行,是在用户看不见的情况下运行的
4 Shell中内置命令fg能够把后台的作业放到前台运行,如果没有带参数的fg命令就是将最近提交的那个后台作业放置到前台运行
5 指定作业的方法及其意义
参数 意义
%n n为后台作业的作业号
%string 命令以string字符串开始的后台作业
%?string 命令包含string字符串的后台作业
%+或%% 最近提交的后台作业
%- 最近第二个提交的后台作业
6 Shell内置的命令jobs是用于显示所有后台运行的作业
3 信号
1 向进程发送信号大多通过"Crtl"键加上一些功能键来实现的
2 Ctrl组合键,信号类型及其意义
组合键 信号类型 意义
Ctrl+C INT信号,即interupt信号 停止当前运行的作业
Ctrl+Z TSTP信号,即terminal stop信号 使当前运行的作业暂时停止(转入阻塞态)
Ctrl+\ QUIT信号 Ctrl+C的强化版本,当Ctrl+C无法停止作业时,使用此组合键
Ctrl+Y TSTP信号,即terminal stop信号 当进程从终端读取输入数据时,暂时停止该进程
====================
https://cnbin.github.io/blog/2015/06/23/fu-shell-yu-zi-shell/
父 Shell 与子 Shell
Jun 23rd, 2015 3:04 pm
Login Shell
登录主机后,在执行 Bash Script
之前,其实我们已经处于一个 BashShell
中。
这个 Shell
叫 login Shell
,是将来我们执行任何 Script
的上层环境。又叫 父SHell
其实每个帐号都可以自定义 loginShell
。以 Linux 来说,帐号的 login Shell
定义在 /etc/passwd
这个文件中。
/etc/passwd
的每一行代表一个帐号,共有7个字段,之间用 :
隔开。
帐号:x:UID 使用者代码:GID 群组代码:用户信息:主目录:login shell路径
第二栏 x
为密码栏,基于系统安全考虑,编码后的密码已经被放入 /etc/passwd
文件中。
login Shell
定义在第7个字段,如果这个字段的 Shell
程序不存在、不合法,或执行失败,则无法登录主机。
父 Shell、子 Shell
当在执行一个 Shell Script
时,父Shell
会根据 Script
程序的第一行 #!
之后指定的 Shell
程序开启一个 子Shell
环境,然后在子Shell
中执行此 Shell Script
。一旦 子Shell
中的 Script
执行完毕,此 子Shell
随即结束,回到 父Shell
中,不会影响 父Shell
原本的环境。
子Shell
环境拥有与 父Shell
相同的环境变量、标准输入、输出、错误等。
例如:
test.sh文件内容
#!/bin/bash
cd /var/www/html
命令行:
chmod +x /test.sh
命令行:
./test.sh
执行完脚本后还原到 父Shell
,并且 父Shell
并没有进入 /var/www/html目录
。
注:这是因为当执行 Shell
文件时,父Shell
会创建 子Shell
,各自独立。
如果需要使用 父Shell
来执行此脚本,可以使用:
命令行:
. ./test.sh
注意 .
与 ./
之间有一个空格符
子 Shell 继续开启子 Shell
与 父Shell
启动 子Shell
方式一样,继续调用下去,即 子Shell
开启 子Shell
。
子 Shell 能够从 父Shell 继承得来的属性如下:
- 当前工作目录
- 环境变量
- 标准输入、标准输出和标准错误输出
- 所有已打开的文件标识符
子 Shell 不能从父 Shell 继承得来的属性,归纳如下:
除了环境变量和 .bashrc
文件中定义变量之外的 Shell 变量
圆括号结构
圆括号结构能够强制将其中的命令运行在 子Shell
中,它的基本格式为:
(
command 1
command 2
…
command n
)
圆括号内的 n 条命令在 子Shell
中运行,bash 版本3之后定义了内部变量 BASH_SUBSHELL
,该变量记录了 子Shell
的层次。
子Shell
只能继承 父Shell
的一些属性,但是,子Shell
不可能反过来改变 父Shell
的属性。
------------------------------------------------
http://wiki.jikexueyuan.com/project/linux-command/chap37.html