shell脚本学习笔记一

一、前言

使用shell写脚本也写了很多次了,不过大部分都是要用到了谷歌用法然后来的,没有比较全面的一次学习,这次趁着有些时间从头开始学一次shell脚本,也新建了一个新的文集,作为学习笔记使用,shell脚本大神请绕道哈~

二、基本的io重定向

标准io也就是标准的输入输出,这个可能是软件设计原则里边最普遍最重要的概念了,shell提供了集中语法标记,用来改变默认的io端,就是输入端和输出端。在这里还将说到一个强大的命令 -- tr

以 < 改变标准输入: program < file 可将 program 的标准输入修改为 file,如:(shell_demo是路径)

先查看我本地名交 wc_test 的文件内容:

➜  shell_demo vim  wc_test
 ls | wc -l
~                                                                               
~                                                                               
~         

可以看到内容是 ls ......

再使用tr命令,将wc_test定义为标准输入,并且将其中的内容的ls删除并且打出删除后的结果,命令如下:

➜  shell_demo tr -d 'ls' < wc_test
  | wc -

从输出的结果可以看出,wc_test的内容被删除了ls并且打印了出来。

使用 > 改变标准输出: program > file 可将program的标准输出修改为file,在上面的命令上进行再次的修改:

➜  shell_demo tr -d 'ls' < wc_test > wc_test2

此处是将wc_test的内容被删除了ls之后再将其输入到wc_test2中,此处可以通过vim查看内容,并且要注意一点的是,在同目录下本来是没有wc_test2这个文件的,但是后来却新建了一个,说明重定向符在目的文件不存在时会新建一个,并且如果目的文件存在了,则原有内容会被覆盖掉。

那么,在日常需求中,也会不覆盖文件,而是附加内容的需求存在,那么怎么做呢?

shell提供了 >> 附加命令,program >> file 可将program的标准输出附加到file的结尾处,注意的是,如果文件不存在则会新建一个,如果存在了,则不会覆盖原有文件,而是将程序所产生的数据附加到文件末尾处,还是在原有的命令上进行修改:

➜  shell_demo tr -d 'ls' < wc_test >> wc_test2

使用vim查看wc_test2可以发现内容已经变成了这样:

  | wc -
  | wc -
~        

标准输入和输出都讲到了,接下来要说说如何将输入和输出连接在一起,也就是管道,shell 以 | 建立管道,即program1 | program2 ,可将program1的标准输出修改为program2的标准输入,为了看到效果,我先将wc_test里边的内容修改为:

ls
9
8     
7
6
5   
4   
3
2
1
1
2
3
4

在采用tr命令将ls字符删除,并且将删除后的数据用通道的形式变成第二个程序的标准输入,命令如下:

➜  shell_demo tr -d 'ls' < wc_test | sort > wc_test3

通过vim查看wc_test3的数据可以发现,数据是是wc_test中去掉ls后的内容并且是排好序的。

tr的作用异常强大,具备对标准输入的字符压缩、替换删除的作用,具体的可以查看 http://man.linuxde.net/tr ,此处便不做详解。

sort的作用是排序,可惜的是针对每行进行排序,具体的可以查看 http://man.linuxde.net/sort ,此处便不做详解。

这里有个知识点想提及,在ubuntu系统中经常会遇见的一个问题,那就是修改root密码,如何做到修改密码的时候不打印键盘输入的字符,这里先给出一份脚本,再来讲解知识点,命令如下所示:

#!/bin/sh
printf "请输入密码:"
stty -echo
read pass < /dev/tty
printf "\n" 
printf "请再输入密码:"
read pass2 < /dev/tty  
stty echo
printf "\n"           

以上是我新建的一个名字叫 wc_test4.sh 的脚本文件,运行后可以看到类似修改root密码的时候输入字符终端却不打印出字符的效果,和这种效果有关的一个知识点,那就是/dev/tty,当程序打开此文件时,unix会自动将它重定向到终端,而stty(set tty)命令就是用来控制终端的各种设置,比如 stty -echo 的作用就是设置终端不打印出字符,当然了使用结束后用stty echo 来恢复该功能。

三、新学的脚本命令

➜  ~ ls
examples.desktop  Snapshots  模板  图片  下载  桌面
npm-debug.log     公共的     视频  文档  音乐

理解备注:这是查询本地目录的一个命令。

但是有时候会衍生另一种需求:查看文件夹下面的文件和文件夹个数(同一目录下的),命令如下:

➜  ~ ls | wc -l
11

理解备注:在这里利用的是wc字数统计程序,可以算出行数、字数与字符数,| (管道)符号可以在两程序之间建立管道,ls的输出,成了wc的输入,wc所列出的结果就是目录下文件和文件夹的个数。

有时候会遇见一种情况,需要将命令放进独立的脚本文件里,为了方便以后直接使用,可以尝试一下的方法:

➜  cat > wc_test

输入以下命令:(可以替换为你想放进独立脚本的如何命令)

 ls | wc -l

这里输入后记得以 ctrl+d 结束输入!

之后就是添加执行权限~

➜  chmod +x wc_test

运行输出

➜  ./wc_test 

执行了这个脚本程序之后,会随之诞生一个问题, 那就是在执行这个shell脚本的时候,内核是如何执行这个过程的?

当shell要求内核执行这个脚本时,内核讲无法执行这件事,并回应“not executable format file”错误信息,意思就是说这个不是内核可执行的格式文件,shell收到此错误信息之后,就会认为这不是编译程序,那么一定是shell脚本,接着就会启动一个新的/bin/sh(标准的shell)副本来执行程序。

当系统只有一个shell时,退回到/bin/sh的机制很方便,但是现在的unix系统都会拥有好几个shell,因此告知unix内核是哪个shell来执行所指定的shell脚本就有必要了,那么怎么做呢?方法是在脚本的第一行设置,采用#!来开头,例如在我的go微服务项目(https://github.com/wiatingpub/MTBSystem)中的脚本:

#!/bin/sh


if [ $1 == "all" ]; then
    for srv in `ls src`; do
        if [[ ${srv:0-4} == "-srv" ]]; then
            echo "开始更新$srv"
            GOROOT=/data/services/go GOBIN=/data/goapp/mtbsystem/bin GOPATH=`pwd`:`pwd`/vendor /data/services/go/bin/go install $srv && sudo supervisorctl restart $srv:*
        fi
    done
else
    for srv in "$@"
    do
        if [[ "${srv:0-4}" != "-srv" ]]; then
            srv="${srv}-srv"
        fi
        echo "开始更新$srv"
        GOROOT=/data/services/go GOBIN=/data/goapp/mtbsystem/bin GOPATH=`pwd`:`pwd`/vendor /data/services/go/bin/go install $srv && sudo supervisorctl restart class-$srv:*
    done
fi

ps:今天一边学习一边思考人生,也扔了个漂流瓶记录了下想法,希望未来能做个行动派,像我现在这样的年纪,更多的是想的多,做的少~

有兴趣的可以关注我的个人公众号 ~

shell脚本学习笔记一_第1张图片
qrcode_for_gh_04e57fbebd02_258.jpg

你可能感兴趣的:(shell脚本学习笔记一)