本文共 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 命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量.该命
令对变量进行两次扫描#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 是由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/