zoukankan      html  css  js  c++  java
  • 《Shell十三问》笔记(上)

    《shell十三问》是网中人前辈首发在CU论坛上对SHELL的一些整理,非常值得一读


    注:笔记的标号非问题标号,而是知识点的标号。本篇笔记记录的是1-10问的知识点

    (1)IFS:Shell预设使用的栏位分隔符号,可以由一个几多个“空白键(White Space)、表格键(Tab)、回车键(Enter)”组成

    (2)echo命令支持的常用选项和反斜线控制字符

    • -e:启用反斜线控制字符的转换(参考下表)
    • -E:关闭反斜线控制字符的转换(预设如此)
    • -n:取消行末之换行符号(与-e选项下的c字符同意)

    [toggle title="反斜线控制字符"]


    a:ALERT / BELL (从系统喇叭送出铃声)
    :BACKSPACE ,也就是向左退格键
    c:取消行末之换行符号
    E:ESCAPE,跳脱键
    f:FORMFEED,换页字符
    :NEWLINE,换行字符
    :RETURN,回车键
    :TAB,表格跳位键
    v:VERTICAL TAB,垂直表格跳位键
    :ASCII 八进位编码(以 x 开首为十六进位)
      \:反斜线本身


    [/toggle]

    (3)‘’单引号为Hard quote、“”双引号为Soft quote

    $ A=B C
    $ echo '"$A"'        # 最外面的是单引号
    "$A"
    $ echo "'$A'"        # 最外面的是双引号
    'B C'

    (提示:单引号及双引号,在 quoting 中均被关闭了。)

    (4)var=${str=expr}的意思


    首先,var=$str 这个大家都可理解吧。
    而接下来的思考方向是,究竟 $str 这个变量是如下哪一种情况呢:
    1) unset
    2) null
    3) not null

    1) 假如是 unset(未定义) ,那麽 var=${str=expr} 的结果将是:
    var=expr
    str=expr

    2) 假如是 null(空) ,那 var=${str=expr} 的结果是:
    var=
    str=

    3) 假如是 not null (非空,比方为 xyz ),那 var=${str=expr} 之结果是:
    var=xyz
    str=xyz


    (5)三种执行shell方法的区别(sh、source、exec)

    sh:父进程会fork一个子进程,shell script在子进程中执行

    source:在原进程中执行,不会fork子进程

    exec:在原进程中执行,但是同时会终止原进程

    注:使用export会把父进程中的变量向子进程中继承,但是反过来却不行,在子进程中,不管环境如果改变,均不会影响父进程

    根据例子来更加清晰的理解一下执行的过程:

    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"

    再依次按如下依次执行查看结果

    $ ./1.sh fork
    $ ./1.sh source
    $ ./1.sh exec

    (如不能运行可能是你新建文件后没有给予权限)

    运行:

    sudo chmod 755 1.sh 1.sh
    
    
    [sincerefly@localhost 123]$ ./1.sh fork
    PID for 1.sh before exec/source/fork:12559
    1.sh: $A is B
    using fork by default...
    PID for 2.sh: 12560
    2.sh get $A=B from 1.sh
    2.sh: $A is C
    PID for 1.sh after exec/source/fork:12559
    1.sh: $A is B
    
    [sincerefly@localhost 123]$ ./1.sh source
    PID for 1.sh before exec/source/fork:12562
    1.sh: $A is B
    using source...
    PID for 2.sh: 12562
    2.sh get $A=B from 1.sh
    2.sh: $A is C
    PID for 1.sh after exec/source/fork:12562
    1.sh: $A is C
    
    [sincerefly@localhost 123]$ ./1.sh exec
    PID for 1.sh before exec/source/fork:12564
    1.sh: $A is B
    using exec...
    PID for 2.sh: 12564
    2.sh get $A=B from 1.sh
    2.sh: $A is C
    [sincerefly@localhost 123]$

    由输出可知,source和exec都是在同一进程中执行,但是需要注意,使用exec终止了原来的父进程,因此,可以看到后两条命令没有执行

    (6)()与{}的区别

    ()将command group置于sub-shell去执行,也称为nested sub-shell
    {}则是在同一个shell内完成,也称为non-named command group

    通常而言,若所作的修改是临时的,且不想影响原有或以后的设定,那我们就 nested sub-shell ,
    反之,则用 non-named command group

    (7)bash中function的两种定义方式

    function function_name {
    command1
    command2
    command3
    ....
    }
    
    fuction_name () {
    command1
    command2
    command3
    ....
    }

    (8)$()的意思
    在 bash shell 中,$( ) 与 ` ` (反引号) 都是用来做命令替换用的

    command1 `command2 \`command3\` `
    等效于
    command1 $(command2 $(command3))

    上面的写法更有通用性,下面的方式更清晰明了
    一个$()的例子

    $ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)
    the last sunday is 2014-02-02

    (9)${}的意思

    ${}常用来界定变量名的范围

    还有更多的其它实用功能

    为了完整起见,我这裡再用一些例子加以说明 ${ } 的一些特异功能:
    假设我们定义了一个变量为:
    file=/dir1/dir2/dir3/my.file.txt
    我们可以用 ${ } 分别替换获得不同的值:
    ${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
    记忆的方法为:

    # 是去掉左边(在鑑盘上 # 在 $ 之左边)
    % 是去掉右边(在鑑盘上 % 在 $ 之右边)
    单一符号是最小匹配﹔两个符号是最大匹配。

    ${file:0:5}:提取最左边的 5 个字节:/dir1
    ${file:5:5}:提取第 5 个字节右边的连续 5 个字节:/dir2

    我们也可以对变量值裡的字串作替换:
    ${file/dir/path}:将第一个 dir 提换为 path:/path1/dir2/dir3/my.file.txt
    ${file//dir/path}:将全部 dir 提换为 path:/path1/path2/path3/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} :若 $file 没设定,则将 my.file.txt 输出至 STDERR。 (空值及非空值时不作处理)
    ${file:?my.file.txt} :若 $file 没设定或为空值,则将 my.file.txt 输出至 STDERR。 (非空值时不作处理)

    tips:
    以上的理解在于, 你一定要分清楚 unset 与 null 及 non-null 这三种赋值状态.
    一般而言, : 与 null 有关, 若不带 : 的话, null 不受影响, 若带 : 则连 null 也受影响.

    还有哦,${#var} 可计算出变量值的长度:
    ${#file} 可得到 27 ,因为 /dir1/dir2/dir3/my.file.txt 刚好是 27 个字节...


    (10)${}处理数组
    一般而言,A="a b c def" 这样的变量只是将 $A 替换为一个单一的字串,但是改为 A=(a b c def) ,则是将 $A 定义为组数...
    bash 的组数替换方法可参考如下方法:

    ${A[@]} 或 ${A[*]}

    可得到 a b c def (全部组数)

    ${A[0]}

    可得到 a (第一个组数),${A[1]} 则为第二个组数...

    ${#A[@]} 或 ${#A[*]}

    可得到 4 (全部组数数量)

    ${#A[0]}

    可得到 1 (即第一个组数(a)的长度),${#A[3]} 可得到 3 (第四个组数(def)的长度)

    A[3]=xyz

    则是将第四个组数重新定义为 xyz ...

    (11)$(())的意思

    它是用来作整数运算的。
    在 bash 中,$(( )) 的整数运算符号大致有这些:
    + - * / :分别为 "加、减、乘、除"。
    % :余数运算
    & | ^ !:分别为 "AND、OR、XOR、NOT" 运算。

    在$(())支持C语言中的自增自减操作,和比较判断,还支持2,8,16进制转换成十进制

    a=12
    
    echo $((a+=3)) #自加3
    
    echo $((a++)) #自增1
    
    echo $((a>10)) #是则输出1,否则输出0
    
    echo $((16#2a))

    (12)$@与$*

    先说下$#的作用,就是计算命令行有多少个参数

    以前面的 my.sh p1 "p2 p3" 为例:
    由于 p2 与 p3 之间的 IFS 是在 soft quote 中,因此 $# 可得到 2 的值。
    但如果 p2 与 p3 没有置于 quoting 中话,那 $# 就可得到 3 的值了。

    然后解释一下$@与$*

    若在 command line 上跑 my.sh p1 "p2 p3" p4 的话,
    不管是 $@ 还是 $* ,都可得到 p1 p2 p3 p4 就是了。
    但是,如果置于 soft quote 中的话:
    "$@" 则可得到 "p1" "p2 p3" "p4" 这三个不同的词段(word)﹔
    "$*" 则可得到 "p1 p2 p3 p4" 这一整串单一的词段。

    看一个演示程序

    #!/bin/bash
    
    my_fun() {
    echo "$#"
    }
    
    echo 'the number of parameter in "$@" is '$(my_fun "$@")
    echo 'the number of parameter in "$*" is '$(my_fun "$*")

    然后执行

    ./my.sh p1 "p2 p3" p4
    
    the number of parameter in "$@" is 3
    
    the number of parameter in "$*" is 1

    (13)&&与||

    看下面一个例子:

    $ A=123
    $ [ -n "$A" ] && echo "yes! it's ture." || echo "no, it's NOT ture."
    yes! it's ture.
    $ unset A
    $ [ -n "$A" ] && echo "yes! it's ture." || echo "no, it's NOT ture."
    no, it's NOT ture.

    下面的判断是:当 $A 被赋与值时,再看是否小于 100 ,否则送出 too big! :

    $ A=123
    $ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
    too big!

    若我将 A 取消,照理说,应该不会送文字才对啊(因为第一个条件就不成立了)...

    $ unset A
    $ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
    too big!

    为何上面的结果也可得到呢?
    又,如何解决之呢?
    (提示:修改方法很多,其中一种方法可利用第七章介绍过的 command group ...)

    [ -n "$A" ] && { [ "$A" -lt 100 ] || echo 'too big!'; }



    到此,1-10问中的大部分知识点都记录下来了,春节这两天没事儿就看点,记录点,方便以后复习查阅,本想都整理到一篇文章中的,只是记录到10问后发现还有不少内容需要记录但是本文已经2500多字了,不可能舍弃一些内容。所以拆个上下。

    网中人前辈整理的《shell十三问》确实使人收获颇多,每天看些,吸收些,真心不错,多看些真有点头痛呢、、引用白岩松同学的书名,真是“痛并快乐着”,哈哈、

  • 相关阅读:
    Delphi XE5 android 蓝牙通讯传输
    Delphi XE5 android toast
    Delphi XE5 android openurl(转)
    Delphi XE5 如何设计并使用FireMonkeyStyle(转)
    Delphi XE5 android 捕获几个事件
    Delphi XE5 android listview
    Delphi XE5 android 黑屏的临时解决办法
    Delphi XE5 android popumenu
    Delphi XE5 android 获取网络状态
    Delphi XE5 android 获取电池电量
  • 原文地址:https://www.cnblogs.com/ishell/p/4240136.html
Copyright © 2011-2022 走看看