博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Shell 编程进阶(四)
阅读量:7220 次
发布时间:2019-06-29

本文共 14180 字,大约阅读时间需要 47 分钟。

更改字段分隔符

默认情况下,bash shell会将以下字符当作字段分隔符。

空格

制表符 TAB

换行符\n

环境变量中有有一个叫IFS的变量用来存放分隔符的。

这里有一个文件,里面各种格式,我们用for 来遍历里面的数据#cat a.txt i love youwho are youroot:xxx:/bin/bashabc;cdf;运行结果#bash IFS.sh iloveyouwhoareyouroot:xxx:/bin/bashabc;cdf;

可是,这并不是我们预期的效果。

碰到这种情况,环境变量IFS就有绝妙的用法。假如要遍历一个文件中用冒号:分隔的值,(/etc/passwd).要做的就是将IFS的值设定为IFS=:上面的例子中,我们想要的是一句完整的句子,并且把冒号:和分号;的字段做为单独的值,那么IFS就得这样设置IFS=:;这个赋值会将换行符\n 、 冒号:分号;作为字段的分隔符。如何使用IFS字符解析数据没有任何限制。
#!/bin/bashIFS=:;for i in $(cat a.txt);do        echo $idone注意上面的分隔符中的分号#bash IFS.sh i love youwho are yourootxxx/bin/bashabc;cdf;
#!/bin/bash#!/bin/bashIFS=:";"for i in $(cat a.txt);do        echo $idone注意上面的分隔符中的分号,结果是不一样的#bash IFS.sh i love youwho are yourootxxx/bin/bashabccdf如果想在一个脚本既想用新设置的IFS值,又想在其他地方调用原来的IFS值,建议的做法是:IFS.old=$IFSIFS=新值

执行以下代码,可以显示出在环境变量PATH所包含的所有目录中全部的可执行文件,数量还真不行呢

#!/bin/bashIFS=:for folder in $PATH;do        echo "$folder"        for file in $folder/*;do                [ -x "$file" ] && echo "$file"        donedone运行结果.../usr/bin/zipsplit/usr/bin/zless/usr/bin/zmore/usr/bin/znew/usr/bin/zsoelim/root/bin...#bash PTAH.sh |wc -l1660

字符串切割

 ${#var}

返回字符串变量var的长度

#echo $abcabcdefghijklmnopqrstuvwxyz#echo ${#abc}26

 ${var:offset}

返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,

offset的取值在0到 ${#var}-1 之间(bash4.2后,允许为负值)

#echo $num123456789#echo ${num:3:4}4567#echo ${num:3}456789注意以下的,并不会以负数的方式处理#echo $num-10-9-8-7-6-5-4-3-2-10123456789#echo ${num:3:2}-9不同版本bash的差异CentOS release 6.9 (Final) bash, version 4.1.2(2)CentOS Linux release 7.4.1708 (Core) bash, version 4.2.46(2)Centos 7#echo $num123456789#echo ${num: -5:-2}567Centos 6#echo $num123456789#echo ${num: -5:-2}-bash: -2: substring expression < 0

 ${var:offset:number}

返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分

#echo $num123456789#echo ${num:3:4}4567

 ${var: -length}

取字符串的最右侧几个字符。注意:冒号后必须有一空白字符

截取右数N个#echo $num123456789#echo ${num: -3}789

 ${var:offset:-length}

从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容

去头N个,去尾N个#echo $num123456789#echo ${num:2:-2}34567

 ${var: -length:-offset}

先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容

注意:-length前空格

先从后截取N个,再从后再截取N个。注意前面的数字一定要比后面的大#echo $num123456789#echo ${num: -5:-2}567不同版本bash的差异CentOS release 6.9 (Final) bash, version 4.1.2(2)CentOS Linux release 7.4.1708 (Core) bash, version 4.2.46(2)Centos 7#echo $num123456789#echo ${num: -5:-2}567Centos 6#echo $num123456789#echo ${num: -5:-2}-bash: -2: substring expression < 0

字符串处理

基于模式取子串

 ${var#*word}

方向流 》

其中word可以是指定的任意字符

功能:自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符之间的所有字符.包括word也删除

#echo $num123456789#echo ${num#*5}6789找到的第一个字符串#echo $varroot:x:0:0:,,00000000:/root:/bin/bash#echo ${var#*root}:x:0:0:,,00000000:/root:/bin/bash------------------------------------------------#echo $urlhttps://www.baidu.com/?tn=98012088_5_dg&ch=12这个?号代表一个字符#echo ${url#*?}ttps://www.baidu.com/?tn=98012088_5_dg&ch=12转义之后的?号#echo ${url#*\?}tn=98012088_5_dg&ch=12

 ${var##*word}

贪婪模式 方向流 》

功能:自左而右,查找var变量所存储的字符串中,删除字符串开头至最后一次由word指定的字符之间的所有内容,包括word也删除

#echo $varroot:x:0:0:,,00000000:/root:/bin/bash#echo ${var##*root}:/bin/bash

 ${var%word*}

方向流 《

其中word可以是指定的任意字符;

功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符之间的所有字符;

#echo $urlhttps://www.baidu.com/?tn=98012088_5_dg&ch=12#echo ${url%\?*}https://www.baidu.com/

 ${var%%word*}

贪婪模式 方向流 《

功能:自右而左,查找var变量所存储的字符串中, 删除从最后一个字符向左至最后出现word字符之间的所有字符

#echo $varroot:x:0:0:,,00000000:/root:/bin/bash[root@centos7 service]#echo ${var%%/*}root:x:0:0:,,00000000:#echo $urlhttps://www.baidu.com/?tn=98012088_5_dg&ch=12#echo ${url%%?*}#echo ${url%%\?*}https://www.baidu.com/
#echo ${url2##:*}http://www.magedu.com:80#echo ${url2##*:}80#echo ${url2%%:*}http

查找替换

 ${var/pattern/substr}

查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之

#echo $varroot:x:0:0:,,00000000:/root:/bin/bash#echo ${var/root/hunk}hunk:x:0:0:,,00000000:/root:/bin/bash

 ${var//pattern/substr}

贪婪模式

查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之

#echo $varroot:x:0:0:,,00000000:/root:/bin/bash#echo ${var//root/hunk}hunk:x:0:0:,,00000000:/hunk:/bin/bash

 ${var/#pattern/substr}

查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之

这里的行首不要和正则表达式的^搞混哦#echo $url3http://ilovelinux.tech http://www.baidu.com#echo ${url3/#http/ftp}ftp://ilovelinux.tech http://www.baidu.com

 ${var/%pattern/substr}

查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之

这里的行首不要和正则表达式的$搞混哦,而且语法位置不一样echo $url3http://ilovelinux.tech http://www.baidu.com http#echo ${url3/%http/ftp}http://ilovelinux.tech http://www.baidu.com ftp

查找并删除

 ${var/pattern}

删除var所表示的字符串中第一次被pattern所匹配到的字符串

#echo $varroot:x:0:0:,,00000000:/root:/bin/bash#echo ${var/root}:x:0:0:,,00000000:/root:/bin/bash

 ${var//pattern}

贪婪模式

删除var所表示的字符串中所有被pattern所匹配到的字符串

#echo $varroot:x:0:0:,,00000000:/root:/bin/bash#echo ${var//root}:x:0:0:,,00000000:/:/bin/bash

 ${var/#pattern}

删除var所表示的字符串中所有以pattern为行首所匹配到的字符串

#echo $var2@@@@root@root:x:0:0:,,00000000:/root:/bin/bash$root$$$#echo ${var2/#@}@@@root@root:x:0:0:,,00000000:/root:/bin/bash$root$$$scr=/app/a/b/c/d/link.txtecho ${scr/#'/'}app/a/b/c/d/link.txt

 ${var/%pattern}

删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串

#echo $var2@@@@root@root:x:0:0:,,00000000:/root:/bin/bash$root$$$#echo ${var2/%$}@@@@root@root:x:0:0:,,00000000:/root:/bin/bash$root$$

字符大小写转换

 ${var^^}

把var中的所有小写字母转换为大写

#echo $varroot:x:0:0:,,00000000:/root:/bin/bash#echo ${var^^}ROOT:X:0:0:,,00000000:/ROOT:/BIN/BASH

 ${var,,}

把var中的所有大写字母转换为小写

#echo $varROOT:X:0:0:,,00000000:/ROOT:/BIN/BASH#echo ${var,,}root:x:0:0:,,00000000:/root:/bin/bash

变量赋值的特别

变量有3种状态,准确来说,是2种。

1.没有声明

2.有声明,值为空

3.有声明,有值

变量配置方式 str为未声明变量 str变量空值 str变量有值
var=${str-表达式} var=表达式 var= var=$str
var=${str:-表达式} var=表达式 var=表达式 var=$str
var=${str+表达式} var= var=表达式 var=表达式
var=${str:+表达式} var= str不变 var= var=表达式
var=${str=表达式} str=表达式 var=表达式 str不变 var= str不变 var=$str
var=${str:=表达式} str=表达式 var=表达式 str=表达式 var=表达式 str不变 var=$str
var=${str?表达式} 表达式输出至stderror var= var=$str
var=${str:?表达式} 表达式输出至stderror 表达式输出至stderror var=$str

举2个例说明下:

unset strvar=${str-"abc"}echo "var=$var"运行结果var=abcstr=var=${str-"abc"}echo "var=$var"运行结果var=unset strstr='str有值'var=${str-"abc"}echo "var=$var"运行结果var=str有值
#unset str#var=${str:?"abc"}#echo "var=$var"运行结果a.sh: line 3: str: abc#str=#var=${str:?"abc"}#echo "var=$var"运行结果a.sh: line 7: str: abcunset strstr='str有值'var=${str:?"abc"}echo "var=$var"运行结果var=str有值

高级变量用法-有类型变量

 Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两个命令是等价的。typeset 是属于将被淘汰的命令队列中。

declare [选项] 变量名-r 声明或显示只读变量-i 将变量定义为整型数-a 将变量定义为数组-A 将变量定义为关联数组-f 显示已定义的所有函数名及其内容-F 仅显示已定义的所有函数名-x 声明或显示环境变量和函数-l 声明变量为小写字母 declare –l var=UPPER    #declare -l var=AAA    #echo $var    aaa-u 声明变量为大写字母 declare –u var=lower    #declare -u var=aaa    #echo $var    AAA    如果变量一但声明为有类,那么赋值的时候就要特别注意了。    #unset var    #declare -i var    #var=10;echo $var    10    #var=abc;echo $?;echo $var    0   》这里返回的上条命令的结果是0    0   》 但是var的值却是0,因为这是一个int的变量,却存了字符类型

eval 命令

eval 命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量.该命

令对变量进行两次扫描

#n=10#echo {1..$n}{1..10}#eval echo {1..10}    第一次扫描时,会将 $n 置换成 10    第二次扫描时,会执行echo 命令结果如下:1 2 3 4 5 6 7 8 9 10

间接变量引用

如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用

 var1的值是var2,而var2又是变量名,var的值为value,间接变量引用是指通过var1获得变量值value的行为

bash Shell提供了两种格式实现间接变量引用第一种写法#who=user#user=hunk#eval echo \$$who    \是必须的,否则$$代表的是当前进程号    eval 第一次扫描时,置换为 eval echo $user    eval 第二次扫描时,执行echo $user结果hunk第二种写法#echo ${!who}hunk第三种写法:#who=$user#user=hunk#echo $who结果hunk

expect

expect 是由Don Libes基于Tcl( Tool Command Language)语言开发的,主要应用于自动化交互式操作的场景,借助

Expect处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对
多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率

expect 语法:expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]-c:从命令行执行expect脚本,默认expect是交互地执行的示例:expect -c 'expect "\n" {send "显示这里的字符"}'    #expect -c 'expect "\n" {send "显示这里的字符"}'    捕抓到回车    显示这里的字符-d:可以输出输出调试信息示例:expect -d ssh.expexpect中相关命令    spawn:启动新的进程    send:用于向进程发送字符串    expect:从进程接收字符串        一般特征 字符串往往是等待输入的最后的提示符的特征信息    interact:允许用户交互    exp_continue 匹配多个字符串在执行动作后加此命令

expect最常用的语法(tcl语言:模式-动作)

#!/usr/local/bin/expect -f 单一分支模式语法:    expect “hi” {send “You said hi\n"}    匹配到包含 hi 后,会输出“you said hi”,并换行 多分支模式语法:    expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n" }  "bye" { send "Good bye\n" }    匹配hi,hello,bye任意字符串时,执行相应输出。等同如下:expect {"hi" { send "You said hi\n"}"hehe" { send "Hehe yourself\n"}"bye" { send “Good bye\n"}}这不是不循环,倒累似于if 判断,判断完了就退出了

示例:

实现scp的自动登录并复制文件

#ssh 192.168.4.100The authenticity of host '192.168.4.100 (192.168.4.100)' can't be established.RSA key fingerprint is SHA256:FlJ8SyKInGHEFpnwuhdCooAgVxRtxJ4hNKO2Fzd34xA.RSA key fingerprint is MD5:4b:58:95:8f:82:d5:44:e9:d7:83:0b:43:3e:55:5e:98.Are you sure you want to continue connecting (yes/no)?以上是默认第一次远程连接的时候,需要用户输入 yes/no  。如果用expect来捕获,最好复制关键字符串,而不是自己键入,以保证准确率。#!/usr/bin/expect                                            这里的与bash脚本有差异spawn scp /tmp/b.log root@192.168.4.100:/tmp   启动一个新的进程,这个进程就是scpexpect {                                                          这里是代码块开始    "yes/no" {  send "yes\n";exp_continue }    捕获 "yes/no"关键字时,向进程scp发送"yes"并回车    "password" {  send "passwd1234\n" }       捕获 "password"关键字时,向进程scp发送"passwd1234"并回车}expect eof                                      这里是代码块的结束建议将脚本保存为exp结尾,以便与bash脚本区别执行结果#expect ssh1.exp spawn scp /tmp/b.log hunk@192.168.4.100:/tmpThe authenticity of host '192.168.4.100 (192.168.4.100)' can't be established.RSA key fingerprint is SHA256:FlJ8SyKInGHEFpnwuhdCooAgVxRtxJ4hNKO2Fzd34xA.RSA key fingerprint is MD5:4b:58:95:8f:82:d5:44:e9:d7:83:0b:43:3e:55:5e:98.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '192.168.4.100' (RSA) to the list of known hosts.hunk@192.168.4.100's password: b.log                                                                                            100%   39    41.3KB/s   00:00

使用变量

语法:set 变量名 值#!/usr/bin/expectset ip 192.168.4.100set user hunkset passwd passwd1234set timeout 10spawn ssh $user@$ipexpect {    "yes/no" {  send "yes\n";exp_continue }    "password" {  send "$passwd\n" }}interactexpect eof

位置参数

语法:set 变量名 [lindex $argv 数字]$argv 0 代表第一个位置参数,$argv 1 代表第二个... 注意别和bash的位置参数搞混了参数位置是相对于执行程序本身的位置,而不是相对变量#!/usr/bin/expectset ip [lindex $argv 0]set user [lindex $argv 1]set pass [lindex $argv 2]spawn ssh $user@$ipexpect {    "yes/no" { send "yes\n";exp_continue }    "password" { send "$pass\n"}}interact  这个指令是使用交换模式执行命令#expect ssh2.exp 192.168.4.100 hunk passwd1234spawn ssh hunk@192.168.4.100hunk@192.168.4.100's password: Last login: Sat Jan  6 15:39:46 2018 from 192.168.4.101
#!/usr/bin/expect#设置变量set ip 192.168.4.100set user hunkset pass passwd1234set timeout 10#启动新的ssh进程spawn ssh $user@$ip#ssh 自动验证expect {    "yes/no" { send "yes\n";exp_continue }    "password" { send "$pass\n"}}#登录后执行2条命令expect "HI" { send "echo 'hello'\n" }expect "llo" { send "date\n"}#发送exit并回车send "exit\n"expect eof执行结果#expect ssh3.expspawn ssh hunk@192.168.4.100hunk@192.168.4.100's password: Last login: Sat Jan  6 16:13:17 2018 from 192.168.4.101HI,hunk, Time is 2018-01-06#16:13:53echo 'hello'dateexit[hunk@centos6 ~]$echo 'hello'hello[hunk@centos6 ~]$dateSat Jan  6 16:13:53 CST 2018[hunk@centos6 ~]$exitlogoutConnection to 192.168.4.100 closed.

bash脚本中调用expect

关键点是使用多行重定向#!/bin/baship=$1user=$2pass=passwd1234#下面为使用多行重定向执行expect << EOFset timeout 10spawn ssh $user@$ipexpect {    "yes/no" { send "yes\n";exp_continue }    "password" { send "$pass\n"}}expect "HI" { send "echo 'hello'\n" }expect "llo" { send "date\n"}send "exit\n"expect eofEOF

实例

使用ftp自动上传文件

#!/bin/baship=192.168.4.100user=hunkpass=passwd1234expect  << EOFset timeout 10spawn ftp $ipexpect {    "Name" { send "$user\n";exp_continue }    "Password" { send "$pass\n"}}expect "ftp>" { send "put ftp.sh\n" }expect "ftp>" { send "ls\n" }expect "ftp>" { send "bye\n" }expect eofEOF

运行结果

#bash ftp.sh spawn ftp 192.168.4.100Connected to 192.168.4.100 (192.168.4.100).220 (vsFTPd 2.2.2)Name (192.168.4.100:root): hunk331 Please specify the password.Password:230 Login successful.Remote system type is UNIX.Using binary mode to transfer files.ftp> put ftp.shlocal: ftp.sh remote: ftp.sh227 Entering Passive Mode (192,168,4,100,106,68).150 Ok to send data.226 Transfer complete.294 bytes sent in 8.2e-05 secs (3585.37 Kbytes/sec)ftp> ls227 Entering Passive Mode (192,168,4,100,193,210).150 Here comes the directory listing.-rw-r--r--    1 500      500           294 Jan 06 09:45 ftp.sh226 Directory send OK.ftp> bye221 Goodbye.

生成相对路径的软链接:

#!/bin/bashpath='../'#判断参数        if [  $# -ne 2 ];then                        echo "必须2个参数,语法:`basename $0` 源文件绝对路径 软链接绝对路径" && exit 10                elif [ ! -e "$1" ];then                        echo "$1 不存在" && exit 20                elif [ ! -d `dirname $2` ];then                        echo "$2 有不存在的目录" && exit 30                elif [[ "$1" =~ ^[^/] ]];then                        echo "$1 不是绝对路径" && exit 40                elif [[ "$2" =~ ^[^/] ]];then                        echo "$2 不是绝对路径" && exit 50        else                dec=${2/%'/'}                [ -d "$dec" ] && echo "$dec 是目录,请换成文件名" && exit 60                #定义软链接绝对路径到根目录的层数                level=`dirname $2 | grep -o '/'|wc -l`                #定义去除源文件中首个/                src=${1/#'/'}                #定义去除软链接绝对路径中尾部的/                #定义生成相对路径                newscr=`for (( i=1; i<=$level;i++));do echo -n $path;done`                echo -e "生成的创建的相对路径软链接的命令为:                \033[1;33mln -s $newscr$src $dec \033[0m"        fi

运行结果

#bash 2create_symbolic.sh必须2个参数,语法:2create_symbolic.sh 源文件绝对路径 软链接绝对路径#bash create_symbolic.sh app/a/b/c/d/link.txt /home/hunk/test/app/a/b/c/d/link.txt 不存在#bash create_symbolic.sh /app/a/b/c/d/link.txt home/hunk/test/home/hunk/test/ 有不存在的目录#bash create_symbolic.sh /app/a/b/c/d/link.txt /home/hunk/test/aaaa生成的创建的相对路径软链接的命令为:        ln -s ../../../app/a/b/c/d/link.txt /home/hunk/test/aaaa #bash create_symbolic.sh /app/a/b/c/d/link.txt /home/hunk/test/aaaa/生成的创建的相对路径软链接的命令为:        ln -s ../../../app/a/b/c/d/link.txt /home/hunk/test/aaaa #bash create_symbolic.sh /app/a/b/c/d/ /home/hunk/test/aaaa/生成的创建的相对路径软链接的命令为:        ln -s ../../../app/a/b/c/d/ /home/hunk/test/aaaa 感觉太绕。不过是考虑到好些情况。

第五章有时间再写吧~~

本文转自 ljpwinxp 51CTO博客,原文链接:http://blog.51cto.com/191226139/2058238

转载地址:http://cghym.baihongyu.com/

你可能感兴趣的文章
Python在开发程序时提示错误提示“invalid syntax”是什么原因【已解决】
查看>>
人才需求报告
查看>>
[原创] 使ssh不用输入密码(转)
查看>>
PHP实现四种基本排序算法
查看>>
HTTP协议&SOCKET协议-摘抄
查看>>
Firewall cmd 命令
查看>>
2019 大数据学习入门必备规划
查看>>
java json 转为xml文件
查看>>
python中魔术方法简述
查看>>
Java实用手册
查看>>
GIF如何截取录制,怎么做GIF表情包
查看>>
技术的顶峰
查看>>
js动态添加事件-事件委托
查看>>
Python和人工智能到底啥关系?
查看>>
必读的Python入门书籍,你都看过吗?(内有福利)
查看>>
Java 科学计数法转成完整数字展示;double类型小数值不准确处理
查看>>
PHP代码补全(提示)中的两个 Atom 插件的对比
查看>>
C语言之流程控制
查看>>
ElasticSearch ik,elasticsearch-jdbc 使用 和 yii2 实例
查看>>
RPA视频教程丨UiBot—Function函数详解
查看>>