美文网首页
20170805bash学习

20170805bash学习

作者: cndaqiang | 来源:发表于2017-08-07 11:28 被阅读132次

    bash学习

    参考

    Linux bash总结(一) 基础部分(适合初学者学习和非初学者参考)
    Linux 入门公开课
    C语言中文网-Shell教程
    处理linux的echo命令不输出转义符号的问题
    runoob-Shell 教程
    Linux标准输入/输出和重定向
    Linux 输入输出重定向, &>file, 2>&1, 1>&2 等
    exec的重定向 - GitHub
    linux shell 管道命令(pipe)使用及与shell重定向区别

    数据类型

    1.数值

    2.字符串

    字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似

    2.1单引号

    单引号的特点:

    • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
    • 单引号字串中不能出现单引号(对单引号使用转义符后也不行
    [chen@localhost tmp]$ echo 'hello$val'
    hello$val
    

    2.2双引号

    双引号的优点:

    • 双引号里可以有变量
    • 双引号里可以出现转义字符
    [chen@localhost tmp]$ echo "${val}hello\'"
    1hello\'
    

    字符串更多操作,见下面的 运算/2字符串运算

    变量&数组

    1.变量

    1.1定义,使用

    • 变量的定义不加$
    • 不加$表示变量本身,可以val++,加$表示变量存储的值val=$val+1
    • val=anything 等号两侧不能有空格
    ubuntu@VM-10-194-ubuntu:/tmp$ VAR="some"
    ubuntu@VM-10-194-ubuntu:/tmp$ NUM=123
    # 注意:等号两侧不能有空格! 变量名称全部用大写是编程惯例
    ubuntu@VM-10-194-ubuntu:/tmp$ echo VAR
    VAR
    ubuntu@VM-10-194-ubuntu:/tmp$ echo $VAR
    some
    # 调用变量要加 $
    [chen@localhost tmp]$ $val=hello
    -bash: 1=hello: command not found
    #$val表示变量存储的值,不能作为左值
    

    1.2只读变量

    格式 readonly val

    [chen@localhost tmp]$ val=hello
    [chen@localhost tmp]$ readonly val
    [chen@localhost tmp]$ val=1
    -bash: val: readonly variable
    

    1.3删除变量

    格式 unset val

    1.4变量类型

    运行shell时,会同时存在三种变量:

    • 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
    • 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
    • shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

    2.数组

    2.1定义,使用

    定义 数组名=(值1 值2 ... 值n) 或者 数组名[下标]=值
    使用 ${数组名[下标]}

    #创建 a=( )
    ubuntu@VM-10-194-ubuntu:/tmp$ a=(1 2 3 f g)
    ubuntu@VM-10-194-ubuntu:/tmp$ echo $a
    1
    #显示所有元素或者echo ${a[*]}    {}表示内部是一个整体,也可以 ${val},传入参数$10时也要${10}
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]}
    1 2 3 f g
    #显示数组元素,0代表第1个元素
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[2]}   
    3
    ubuntu@VM-10-194-ubuntu:/tmp$ a["sth"]=TT  #这样也行
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]} 
    TT 2 3 f g
    #或者逐个创建
    ubuntu@VM-10-194-ubuntu:/tmp$ a[6]=100
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]}
    TT 2 3 f g 100
    #显示数组元素删除a[2]的值,并不删除a[2]
    ubuntu@VM-10-194-ubuntu:/tmp$ unset a[2]   
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[2]}
    
    ubuntu@VM-10-194-ubuntu:/tmp$ unset a   #删除数组a所有元素
    #${数组名[@或*]:起始位置:长度} 输出数组的第1-3项
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]:0:3}
    #${数组名[@或*]/查找字符/替换字符   替换输出,并不改变数组
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[*]/1/6}
    

    2.2获取数组长度

    # 取得数组元素的个数
    length=${#array_name[@]}
    # 或者
    length=${#array_name[*]}
    # 取得数组单个元素的长度
    lengthn=${#array_name[n]}
    

    bash文件

    1. 创建文件

    ubuntu@VM-10-194-ubuntu:/tmp$ > test.sh
    ubuntu@VM-10-194-ubuntu:/tmp$ touch test2.sh
    ubuntu@VM-10-194-ubuntu:/tmp$ echo "something" >test3.sh
    ubuntu@VM-10-194-ubuntu:/tmp$ cat test3.sh 
    something
    

    创建多行,使用了重定向的知识,重定向见本文后段

    ubuntu@VM-10-194-ubuntu:/tmp$ cat >test4.sh<<EOF
    > some
    > shu wan zhi jie hui che
    > zi dong tian jia >
    > EOF
    #EOF为任意字符串
    ubuntu@VM-10-194-ubuntu:/tmp$ cat test4.sh 
    some
    shu wan zhi jie hui che
    zi dong tian jia >
    

    不过还是喜欢,vi file 直接输入
    2. 添加执行权限

    $ chmod +x test.sh 
    

    3. 编写脚本

    #!/bin/bash
    echo "hello bash"
    exit 0
    

    说明:
    #!/bin/bash : 它是bash文件声明语句,表示是以/bin/bash程序执行该文件。它必须写在文件的第一行!
    echo "hello bash" : 表示在终端输出“hello bash”
    exit 0 : 表示返回0。在bash中,0表示执行成功,其他表示失败。

    4. 执行

    ubuntu@VM-10-194-ubuntu:/tmp$ ./test.sh 
    hello bash
    

    注释

    • 单行注释 以“#”开头的行就是注释
    • 多行注释 sh里没有多行注释,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果
    zhushi()
    {
    zhushi注释内容
    echo zhushi
    }
    :||{
    被注释的多行内容
    }
    

    运算

    1 数值运算

    数值元算主要有4种实现方式:(())、let、expr、bc。
    工作效率:
    (()) == let > expr > bc

    • (())和let是bash内建命令,执行效率高,支持i++;而expr和bc是系统命令,会消耗内存,执行效率低。
    • 只有bc支持浮点运算
    
    #!/bin/bash
    #(())  要加个$再赋予变量
    val=$((3*(5+2)))
    echo $val
    
    #let
    let "val=3*(5+2)"
    echo $val
    
    #expr 又费劲  注意是`重音符  ``输出命令结果,例如val=`ls`,val=`date`
    #还得用空格隔开运算符和数字,不然显示不对
    val=`expr 3 \* \( 5 + 2 \)`
    echo $val
    
    #bc 是重音符`
    val=`echo "3*(5+2)"|bc`
    echo $val
    

    使用$(())或let来进行整数计算,bc进行浮点数计算
    $(( 3+3 ))
    val=`echo "scale=3;1/3"|bc` 重音符
    **i++ ((val++))或者 let val++

    2 字符串运算

    2.1字符串的使用


    ubuntu@VM-10-194-ubuntu:/tmp$ str="hello world"
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str}
    hello world
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${strq:?ERR_MSG}
    -bash: strq: ERR_MSG
    

    2.2 字符串操作


    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${#str}
    11
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str:3:6}
    lo wor
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str%ld}
    hello wor
    #并不影响原字符串
    ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str}
    hello world
    
    

    流程控制(判断循环)

    1 条件判断

    1.1 test

    语法

    test EXPRESSION
    

    test是关键字,表示判断;
    EXPRESSION是被判断的语句。

    1.1 数值判断

    参数 说明
    -eq 等于则为真
    -ne 不等于则为真
    -gt 大于则为真
    -ge 大于等于则为真
    -lt 小于则为真
    -le 小于等于则为真
    [chen@localhost tmp]$ test 12 -eq 2
    [chen@localhost tmp]$ echo $?
    1
    # $?表示上一命令的执行结果(在linux中bash中,true返回0,false返回1
    

    1.2字符串测试

    参数 说明
    = 等于则为真
    != 不相等则为真
    -z 字符串 字符串的长度为零则为真
    -n 字符串 字符串的长度不为零则为真

    [chen@localhost tmp]$ test "hello" != "yes"
    [chen@localhost tmp]$ echo $?
    0
    

    1.3文件测试

    linux文件:目录、链接、设备、文本。。。

    参数 说明
    -e 文件名 如果文件存在则为真
    -r 文件名 如果文件存在且可读则为真
    -w 文件名 如果文件存在且可写则为真
    -x 文件名 如果文件存在且可执行则为真
    -s 文件名 如果文件存在且至少有一个字符则为真即文件长度大于0、非空
    -d 文件名 如果文件存在且为目录则为真
    -f 文件名 如果文件存在且为普通文件则为真
    -L 文件名 如果文件存在且为符号连接
    -c 文件名 如果文件存在且为字符型特殊文件则为真
    -b 文件名 如果文件存在且为块特殊文件则为真
    -u 文件名 如果文件存在且有suid位设置

    ubuntu@VM-10-194-ubuntu:/tmp$ test -f ./123.txt 
    ubuntu@VM-10-194-ubuntu:/tmp$ echo $?
    0
    

    1.2 []条件判断

    语法

    [ EXPRESSION ]
    

    中括号的左右扩弧和EXPRESSION之间都必须有空格
    空格空格
    关于EXPRESSION的说明,参考如下:

    ubuntu@VM-10-194-ubuntu:/tmp$ [ -f test2.sh ]
    ubuntu@VM-10-194-ubuntu:/tmp$ echo $?
    0
    ubuntu@VM-10-194-ubuntu:/tmp$ [ -f test2.s11h ]
    ubuntu@VM-10-194-ubuntu:/tmp$ echo $?
    1
    

    1.3 逻辑

    基本格式(上表也有)
    -a : 逻辑与,操作符两边均为真,结果为真,否则为假。
    -o : 逻辑或,操作符两边一边为真,结果为真,否则为假。
    ! : 逻辑否,条件为假,结果为真。
    [ expre ] && [ expre2 ] 与
    [ expre ] || [ expre2 ] 或

    #判断test2.sh是否可读写
    [ -r test2.sh -a -w test2.sh ]
    

    2 if [ ] ; then cmd; elif [ ] ; then cmd ; else cmd ; fi

    if 或者 elif 后面必须有then,else直接跟命令,可以没有,最后以fi结尾
    在bash文件中,回车,不需要分号,单行if命令需要分号
    [] 可为 test、[]判断,计算式(( ))等,以下涉及判断条件同理
    例1

     if [ 1 -eq 2 ] 
    > then echo 1==2
    > elif [ 2 -eq 3 ]
    > then 2==3
    > elif [ 3 -eq 4 ]
    > then echo 3==4
    > else echo 1=1 
    > fi
    #或者
    if [ 1 -eq 2 ] ; then echo 1==2; elif [ 2 -eq 3 ]; then 2==3; elif [ 3 -eq 4 ]; then echo 3==4; else echo 1==1; fi
    1==1
    

    例2

    #!/bin/bash
    # 提示用户输入一个值
    echo -n "please input a number:"
    # 保存用户的输入到num中
    read NUM
    if [ $NUM -le 5 ] ;
    then echo "$NUM<5";
    else echo "$NUM>5";
    #分号可有可无
    fi
    exit 0
    

    3 case

    # 格式
    case 值 in
    模式1}
    命令1
    ...
    ;;
    模式2)
    命令2
    ...
    ;;
    esac
    

    #!/bin/bash
    echo -n "are you femail(Y/N)"
    read val
    case $val in
    Y|y)
    echo "yes"
    ;;    # 注意此处有两个;
    N|n)
    echo "no"
    ;;
    *)
    echo "incorrect input"
    ;;
    esac      # 注意esac结束
    exit 0
    

    4 for循环

    for 变量名in列表  或者 for  ((i=1;i<7;i++)),**注意两个括号**
    do
    命令1
    命令2...
    done
    

    # 判断当前目录下的文件并输出文件名
    #!/bin/bash
    #将ls结果保存到变量CUR,
    CUR=`ls`    #注意这里是Tab键上面的`
    # 大部分Unix shell以及编程语言如Perl、PHP以及Ruby等都以成对的重音符作指令替代,意思是以
    某一个指令的输出结果作为另一个指令的输入项。例如上述指令
    echo $CUR
    for val in $CUR
    do     #do不可少
    if [ -f $val ] ;
    then  echo "FILE: $val" ;
    fi
    done   #done 不可少
    exit 0
    
    #计算10以内数的和
    #!/bin/bash
    sum=0
    for ((i=1;i<10;i++))
    do
    ((sum=$sum+$i))
    # (( ))表示数值计算
    done
    echo "sum=$sum"
    exit 0
    

    5 until

    循环至少运行一次
    当满足条件时不再执行直接结束

    until 条件
    do
    命令1
    ...
    done
    

    例;输出5以内的自然数

    #!/bin/bash
    val=0
    until (($val==5))   #或者[ "$val" -eq "5" ] 
    do
    echo $val
    ((val++))
    done
    exit 0
    

    6 while

    满足条件才进行循环,最少不运行

    while 命令
    do
    命令1
    命令2
    ...
    done
    

    例;输出5以内的自然数

    #!/bin/bash
    val=0
    while ((val<5))
    do
    echo $val
    ((val=$val+1))
    done
    exit 0
    

    7 break 与continue

    break命令允许跳出循环-----结束
    continue命令跳过这个循环步-----进入下个循环

    例break输出5内自然数

    #!/bin/bash
    val=0
    while true
    do
    if (($val==5));
    then break;
    fi
    echo $val
    ((val++))
    done
    exit 0
    

    例:从0开始逐步递增到10:当数值为5时,将数值递增2;否则,输出数值

    #!/bin/bash
    val=0
    while (($val<=10))
    do
    if (($val==5))   #好像没有;用<Enter>也是可以的
    then ((val=$val+2))
    continue
    else
    echo $val 
    ((val++))
    fi
    done
    exit 0
    

    函数

    所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可

    1 定义

    基本格式

    function 函数名()
    {
    ...
    return 返回值;
    }
    
    • function可有可无。但建议保留,因为保留的话看起来更加直观
    • return 返回值,如果不加,将以最后一条命令运行结果,作为返回值
    #!/bin/bash
    function foo()
    {
    echo "hello"
    return 5;
    }
    foo
    echo $?
    #直接编写foo函数文件,在终端中运行foo“返回值为0”,没有echo结果
    

    结果

    [chen@localhost tmp]$ ./han
    hello
    5
    

    2 传递参数

    $n代表输入的第n个参数

    foo param1 param2 param3
    #参数变量出入函数内部为@ 例
    echo $@
    #输出结果为
    param1 param2 param3
    
    参数 说明
    $? 显示上一条命令的退出状态。判断退出true为0,flase为1,函数退出结果为返回值,例
    [chen@localhost tmp]$ ((1+1))
    [chen@localhost tmp]$ echo $?
    0
    $# 传递到脚本的参数个数
    $n 表示传入的第n个参数
    $10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数
    $* 以一个单字符串显示所有向脚本传递的参数。如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
    $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
    $- 显示Shell使用的当前选项,与set命令功能相同。
    $$ 脚本运行的当前进程ID号
    $! 后台运行的最后一个进程的ID号
    $0 当前sehll名 [chen@localhost tmp]$ echo $0 结果为-bash

    $* 和 $@ 的区别

    • $* 和 $@ 表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。
    • 但是当它们被双引号(" ")包含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。

    #!/bin/bash
    for val in "$*"
    do echo $val
    done
    echo \"
    for val in "$@"
    do echo $val
    done
    exit 0
    
    ubuntu@VM-10-194-ubuntu:/tmp$ ./can 1 2 3 4
    1 2 3 4
    "
    1
    2
    3
    4
    

    内建命令

    1 内建指令查看

    基本格式
    type cmd
    格式说明
    type是命令关键字,cmd表示查看的命令;若输出builtin,则该命令是bash的内建命令。
    例如:
    ubuntu@VM-10-194-ubuntu:/tmp$ type echo
    echo is a shell builtin
    除此之外,用户也可以通过man bash或者man builtins查看bash的全部内置命令
    

    2 常用内建命令

    (01)echo
    命令:echo arg
    功能:在屏幕上显示出由arg指定的字串
    (02)read
    命令格式:read变量名表
    功能:从标准输入设备读入一行,分解成若干字,赋值给bash程序内部定义的变量
    (03)shift
    命令:shift [N] (N为大于0的整数;当N省略时,等价与于“shift 1”)
    功能:所有的参数依次向左移动N个位置,并使用$#减少N,直到$#=0为止。
    (04)alias
    命令:alias name='value'
    功能:别名。用name替换value,value要用单引号括住。
    (05)export
    命令:export变量名[=变量值]
    功能:export可以把bash的变量向下带入子bash(即子bash中可以使用父bash的变量),从而让子进程继承父进程中的环境变量。但子bash不能用export把它的变量向上带入父bash。
    (06)readonly
    命令:readonly 变量名
    功能:定义只读变量。不带任何参数的readonly命令将显示出所有只读变量。
    (07)exec
    命令:exec 命令参数
    功能:当bash执行到exec语句时,不会去创建新的子进程,而是转去执行指定的命令,当指定的命令执行完时,该进程(也就是最初的bash)就终止了,所以bash程序中exec后面的语句将不再 
    被执行。
    (08)"."(点)
    命令:. bash程序文件名
    功能:使bash读入指定的bash程序文件并依次执行文件中的所有语句。
    (09)exit
    命令:exit N
    功能:退出Shell程序。在exit之后可有选择地指定一个数位作为返回状态。
    

    echo

    • echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。命令格式
    echo "anything"
    "可有可无
    
    • 1 显示转义字符 backslash escapes
      在echo命令的帮助中,写到默认禁止反斜杠功能。如果需要开起反斜杠转义功能,需要使用-e的参数。
    转义字符 含义
    \\ 反斜杠
    \a 警报,响铃
    \b 退格(删除键)
    \f 换页(FF),将当前位置移到下页开头
    \n 换行
    \r 回车
    \t 水平制表符(tab键)
    \v 垂直制表符

    #!/bin/bash
    echo -e  "hello\c"
    echo "hell\c"
    echo  hell
    exit 0
    

    结果

    hellohell\c
    hell
    

    可看到,需要 -e 才能启用转义字符功能
    对于 " /需要// /" /$来输出,不适用上述规则

    • 2 显示变量
    echo $val
    

    当变量与其它字符相连时用{}隔开

    echo ${val}-2-3 
    
    • 3 显示结果重定向至文件(写入另一个文件)
    [chen@localhost tmp]$ echo rete >file
    [chen@localhost tmp]$ cat file
    rete
    
    • 4 原样输出字符串
      单引号
    [chen@localhost tmp]$ echo '\$cal'
    \$cal
    
    • 5 输出命令结果
      重音符`
    [chen@localhost tmp]$ echo `date`
    Mon Aug 7 00:23:33 PDT 2017
    

    双引号可有可无,单引号主要用在原样输出中,重音符输出命令

    printf

    printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。

    注意:printf 由 POSIX 标准所定义,移植性要比 echo 好。

    格式

    printf  format-string  [arguments...]
    
    • format-string 为格式控制字符串
    • arguments 为参数列表
    [chen@localhost tmp]$ printf "%d%s%c%f\n" 1 "abc" "def" "0.1"
    1abcd0.100000
    

    %d %s %c %f 格式替代符详解:
    d:data 数字 -- 对应位置参数必须是数字型,否则报错!
    s:str 字符串 -- 对应位置参数必须是字符串或者字符型,否则报错!
    c:char 字符 -- 对应位置参数必须是字符串或者字符型,否则报错!
    f:float 浮点 -- 对应位置参数必须是数字型,否则报错!
    如:上面第三个参数是 "def",%c 自动截取字符串的第一个字符作为结果输出,若第四个参数无0.1则输出0.000000

    转义字符 含义
    \\ 反斜杠
    \a 警报,响铃
    \b 退格(删除键)
    \f 换页(FF),将当前位置移到下页开头
    \n 换行
    \r 回车
    \t 水平制表符(tab键)
    \v 垂直制表符
    \c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
    \ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
    \0ddd 表示1到3位的八进制值字符

    指定输出宽度
    %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐)
    默认转义

    转义字符 含义
    \\ 反斜杠
    \a 警报,响铃
    \b 退格(删除键)
    \f 换页(FF),将当前位置移到下页开头
    \n 换行
    \r 回车
    \t 水平制表符(tab键)
    \v 垂直制表符
    \c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
    \ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
    \0ddd 表示1到3位的八进制值字符

    与C语言printf()函数的不同

    • printf 命令不用加()
    • format-string 可以没有引号,但最好加上,单引号双引号均可,不加上运行结果偶尔异常,如
    [chen@localhost tmp]$ printf "hello\n"
    hello
    [chen@localhost tmp]$ printf hello\n
    hellon[chen@localhost tmp]$
    
    • 参数多于格式控制符(%)时,format-string 可以重用,可以将所有参数都转换,即未指定格式使用前面的格式
    [chen@localhost tmp]$ printf "%s\n%s%s\n" 1 2 3
    1
    23
    [chen@localhost tmp]$ printf "%s\n" 1 2 3    #格式不够使,借前面的格式
    1
    2
    3
    
    • arguments 使用空格分隔,不用逗号。
    [chen@localhost tmp]$ printf "%s\n" 1,2 3  #逗号不分隔,空格分隔
    1,2
    3
    

    调试

    1 bash命令调试

    bash [-nvx] scripts.sh
    

    选项与参数:

    -n :不要执行 script,仅查询语法的问题;
    -v :再执行 sccript 前,先将 scripts 的内容输出到屏幕上;
    -x :将使用到的 script 内容显示到屏幕上,这是很有用的参数!
    例如,想要执行bash脚本,并查看bash的调用流程,可以通过以下命令:
    $ bash -x test.sh
    

    2 echo调试

    echo [OPTION] STRING
    -n : 输出内容之后,不换行。默认是输入内容之后,换行。
    -e : 开启反斜线“\”转义功能
    -E : 关闭反斜线“\”转义功能(**默认**)。
    例如,输出“please input a number:”之后不换行。
    $ echo -n "please input a number:"
    

    3 printf

    和echo一样,printf也能用于输出。语法格式和C语言中printf一样。
    例如,输出“hello printf”之后换行。
    $ printf "hello printf\n"
    

    输入输出重定向

    标准输入,标准输出,标准错误输出
    执行一个shell命令行时通常会自动打开三个标准文件,即

    • 标准输入文件(stdin),即shell的输入设备(linux里设备也是文件),通常对应终端的键盘;
    • 标准输出文件(stdout)为执行命令成功时将运行的结果输出到的文件
    • 标准错误输出到文件(stderr)为执行命令报错时将错误信息输出到的文件,这两个文件都对应终端的屏幕。

    /dev下可以看到 stdin stdout stderr三个文件分别为标准输入,标准输出,标准错误输出,通过下面的例子可以看到 stdin stdout stderr分别连接到不同的文件

    ubuntu@VM-10-194-ubuntu:/dev$ ls -all std*
    lrwxrwxrwx 1 root root 15 Aug  7 09:34 stderr -> /proc/self/fd/2
    lrwxrwxrwx 1 root root 15 Aug  7 09:34 stdin -> /proc/self/fd/0
    lrwxrwxrwx 1 root root 15 Aug  7 09:34 stdout -> /proc/self/fd/1
    

    输入输出重定即: 改变输入/输出数据的的读取/输出的默认文件(键盘或终端)为其他文件(普通文件,文件描述符,文件设备),而且只能是文件

    常用操作符

    名称 默认文件描述符 操作符
    标准输入(stdin) 0 < ,<<,0<或0<<
    0<与<等价,其他带有文件描述符的操作符意义同理,<<代表追加
    标准输出(stdout) 1 >, >>, 1> 或 1>>
    标准错误输出(stderr) 2 2> 或 2>>

    输入重定向

    输入重定向是指把命令(或可执行程序)的标准输入重定向到指定的文件中。也就是说,输入可以不来自键盘,而来自一个指定的文件。所以说,输入重定向主要用于改变一个命令的输入源,特别是改变那些需要大量输入的输入源。

    格式

    命令 操作符 重定向文件
    

    示例命令

    格式 说明
    Command < filename Command 命令以 filename 文件作为标准输入
    Command < filename > filename2 Command 信不信以 filename 文件作为标准输入,以 filename 2 作为标准输出
    注: 输入(>或>>) 与 输出(<或<<)无强制顺序 即wc >test<<delim与wc <<delim >test一样
    Command << delimiter 从标准输入中读入,以 delimiter(任意) 为结束符,祥见示例

    注: 上表格中的重定向仅在当前命令生效

    wc 统计指定文件包含的行数、单词数和字符数

    #当只输入wc时,回等待用户输入,直至 ctrl+d停止,然后统计行数、单词数和字符数
    ubuntu@VM-10-194-ubuntu:/tmp$ wc
    123 34
    23      1       3       9
    # 也可将输入重定向为某个文件
    ubuntu@VM-10-194-ubuntu:/tmp$ wc < file
    13 13 95
    
    ubuntu@VM-10-194-ubuntu:/tmp$ wc <<delim
    > haha
    > test
    > lala
    > delim
     3  3 15
    #或者,将结果输出到test中
    ubuntu@VM-10-194-ubuntu:/tmp$ wc >test<<delim
    > haha
    > test
    > lala
    > delim
    ubuntu@VM-10-194-ubuntu:/tmp$ cat test
     3  3 15
    

    输出重定向

    输出重定向是指把命令(或可执行程序)的标准输出或标准错误输出重新定向到指定文件中。这样,该命令的输出就不显示在屏幕上,而是写入到指定文件中。

    输出重定向比输入重定向更常用,很多情况下都可以使用这种功能。例如,如果某个命令的输出很多,在屏幕上不能完全显示,那么将输出重定向到一个文件中,然后 再用文本编辑器打开这个文件,就可以查看输出信息;如果想保存一个命令的输出,也可以使用这种方法。

    格式

    命令 操作符 重定向文件
    

    注意标准输出和标准错误输出不是"一个"文件,所以有两个输出,只改一个的话,另一个不变,同时改变需要2>file 1>file(意思是标准输出到file,标准错误输出到file),或者>file 2>&1(表示标准输出到file,标准错误输出到标准输出的文件即file)或者&>file (意思是把 标准输出 和 标准错误输出 都重定向到文件file中)
    >>为追加输入,即在原文件内末尾添加
    示例命令

    格式 说明
    Command > filename 把标准输出重定向到一个新文件中
    Command >> filename 把标准输出重定向到一个文件(追加)
    Command 2> filename 把标准错误重定向到一个文件中
    Command 2>> filename 把标准错误重定向到一个文件中(追加)
    Command > filename 2>&1 把标准输出和错误一起重定向到一个文件中
    Command >> filename 2>&1 把标准输出和错误一起重定向到一个文件中(追加)

    注: 上表格中的重定向仅在当前命令生效

    ubuntu@VM-10-194-ubuntu:/tmp$ ls vv >file
    ubuntu@VM-10-194-ubuntu:/tmp$ ls vv >>file
    ubuntu@VM-10-194-ubuntu:/tmp$ cat file
    vv
    vv
    

    绑定重定向

    前两种重定向仅生效一次,绑定重定向即永久重定向输入输出文件,所有命令的输入输出都都来自重定向后的文件
    格式

    exec 操作符 重定向文件
    

    操作符 < 0< << > 1> .... 与之前意义相同

    # 将输出绑定重定向到/tmp/test后命令的结果不会输出到终端
    ubuntu@VM-10-194-ubuntu:/tmp$ exec >/tmp/test
    ubuntu@VM-10-194-ubuntu:/tmp$ ls
    ubuntu@VM-10-194-ubuntu:/tmp$ ls -11
    # 将输出重定向回/dev/tty(注意必须写完整路径)则终端又输出命令运行结果
    ubuntu@VM-10-194-ubuntu:/tmp$ exec >/dev/tty
    ubuntu@VM-10-194-ubuntu:/tmp$ ls
    !  1  cal.sh  cancan  cvm_init.log  ff    net_affinity.log  sagent.pid  test  vv
    0  2  can     chon    ec            file  pin               setRps.log  tty
    

    文件描述符

    文件描述符,可以理解为文件的一个数字标记

    • &0代表标准输入文件的标记为0
    • &1 代表标准输出文件标记为1
    • &2代表标准错误输出
    • &-代表关闭与它绑定的描述符

    &n 代表是已经存在的文件描述符,在重定向时与文件地位相同,即重定向的文件也可以用文件描述符来表示

    格式

    #指定m描述的文件
    命令 m>file
    #一般为一次性重定向,绑定重定向命令为exec,
    # file也可以为&n,这样m->n->n描述的文件
    

    示例命令

    格式 说明
    Command >&n 把标准输出重定向到文件描述符 n 中,如 ls >&1 ,若要实现绑定重定向则Command为exec如 exec >&n
    Command m>&n 把往文件描述符 m 的输出重定向到文件描述符 n 上,2>&1,若要实现绑定重定向则 exec m>&n
    Command <&- 关闭标准输入同样绑定则Command为exec
    Command 2>&- 关闭标准错误输出,和 2>/dev/null 有类似功效,同样绑定则Command为exec
    Command n>&- 关闭输出文件描述符n,和 2>/dev/null 有类似功效,同样绑定则Command为exec

    ubuntu@VM-10-194-ubuntu:/tmp$ exec 6>&1
    #原理: 6 -> 1 -> stdout -> /dev/tty
    ubuntu@VM-10-194-ubuntu:/tmp$ exec 1>test.txt
    #原理: 1->test.txt 同时 stdout -> test.txt
    ubuntu@VM-10-194-ubuntu:/tmp$ ls
    #原理: ls结果输出到/tmp/test.txt了
    ubuntu@VM-10-194-ubuntu:/tmp$ exec 1>&6
    #原理: 1-> 6 -> /dev/tty 同时 stdout -> /dev/tty,也可直接exec 1>/dev/tty或者 exec 1>&2
    ubuntu@VM-10-194-ubuntu:/tmp$ ls
    cvm_init.log  net_affinity.log  sagent.pid  setRps.log  test.txt
    #原理: ls结果输出到/dev/tty即终端
    ubuntu@VM-10-194-ubuntu:/tmp$ ls /dev/fd
    0  1  2  3  6
    #添加文件描述符后在/dev/fd里可以看到
    ubuntu@VM-10-194-ubuntu:/tmp$ exec 6>&-
    #
    ubuntu@VM-10-194-ubuntu:/tmp$ ls /dev/fd
    0  1  2  3
    
    

    备注

    1. > < n>等操作符可以同时出现在一条命令里,而且无顺序要求,如
    ubuntu@VM-10-194-ubuntu:/tmp$ exec 1>test 2>/dev/tty
    
    2. 命令行前面这一条ubuntu@VM-10-194-ubuntu:/tmp$以及输入的命令在键盘上的显示好像属于标准错误输出
    3. 命令 < 文件 中的命令应该能接受标准输入 ,命令 > 文件中的命令应该有标准输出
    4. 在类Unix系统中,/dev/null,或称空设备,是一个特殊的设备文件,它丢弃一切写入其中的数据(但报告写入操作成功),读取它则会立即得到一个EOF,可以重定向输出到/dev/null

    其他传递

    管道

    将一个程序或命令的输出作为另一个程序或命令的输入,有两种方法,一种是通过一个临时文件将两个命令或程序结合在一起,例如

    ubuntu@VM-10-194-ubuntu:/tmp$ ls > test
    ubuntu@VM-10-194-ubuntu:/tmp$ cat test
    cvm_init.log
    net_affinity.log
    sagent.pid
    setRps.log
    test
    

    另一种是Linux所提供的管道功能。

    ubuntu@VM-10-194-ubuntu:/tmp$ ls | cat
    cvm_init.log
    net_affinity.log
    sagent.pid
    setRps.log
    test
    

    管道工作流程


    command1正确输出,作为command2的输入 然后comand2的输出作为,comand3的输入 ,comand3输出就会直接显示在屏幕上面了。

    通过管道之后:comand1,comand2的正确输出不显示在屏幕上面

    注意:

    1. 管道命令只处理前一个命令正确输出,不处理错误输出
    2. 管道命令右边命令,必须能够接收标准输入流命令才行

    管道与重定向的区别

    1. 管道
      左边的命令应该有标准输出 | 右边的命令应该接受标准输入
      重定向
      左边的命令应该有标准输出 > 右边只能是文件
      左边的命令应该需要标准输入 < 右边只能是文件
    2. 管道触发两个子进程执行|两边的程序;而重定向是在一个进程内执行
    #可以相互转换情况
    #输入重定向
     
    [chengmo@centos5 shell]$ cat test.sh| grep -n 'echo'
    5:    echo "very good!";
    7:    echo "good!";
    9:    echo "pass!";
    11:    echo "no pass!";
    #"|"管道两边都必须是shell命令
     
     
    [chengmo@centos5 shell]$ grep -n 'echo' <test.sh    
    5:    echo "very good!";
    7:    echo "good!";
    9:    echo "pass!";
    11:    echo "no pass!";
    #"重定向"符号,右边只能是文件(普通文件,文件描述符,文件设备)
     
     
    [chengmo@centos5 shell]$ mail -s 'test' 8292669@qq.com <test.sh
    [chengmo@centos5 shell]$ cat test.sh|mail -s 'test' 8292669@qq.com
    #以上2个也相同,将test.sh内容发送到指定邮箱。
     
     
    [chengmo@centos5 shell]$ (sed -n '1,$p'|grep -n 'echo')<test.sh 
    5:    echo "very good!";
    7:    echo "good!";
    9:    echo "pass!";
    11:    echo "no pass!";
    #这个脚本比较有意思了。由于前面是管道,后面需要把test.sh内容重定向到 sed ,然后sed输出通过管道,输入给grep.需要将前面用"()"运算符括起来。在单括号内的命令,可以把它们看作一个象一个命令样。如果不加括号test.sh就是grep 的输入了。
     
     
    #上面一个等同于这个
    [chengmo@centos5 shell]$ sed -n '1,$p'<test.sh | grep -n 'echo'
    5:    echo "very good!";
    7:    echo "good!";
    9:    echo "pass!";
    11:    echo "no pass!";
     
    #重定向运算符,在shell命令解析前,首先检查的(一个命令,执行前一定检查好它的输入,输出,也就是0,1,2 设备是否准备好),所以优先级会最高
     
     
    [chengmo@centos5 shell]$ sed -n '1,10p'<test.sh | grep -n 'echo' <testsh.sh
    10:echo $total;
    18:echo $total;
    21:     echo "ok";
    #哈哈,这个grep又接受管道输入,又有testsh.sh输入,那是不是2个都接收呢。刚才说了"<"运算符会优先,管道还没有发送数据前,grep绑定了testsh.sh输入,这样sed命令输出就被抛弃了。这里一定要小心使用
     
    #输出重定向
     
    [chengmo@centos5 shell]$ cat test.sh>test.txt
    [chengmo@centos5 shell] cat test.sh|tee test.txt &>/dev/null
    #通过管道实现将结果存入文件,还需要借助命令tee,它会把管道过来标准输入写入文件test.txt ,然后将标准输入复制到标准输出(stdout),所以重定向到/dev/null 不显示输出
    #">"输出重定向,往往在命令最右边,接收左边命令的,输出结果,重定向到指定文件。也可以用到命令中间。
     
     
    [chengmo@centos5 shell]$ ls test.sh test1.sh testsh.sh 2>err.txt | grep 'test'
    test.sh
    testsh.sh
    #目录下面有:test,testsh文件,test1.sh不存在,因此将ls 命令错误输出输入到err.txt 正确输出,还会通过管道发送到grep命令。
    [chengmo@centos5 shell]$ ls test.sh test1.sh testsh.sh &>err.txt | grep 'test'
    #这次打印结果是空,&代表正确与错误输出 都输入给err.txt,通过管道继续往下面传递数据为空,所以没有什么显示的
     
    #同样">"输出重定向符,优先级也是先解析,当一个命令有这个字符,它就会与左边命令标准输出绑定。准备好了这些,就等待命令执行输出数据,它就开始接收
    

    命令替换(重音符`)

    命令替换和重定向有些相似,但区别在于命令替换是将一个命令的输出作为另外一个命令的参数

    重定向必须为一个文件,而重音符``是将命令的结果作为参数传递例如

    ubuntu@VM-10-194-ubuntu:/tmp$ echo 192.168.1.1 > file
    # ping需要参数,不是文件,即不能接受标准输入,可以接受重音符运行的参数,不能接受重定向的输入文件
    ubuntu@VM-10-194-ubuntu:/tmp$ ping `cat file`
    PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
    ^Z
    [8]+  Stopped                 ping `cat file`
    ubuntu@VM-10-194-ubuntu:/tmp$ echo 192.168.1.1 >file
    ubuntu@VM-10-194-ubuntu:/tmp$ ping < file
    Usage: ping [-aAbBdDfhLnOqrRUvV]...省略
    ##cat 可以接收文件,也可以接受参数,即可以接受标准输入
    ubuntu@VM-10-194-ubuntu:/tmp$ cat file
    192.168.1.1
    ubuntu@VM-10-194-ubuntu:/tmp$ cat < file
    192.168.1.1
    ubuntu@VM-10-194-ubuntu:/tmp$ cat `echo file`
    192.168.1.1
    

    还有好多需要学的,留在下篇继续

    相关文章

      网友评论

          本文标题:20170805bash学习

          本文链接:https://www.haomeiwen.com/subject/bqvilxtx.html