使用 fork 方式运行 script 时, 就是让 shell(parent process) 产生一个 child process 去执行该 script, 当 child process 结束后, 会返回 parent process,
但 parent process 的环境是不会因 child process 的改变而改变的.
source
使用 source 方式运行 script 时, 就是让 script 在当前 process 内执行, 而不是产生一个 child process 来执行. 由于所有执行结果均于当前 process 内完成, 若 script 的环境有所改变, 当然也会改变当前 process 环境了.
exec
使用 exec 方式运行script时, 它和 source 一样, 也是让 script 在当前process内执行, 但是 process 内的原代码剩下部分将被终止. 同样, process 内的环境随script 改变而改变.
结论:通常如果我们执行时,都是默认为fork的。大家可以通过pstree命令看看关于父子进程的关系。如上,如果想让父进程得到子进程的环境变量,就是source方式了
1.sh的脚本
- #!/bin/bash
- A=B
- echo "PID for 1.sh before exec/source/fork:$$"
- export A
- echo "1.sh: \$A is $A"
- case $1 in
- exec)
- echo "using exec…"
- exec ./2.sh ;;
- source)
- echo "using source…"
- . ./2.sh ;;
- *)
- echo "using fork by default…"
- ./2.sh ;;
- esac
- echo "PID for 1.sh after exec/source/fork:$$"
- echo "1.sh: \$A is $A"
2.sh的脚本
- #!/bin/bash
- echo "PID for 2.sh: $$"
- echo "2.sh get \$A=$A from 1.sh"
- A=C
- export A
- echo "2.sh: \$A is $A"
3. 实验
3.1 fork
- PID for 1.sh before exec/source/fork:25992
- 1.sh: $A is B
- using fork by default…
- PID for 2.sh: 25993
- 2.sh get $A=B from 1.sh
- 2.sh: $A is C
- PID for 1.sh after exec/source/fork:25992
- 1.sh: $A is B
3.2 source
- PID for 1.sh before exec/source/fork:25994
- 1.sh: $A is B
- using source…
- PID for 2.sh: 25994
- 2.sh get $A=B from 1.sh
- 2.sh: $A is C
- PID for 1.sh after exec/source/fork:25994
- 1.sh: $A is C
注意: 三个PID都是25994, 子程序的环境变量被带了出来.
3.3 exec
- PID for 1.sh before exec/source/fork:25997
- 1.sh: $A is B
- using exec…
- PID for 2.sh: 25997
- 2.sh get $A=B from 1.sh
- 2.sh: $A is C
注意: 主程序的剩下部分不执行了.
1. 截断功能
${file#*/}: 拿掉第一条/及其左边的字符串:dir1/dir2/dir3/my.file.txt
${file##*/}: 拿掉最后一条/及其左边的字符串:my.file.txt
${file#*.}: 拿掉第一个.及其左边的字符串:file.txt
${file##*.}: 拿掉最后一个.及其左边的字符串:txt
${file%/*}: 拿掉最后条/及其右边的字符串:/dir1/dir2/dir3
${file%%/*}: 拿掉第一条/及其右边的字符串:(空值)
${file%.*}: 拿掉最后一个.及其右边的字符串:/dir1/dir2/dir3/my.file
${file%%.*}: 拿掉第一个.及其右边的字符串:/dir1/dir2/dir3/my
记忆的方法为:
[list]#是去掉左边, ##最后一个
%是去掉右边, %%第一个
2. 字符串提取
单一符号是最小匹配﹔两个符号是最大匹配。
${file:0:5}:提取最左边的 5 个字节:/dir1
${file:5:5}:提取第 5 个字节右边的连续 5 个字节:/dir2
3. 字符串替换
${file/dir/path}:将第一个 dir 提换为 path:/path1/dir2/dir3/my.file.txt
${file//dir/path}:将全部 dir 提换为 path:/path1/path2/path3/my.file.txt
4. 针对不同的变量状态赋值(没设定、空值、非空值):
${file-my.file.txt}: 若$file没有设定,则使用my.file.txt作返回值。(空值及非空值时不作处理)
${file:-my.file.txt}:若$file没有设定或为空值,则使用my.file.txt作返回值。(非空值时不作处理)
${file+my.file.txt}: 若$file设为空值或非空值,均使用my.file.txt作返回值。(没设定时不作处理)
${file:+my.file.txt}:若$file为非空值,则使用my.file.txt作返回值。(没设定及空值时不作处理)
${file=my.file.txt}: 若$file没设定,则使用my.file.txt作返回值,同时将$file 赋值为 my.file.txt。(空值及非空值时不作处理)
${file:=my.file.txt}:若$file没设定或为空值,则使用my.file.txt作返回值,同时将 $file 赋值为 my.file.txt。(非空值时不作处理)
${file?my.file.txt}: 若$file没设定,则将my.file.txt输出至 STDERR。(空值及非空值时不作处理)
${file:?my.file.txt}:若$file没设定或为空值,则将my.file.txt输出至STDERR。(非空值时不作处理)
注意:
":+"的情况是不包含空值的.
":-", ":="等只要有号就是包含空值(null).
5. 变量的长度
${#file}
6. 数组运算
A=(a b c def)
${A[@]} 或 ${A[*]} 可得到 a b c def (全部组数)
${A[0]} 可得到 a (第一个组数),${A[1]} 则为第二个组数...
${#A[@]} 或 ${#A[*]} 可得到 4 (全部组数数量)
${#A[0]} 可得到 1 (即第一个组数(a)的长度),${#A[3]} 可得到 3 (第四个组数(def)的长度)
\a:ALERT / BELL (从系统喇叭送出铃声)
\b:BACKSPACE ,也就是向左删除键
\c:取消行末之换行符号
\E:ESCAPE,跳脱键
\f:FORMFEED,换页字符
\n:NEWLINE,换行字符
\r:RETURN,回车键
\t:TAB,表格跳位键
\v:VERTICAL TAB,垂直表格跳位键
\n:ASCII 八进位编码(以 x 开首为十六进制)
\\:反斜线本身
2. 常用meta
IFS:由 <space> 或 <tab> 或 <enter> 三者之一组成(我们常用 space )。
CR: 由 <enter> 产生。
3. 除了IFS与CR,常用的meta还有:
= :设定变量。
$ :作变量或运算替换(请不要与 shell prompt 搞混了)。
> :重导向stdout。
< :重导向stdin。
| :命令管道。
& :重导向file descriptor,或将命令置于后台执行。
() :将其内的命令置于nested subshell执行,或用于运算或命令替换。
{} :将其内的命令置于non-named function中执行,或用在变量替换的界定范围。
; :在前一个命令结束时,而忽略其返回值,继续执行下一个命令。
&& :在前一个命令结束时,若返回值为true,继续执行下一个命令。
|| :在前一个命令结束时,若返回值为false,继续执行下一个命令。
! :执行history列表中的命令
4. quoting的三种方法
hard quote:' '(单引号),凡在hard quote中的所有meta均被关闭。
soft quote:“”(双引号),在soft quote中大部份 meta 都会被关闭,但某些
则保留(如$)。
escape:\(反斜线),只有紧接在escape(跳脱字符)之后的单一meta才被关闭。
- 4.1 示例1
- [test@rhel5 ~]$ A="B C"
- [test@rhel5 ~]$ echo "'$A'"
- 'B C'
- [test@rhel5 ~]$ echo '"$A"'
- "$A"
- [test@rhel5 ~]$ A=B\ C
- [test@rhel5 ~]$ echo "'$A'"
- 'B C'
- [test@rhel5 ~]$ echo '"$A"'
- "$A"
- 4.2 示例2
- [test@rhel5 ~]$ A="
- > B
- > C"
- [test@rhel5 ~]$ echo $A
- B C
- [test@rhel5 ~]$ echo "$A"
- B
- C
# $A 时的变量没至于 soft quote 中,因此当变量替换完成后并作命令行重组时,<enter> 会被解释为 IFS ,而不是解释为 New Line 字符。
5. ()与{}这两对符号的差异
()将command group置于sub-shell去执行,也称nested sub-shell。
{}则是在同一个shell内完成,也称为non-named command group。
所谓function,就是用一个名字去命名一个command group,然后再调用这个名字去执行command group。
6. $(( ))与$( )还有${ }的区别
$( )与` `(反引号)都是用来做命令替换用(command substitution)的。在多层次的复合替换中,``须要额外的跳脱(\`)处理,而$()则比较直观。
${ }是用来作变量替换用的。
$(( ))用来作整数运算的。
7. ${ }的一些特异功能
详见"shell中${}的妙用 "
8. $$, $#, $@, $*区别?
$$: 进程ID
$#: 参数的数量
$?: 上一条命令的返回值
my.sh p1 "p2 p3" p4
"$@" 则可得到 "p1" "p2 p3" "p4" 这三个不同的词段(word)﹔
"$*" 则可得到 "p1 p2 p3 p4" 这一整串单一的词段。
9. 命令的返回值
Return Value的作用,是用来判断行程的退出状态(exit status),只有两种:
0的话为"真"(true)
非0的话为"假"(false)
10. &&和||
&& 与 || 都是用来"组建"多个 command line 用的:
command1 && command2:其意思是command2只有在RV为0(true)的条件下执行。
command1 || command2:其意思是command2只有在RV为非0(false)的条件下执行。
11. < 和 >
标准输出: 1>
错误输出: 2>
两个输出合并: 1>file.both 2>&1
输出到空设备: &>/dev/null
12. 管道pipe
上一个命令的stdout接到下一个命令的stdin
13. if与case
- if comd1; then
- comd2
- elif comd3; then
- comd4
- else
- comd5
- fi
- case "$1" in
- start)
- start
- ;;
- stop)
- stop
- ;;
- status)
- rhstatus
- ;;
- restart|reload)
- restart
- ;;
- condrestart)
- [ -f /var/lock/subsys/syslog ] && restart || :
- ;;
- *)
- echo $"Usage: $0 {start|stop|status|restart|condrestart}"
- exit 1
- esac
13. for, while, until
- for var; do
- ......
- done
- for var in one two three four five
- do
- echo '$var is '$var
- done
- for ((i=1;i<=10;i++))
- do
- echo "num is $i"
- done
- num=1
- while [ "$num" -le 10 ]; do
- echo "num is $num"
- num=$(($num + 1))
- done