bash

shell是一个解释器,启动器

    解释器:

         用户交互输入

         文本文件输入

                头部定义脚本解释器:

#! /bin/bash

#! /usr/bin/python



读取方式:

    当前shell进程执行:

        source(.)

[root@node01 shell]# vi s.sh 
[root@node01 shell]# cat s.sh 
echo "hello word!!"
echo "进程号:$$"


[root@node01 shell]# echo $$
1469
[root@node01 shell]# source s.sh #进程id与当前进程 id相同
hello word!!
进程号:1469
[root@node01 shell]# . s.sh 
hello word!!
进程号:1469

    新建子shell进程执行:

        ./filename  或  /bin/bash ./filename

[root@node01 shell]# sed -i '1i\#!/bin/bash' s.sh  #添加前缀,指定解释器 
[root@node01 shell]# cat s.sh 
#!/bin/bash
echo "hello word!!"
echo "进程号:$$"

[root@node01 shell]# chmod +x ./s.sh #给脚本文件加上执行权限
[root@node01 shell]# echo $$
1469
[root@node01 shell]# ./s.sh  #进程id与父进程不同
hello word!!
进程号:1527
[root@node01 shell]# /bin/bash ./s.sh 
hello word!!
进程号:1528


函数:

[root@node01 shell]# pt ()
> {
>    echo "进程号:$$"
> }
[root@node01 shell]# pt
进程号:1105
[root@node01 shell]# type pt #pt为函数类型
pt is a function
pt () 
{ 
    echo "进程号:$$"
}


变量:

     本地:

          当前shell进程拥有;

          生命周期随shell进程;

#  下面验证:子进程消失后,其变量是否消失

[root@node01 shell]# pstree
init─┬─auditd───{auditd}
     ├─crond
     ├─dbus-daemon
     ├─master─┬─pickup
     │        └─qmgr
     ├─6*[mingetty]
     ├─rsyslogd───3*[{rsyslogd}]
     ├─sshd───sshd───bash───pstree
     └─udevd───2*[udevd]
[root@node01 shell]# /bin/bash #创建一个子进程
[root@node01 shell]# pstree
init─┬─auditd───{auditd}
     ├─crond
     ├─dbus-daemon
     ├─master─┬─pickup
     │        └─qmgr
     ├─6*[mingetty]
     ├─rsyslogd───3*[{rsyslogd}]
     ├─sshd───sshd───bash───bash───pstree  #多一层bash
     └─udevd───2*[udevd]
[root@node01 shell]# city=changsha #子进程里创建变量
[root@node01 shell]# echo $city
changsha
[root@node01 shell]# exit #退出子进程
exit
[root@node01 shell]# pstree 
init─┬─auditd───{auditd}
     ├─crond
     ├─dbus-daemon
     ├─master─┬─pickup
     │        └─qmgr
     ├─6*[mingetty]
     ├─rsyslogd───3*[{rsyslogd}]
     ├─sshd───sshd───bash───pstree #子进程已删除
     └─udevd───2*[udevd]
[root@node01 shell]# echo $city #打印无结果

     局部:

         只能用于函数

         local val=100

[root@node01 shell]# localtest () {
> local attr=4396 #创建一个局部变量
> echo $attr
> }
[root@node01 shell]# localtest #调用方法有结果
4396
[root@node01 shell]# echo $attr #外部调用无结果

     位置:

          脚本、函数下

         $1,$2,${11}

     特殊:

         $#:位置参数个数

         $*:参数列表,双引号引用为一个字符串

         $@:参数列表,双引号引用为单独的字符串

[root@node01 shell]# cat t.sh 
#!/bin/bash
echo "参数列表:$*"
echo "总共 $# 个参数"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "第十一个参数:${11}"


[root@node01 shell]# ./t.sh a b c d e f g h i j k l m n
参数列表:a b c d e f g h i j k l m n
总共 14 个参数
第一个参数:a
第二个参数:b
第十一个参数:k

[root@node01 shell]# cat ./diff.sh 
#!/bin/bash
echo '$*: '$*
echo '$@: '$@

echo '$* for: '
for i in "$*"; do
 echo $i;
done

echo '$@ for: '
for i in "$@"; do
 echo $i;
done

[root@node01 shell]# ./diff.sh a b c d e f  #$*与$@的区别
$*: a b c d e f
$@: a b c d e f
$* for: 
a b c d e f
$@ for: 
a
b
c
d
e
f
         $$:当前shell的PID:在管道中会特殊处理,看下面例子

         $BASHPID:真实PID

         $?:上一个命令退出状态

             0:成功

             other:失败


变量传递:

    首先看例子:

[root@node01 shell]# s=99  #父进程中定义变量
[root@node01 shell]# s=100 | echo ok #管道中修改
ok
[root@node01 shell]# echo $s #父进程中变量未改变
99

    s变量未被修改,这是为什么呢?

因为在管道的左边的命令,并不是在主进程先执行完再把结果通过管道传递给另外一个命令;
而是bash会 创建一个子进程 来执行左边的命令,创建 另外一个子进程 来执行右边的命令,再把左边的输出流跟右边的输入流连接起来

    这就解释为什么s变量未被修改,但是再看下面例子:

[root@node01 shell]# echo $$
1469
[root@node01 shell]# echo $$ | cat
1469

    两个pid一样,这不是与前面矛盾吗? 再看一个例子:

[root@node01 shell]# echo $$
1469
[root@node01 shell]# echo $BASHPID
1469
[root@node01 shell]# echo $BASHPID | cat
1494
[root@node01 shell]# echo $BASHPID | cat
1496

    这个就与预期相同了(子进程的pid不一样),这是为什么呢?

因为bash会把$$做特殊处理,再提交这个命令之前,父进程就会把$$的值给赋值进去
而对于其他的变量,则会让子进程去处理解释

    bash默认不会传递用户变量到子进程,需要 export 导出:

[root@node01 shell]# vi fork.sh #创建一个脚本
[root@node01 shell]# cat fork.sh #脚本里输出city变量
#!/bin/bash
echo $city
[root@node01 shell]# chmod +x ./fork.sh 
[root@node01 shell]# city=changsha #父进程定义一个city变量
[root@node01 shell]# echo $city #父进程能输出
changsha
[root@node01 shell]# ./fork.sh #子进程不能输出

[root@node01 shell]# export city #导出city变量
[root@node01 shell]# ./fork.sh  #子进程能输出
changsha
     那父进程与子进程为值传递还是引用传递?

 经测试为值传递
 也就是
父进程修改变量不会改变子进程变量值
子进程修改变量不会改变父进程变量值
 准确的说shell变量为:
修改时复制(copy on write):就是当变量没有被修改时父子进程共用一个内存地址的变量,但父子进程 修改了变量时,子进程 会拷贝一份



重定向

程序自身都有I/O

    linux中一切皆文件,这是linux的设计思想,优雅之处

    看个演示:

[root@node01 ~]# cd /proc
[root@node01 proc]# ls
1     1099  14   20   29   5    936        dma          kcore       mounts        softirqs       vmstat
10    11    146  21   3    6    974        driver       keys        mpt           stat           zoneinfo
1050  1100  147  22   30   620  acpi       execdomains  key-users   mtd           swaps
1059  1102  15   23   31   664  asound     fb           kmsg        mtrr          sys
1060  1126  150  24   32   665  buddyinfo  filesystems  kpagecount  net           sysrq-trigger
1086  1326  151  25   33   697  bus        fs           kpageflags  pagetypeinfo  sysvipc
1088  12    152  26   351  7    cgroups    interrupts   loadavg     partitions    timer_list
[root@node01 proc]# echo $$
1326

进入proc内核文件夹,里面又很多数字文件夹,每个数字就代表一个进程号;
例如当前的连接终端terminal,1326进程就在里面有个文件夹;

[root@node01 proc]# cd /proc/$$
[root@node01 1326]# ls
attr        cmdline          environ  limits     mounts      oom_adj        root       stack    task
autogroup   comm             exe      loginuid   mountstats  oom_score      sched      stat     wchan
auxv        coredump_filter  fd       maps       net         oom_score_adj  schedstat  statm
cgroup      cpuset           fdinfo   mem        ns          pagemap        sessionid  status
clear_refs  cwd              io       mountinfo  numa_maps   personality    smaps      syscall

进入terminal进程的文件夹

[root@node01 1326]# cd fd
[root@node01 fd]# ll
total 0
lrwx------ 1 root root 64 Jun 27 19:31 0 -> /dev/pts/2
lrwx------ 1 root root 64 Jun 27 19:33 1 -> /dev/pts/2
lrwx------ 1 root root 64 Jun 27 19:33 2 -> /dev/pts/2
lrwx------ 1 root root 64 Jun 27 19:33 255 -> /dev/pts/2

进入fd文件流目录

程序自身都有I/O

    0:标准输入
    1:标准输出
    2:错误输出

每个IO对应一个数字文件,例如 程序对文件开启一个IO流,就会有一个数字3

这些IO输入输出流在这里指向了不同的地方,就实现了解耦;

发现这些输出都指向一个 /dev/pts 文件夹下的一个文件;

[root@node01 fd]# cd /dev/pts
[root@node01 pts]# ll
total 0
crw--w---- 1 root tty  136, 0 Jun 27 17:22 0
crw--w---- 1 root tty  136, 1 Jun 27 17:23 1
crw--w---- 1 root tty  136, 2 Jun 27 20:01 2
c--------- 1 root root   5, 2 Jun 27 17:15 ptmx

这个 /dev/pts 是一个虚拟终端的文件夹,每个终端(连接)对应一个数字

所有每个ssh连接的输出都输出到了终端上

这个就是打开了 0,1,2三个终端,

新建一个ssh连接,就会有个数字3的终端

控制程序I/O位置

    A、重定向ls的标准输出到文件

[root@node01 shell]# ll
total 12
-rwxr-xr-x 1 root root  23 Jun 21 15:23 fork.sh
-rwxr-xr-x 1 root root  54 Jun 21 16:23 s.sh
-rwxr--r-- 1 root root 155 Jun 21 15:08 t.sh
[root@node01 shell]# ls ./ 1> ~/ls.txt
[root@node01 shell]# cat ~/ls.txt
fork.sh
s.sh
t.sh

    B、利用重定向也可以对网络流实现类似操作文件的操作

[root@node01 ~]# type exec
exec is a shell builtin
[root@node01 ~]# exec ls ./shell
fork.sh  s.sh  t.sh
Connection closing...Socket close.
Connection closed by foreign host.
Disconnected from remote host(node01) at 00:05:34.

Type `help' to learn how to use Xshell prompt.
[g:\~]$ 

命令:exec
发现ls执行完,输出了结果,ssh就退出了,这是因为:
exec会把后面的命令的代码在内存中压栈到当前进程的内存栈
而不是另外启动一个进程
所以当前ls执行完后,关闭ls进程就把ssh进程关闭了

[root@node01 fd]# exec 8<> /dev/tcp/www.baidu.com/80
[root@node01 fd]# ll
total 0
lrwx------ 1 root root 64 Jun 27 19:31 0 -> /dev/pts/2
lrwx------ 1 root root 64 Jun 27 19:33 1 -> /dev/pts/2
lrwx------ 1 root root 64 Jun 27 19:33 2 -> /dev/pts/2
lrwx------ 1 root root 64 Jun 27 19:33 255 -> /dev/pts/2
lrwx------ 1 root root 64 Jun 27 20:32 8 -> socket:[10492]

利用exec,让exec去给当前进程绑定一个文件操作符8的输入输出流到一个文件(百度的80端口)

查看fd目录,发现8号绑定到一个socket

(linux中一切皆文件,这里一个socket链接也可以是文件)

[root@node01 fd]# echo -e "GET / HTTP/1.1\n" 1>& 8
让echo的标准输出1重定向到8
1>& 8  文件操作符之间的重定向需要加  &
这里就是给百度通过socket流(8)发送了一个首页的http请求

[root@node01 fd]# cat 0<& 8
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Connection: Keep-Alive
Content-Length: 14615
Content-Type: text/html
Date: Wed, 27 Jun 2018 16:26:21 GMT
Last-Modified: Mon, 11 Jun 2018 11:19:00 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BAIDUID=5512413AB9FEB431236D789E99F654A9:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=5512413AB9FEB431236D789E99F654A9; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1530116781; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1

<!DOCTYPE html><!--STATUS OK-->
<html>
<head>
	<meta http-equiv="content-type" content="text/html;charset=utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=Edge">
	<link rel="dns-prefetch" href="//s1.bdstatic.com"/>
	<link rel="dns-prefetch" href="//t1.baidu.com"/>
	<link rel="dns-prefetch" href="//t2.baidu.com"/>
	<link rel="dns-prefetch" href="//t3.baidu.com"/>
	<link rel="dns-prefetch" href="//t10.baidu.com"/>
	<link rel="dns-prefetch" href="//t11.baidu.com"/>
	<link rel="dns-prefetch" href="//t12.baidu.com"/>
	<link rel="dns-prefetch" href="//b1.bdstatic.com"/>
	<title>百度一下,你就知道</title>
用cat,将cat的的标准输入0重定向给8,
这样就接受到了百度返回的结果,
实现了对文件操作而打开了网页

    C:标准输出、错误输出给混合输出

[root@node01 shell]# ls ./noexist #noexist是一个不存在的目录
ls: cannot access ./noexist: No such file or directory
[root@node01 shell]# ls ./ ./noexist 1> ~/ls.txt
ls: cannot access ./noexist: No such file or directory

让ls输出两个目录 ./ ./noexist
让ls的标准输出重定向到文件
这时错误输出没有重定向,仍然输出到屏幕

[root@node01 shell]# ls ./ ./noexist 1> ~/ls.txt 2> ls-error.txt

让标准输出,错误输出分别重定向输出到不同的文件,这时屏幕没有输出了

这时想让标准输出,错误输出输出到一个文件呢?

[root@node01 shell]# ls ./ ./noexist 1> ~/ls.txt 2>& 1
[root@node01 shell]# cat ~/ls.txt
ls: cannot access ./noexist: No such file or directory
./:
fork.sh
ls-error.txt
s.sh
t.sh

让标准输出1重定向到文件,在让 错误输出2重定向到1,这样就行了

注意这里顺序不能反,重定向到文件须在前,也就是 多个重定向时,重定向是有顺序的

[root@node01 shell]# ls ./ ./noexist 2>& 1 1> ~/ls.txt
ls: cannot access ./noexist: No such file or directory

为什么会这样呢?
因为首先2重定向到1,这时1指向终端,所以2输出到了终端
然后才是重定向1输出到文件

  上面混合输出的简写(>&),只写一个 > 相当于 1>,只重定向标准输出

[root@node01 shell]# ls ./ ./noexist >& ~/ls.txt
[root@node01 shell]# cat ~/ls.txt 
ls: cannot access ./noexist: No such file or directory
./:
fork.sh
ls-error.txt
s.sh
tool.sh
t.sh

  D:重定向输入流演示

[root@node01 shell]# read dengchao #这时会阻塞,让你输入
goodman
[root@node01 shell]# echo $dengchao
goodman

read命令:
read会读取你的输入然后赋值给后面的命令

[root@node01 shell]# read dengchao 0<<<badman
[root@node01 shell]# echo $dengchao 
badman

这里将字符串重定向给read的标准输入
<<<  : 是将 一段字符串 重定向

[root@node01 shell]# cat 0<<IMBORDER
> asfnnaknf
> dasadsdas
> asddasdad
> IMBORDER
asfnnaknf
dasadsdas
asddasdad

将cat的输入流重定向给一个边界字符输入流
就是 利用 << (两个<符) 后面接一个自定义的 边界字符串
后面会让你输入
当读到你定义的边界符时,就会关闭输入流

利用这个可以做一些实用的操作,例如脚本里输出的用户帮助:

#!/bin/bash
cat 0<<BORDER
欢迎使用本脚本工具
使用方法(输入下列数字回车):
1、查看IP
2、查看内存
。。。
BORDER
read toolId
echo "你选择了选项:$toolId"

[root@node01 shell]# ./tool.sh 
欢迎使用本脚本工具
使用方法(输入下列数字回车):
1、查看IP
2、查看内存
。。。
2
你选择了选项:2

这样就不同对每行的提示,分别用echo输出,直接用cat就输出了
bash不会对后面的字符串进行解释,七步扩展,直到遇到边界符



相关文章:

         Linux Shell - bash(二):七步扩展、流程控制

         Linux Shell

添加新评论