Shell是系统跟计算机之间交互时使用的中间介质,他只是系统的一个工具。实际上,在shell和计算机硬件之间还有一层东西——系统内核。如果把计算机硬件比作一个人的躯体,那系统内核就是人的大脑。至此shell,把他比作人的五官似乎更贴切些。言归正传,用户直接面对的不是计算机硬件而是shell,用户把指令告诉shell,然后shell在传输给系统内核,接着内核再去支配计算机硬件去执行各种操作。
! 是与命令历史有关的一个特殊字符。该字符常用的应用有以下3个。
!! :连续两个!!表示执行上一条指令。示例命令如下:
[root@localhost ~]#pwd /root [root@localhost ~]# !! pwd /root |
!n :这里的n是数字,表示执行命令历史中的第n条指令。例如 !1002表示执行命令历史中的第1002个命令,如下所示:
[root@localhost ~]#history |grep 1002 523 history |grep 1002 525 history |grep 1002 [root@localhost ~]# !200 a=`date +%w` 上例中的history命令如果未改动过环境变量,默认可以把最近执行的1000条命令历史打印出来。 |
! 字符串(字符串大于等于1):例如 !pw表示执行命令历史中最近一次以pw开头的命令。示例代码如下:
[root@localhost ~]# !pw pwd /root |
按tab键可以帮我们补全一个指令,一个路径或者一个文件名。连续按两次tab键,系统则会把所有的命令或者文件名都列出来
alias,它也是bash所特有的功能之一。我们可以通过alias把一个常用的并且很长的指令另取名为一个简单易记的指令。如果不想用了,还可以使用unalias命令解除别名功能。直接执行aliens命令,会看到目前预设的别名,如下所示:
[root@localhost ~]# alias alias cp='cp -i' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' alias grep='grep --color=auto' alias l.='ls -d .* --color=auto' alias ll='ls -l --color=auto' alias ls='ls --color=auto' alias mv='mv -i' alias rm='rm -i' alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde' 另外,也可以自定义命令的别名,其格式为alias [命令别名]=[‘具体的命令’], 示例命令如下: [root@localhost ~]# alias aming='pwd' [root@localhost ~]# aming /root [root@localhost ~]# unalias aming [root@localhost ~]# aming -bash: aming: 未找到命令 |
在bash下,可以使用*来匹配零个或多个字符,用?匹配一个字符。示例命令如下:
[root@localhost ~]# ls -d 123.* 123.x 123.y 123.z [root@localhost ~]# touch 123.xxx [root@localhost ~]# ls -d 123.? 123.x 123.y 123.z |
输入输出重定向用于改变命令的输入,输出重定向用于改变命令输出。输出重定向更为常用,它经常用于将命令的结果输入文件中,而不是屏幕上。输入重定向的命令是<,输出重定向的命令是>,另外,还有错误重定向命令2>以及追加重定向命令>>,示例命令如下:
[root@localhost ~]# mkdir /tmp/10 [root@localhost ~]# cd /tmp/10 [root@localhost 10]# echo "123" > 1.txt [root@localhost 10]# echo "123" >> 1.txt [root@localhost 10]# cat 1.txt 123 123 |
它用于将前一个指令的输出作为后一个指令的输入,如下所示:
[root@localhost 10]# cat /etc/passwd|wc -l 19 |
比如环境变量PATH,它是shell预设的一个变量。通常,shell预设的变量都是大写的。变量就是使用一个较简单的字符串来代替某些具有特殊意义的设定以及数据。就拿PATH来说,这个PATH就代替了所有常用命令的绝对路径的设定。有了PATH这个变量,我们运行某个命令时,就不在需要输入全局路径,直接输入命令名即可。你可以使用echo命令显示变量的值,如下所示:
[root@localhost 10]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin [root@localhost 10]# echo $HOME /root [root@localhost 10]# echo $PWD /tmp/10 [root@localhost 10]# echo $LOGNAME root |
使用env命令,可列出系统预设的全部系统变量,如下所示:
[root@localhost ~]# env XDG_SESSION_ID=19 HOSTNAME=localhost.localdomain SELINUX_ROLE_REQUESTED= TERM=vt100 SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=192.168.213.1 52011 22 SELINUX_USE_CURRENT_RANGE= OLDPWD=/tmp/10 SSH_TTY=/dev/pts/0 USER=root LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36: MAIL=/var/spool/mail/root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin PWD=/root LANG=zh_CN.UTF-8 SELINUX_LEVEL_REQUESTED= HISTCONTROL=ignoredups SHLVL=1 HOME=/root LOGNAME=root SSH_CONNECTION=192.168.213.1 52011 192.168.213.101 22 LESSOPEN=||/usr/bin/lesspipe.sh %s XDG_RUNTIME_DIR=/run/user/0 _=/usr/bin/env |
登录不同的用户,这些环境变量的值也不同。当前显示的是root账户的环境变量。下面是常见的环境变量。
env命令显示的变量只是环境变量,系统预设的变量其实还有很多,可以使用set命令把系统预设的全部变量都显示出来。
Set命令喝env命令类似,也可以输出环境变量,如下所示:
[root@localhost ~]# set BASH=/bin/bash BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath BASH_ALIASES=() BASH_ARGC=() BASH_ARGV=() BASH_CMDS=() BASH_LINENO=() BASH_SOURCE=() BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu") BASH_VERSION='4.2.46(2)-release' COLUMNS=80 DIRSTACK=() EUID=0 GROUPS=() HISTCONTROL=ignoredups HISTFILE=/root/.bash_history HISTFILESIZE=1000 HISTSIZE=1000 HOME=/root HOSTNAME=localhost.localdomain HOSTTYPE=x86_64 IFS=$' \t\n' LANG=zh_CN.UTF-8 LESSOPEN='||/usr/bin/lesspipe.sh %s' LINES=23 LOGNAME=root LS_COLORS='rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:' MACHTYPE=x86_64-redhat-linux-gnu MAIL=/var/spool/mail/root MAILCHECK=60 OLDPWD=/tmp/10 OPTERR=1 OPTIND=1 OSTYPE=linux-gnu PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin PIPESTATUS=([0]="127") PPID=40069 PS1='[\u@\h \W]\$ ' PS2='> ' PS4='+ ' PWD=/root SELINUX_LEVEL_REQUESTED= SELINUX_ROLE_REQUESTED= SELINUX_USE_CURRENT_RANGE= SHELL=/bin/bash SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor SHLVL=1 SSH_CLIENT='192.168.213.1 52011 22' SSH_CONNECTION='192.168.213.1 52011 192.168.213.101 22' SSH_TTY=/dev/pts/0 TERM=vt100 UID=0 USER=root XDG_RUNTIME_DIR=/run/user/0 XDG_SESSION_ID=19 _=aming a=4 colors=/root/.dircolors |
Set命令不仅可以显示系统预设的变量,也可以显示用户自定义的变量。比如我们自定义一个变量,如下所示:
[root@localhost ~]# myname=Aming [root@localhost ~]# echo $myname Aming [root@localhost ~]# set |grep myname myname=Aming 虽然可以自定义变量,但是该变量只能在当前shell中生效,如下所示 [root@localhost ~]# echo $myname Aming [root@localhost ~]# bash //执行该命令,会进入一个子shell环境中 [root@localhost ~]# echo $myname [root@localhost ~]# exit exit [root@localhost ~]# echo $myname Aming |
使用bash命令可以在打开一个shell,此时先前设置的myname变量已经不存在了,退出当前shell回到原来的shell,myname变量还在。如果想让设置的环境变量一直生效,该怎么做呢? 这分为两种情况。
[root@localhost ~]# echo "export myname=Aming" >> /etc/profile [root@localhost ~]# source !$ source /etc/profile [root@localhost ~]# bash [root@localhost ~]# echo $myname Aming [root@localhost ~]# exit Exit [root@localhost ~]# useradd test [root@localhost ~]# su - test [test@localhost ~]$ echo $myname Aming |
上例中使用myname-Aming来设置变量myname,那么,在linux下自定义变量,有哪些规则
[root@localhost ~]# myname='Aming li' [root@localhost ~]# echo $myname Aming li |
有一种情况需要你注意,就是变量内容中本身带有单引号,这时就需要加双引号了。示例命令如下:
[root@localhost ~]# myname="Aming'li" [root@localhost ~]# echo $myname Aming'li |
如果变量内容中需要用到其他命令,运行结果则可以用反引号。示例命令如下
[root@localhost ~]# myname=`pwd` [root@localhost ~]# echo $myname /root |
变量内容可以累加其他变量内容,但需要加双引号。示例命令如下
[root@localhost ~]# myname="$LOGNAME"Aming [root@localhost ~]# echo $myname rootAming |
如果不下心把双引号错加为单引号,则得不到你想要的结果。示例命令如下
[root@localhost ~]# myname='$LOGNAME'Aming [root@localhost ~]# echo $myname $LOGNAMEAming |
通过上面几个例子,能看出来使用单引号和双引号的区别。使用双引号时,不会取消双引号中特殊字符本身的作用(这里是$),而使用单引号时,里面的特殊字符将全部失去其本身的作用。
上面讲了很多系统变量,那么在linux系统中,这些变量存在哪里呢?为什么用户一登录shell就自动有了这些变量呢?先看下面几个文件。
[root@localhost ~]# echo $PS1 [\u@\h \W]\$ |
前面提过此处
[root@localhost ~]# ls -d 123.* 123.x 123.y 123.z [root@localhost ~]# touch 123.xxx [root@localhost ~]# ls -d 123.? 123.x 123.y 123.z |
这个符号在linux中表示注释说明,即后面的内容都会被忽略。用法如下:
[root@localhost ~]# abc=123 #aaaaa [root@localhost ~]# echo $abc 123 |
这个字符会将后面的特殊符号(如*)还原为普通字符。用法如下:
[root@localhost ~]# ls -d test\* |
这个字符前面曾多次出现过,它的作用是将前面命令的输出作为后面命令的输入。这里提到的后面的命令,并不是所有的命令都可以的,一般针对文档操作的命令比较常用。例如cat、less、head、tail、grep、cut、sort、wc、uniq、tee、tr、split、sed、awk等,其中grep、sed和awk是正则表达式必须掌握的工具。管道符的用法如下:
[root@localhost ~]# cat testb.txt |wc -l 0 |
在上例中,wc -l用来计算一个文档有多少行。
cut命令用来截取某一个字段,其格式为cut -d ‘分割字符’ [-cf] n,这里的n是数字。该命令有如下几个可用选项。
cut命令的用法如下:
[root@localhost ~]# cat /etc/passwd|cut -d ':' -f 1 |head -5 root bin daemon adm lp |
-d选项后面加冒号作为分隔字符,-f 1表示截取第一段,-f和1之间的空格可与可无。
示例命令如下:
[root@localhost ~]# head -n2 /etc/passwd|cut -c2 o i [root@localhost ~]# head -n2 /etc/passwd|cut -c1 r b [root@localhost ~]# head -n2 /etc/passwd|cut -c1-10 root:x:0:0 bin:x:1:1: [root@localhost ~]# head -n2 /etc/passwd|cut -c5-10 :x:0:0 x:1:1: |
-c选项后面可以是1个数字n,也可以是一个区间n1-n2,还可以是多个数字n1、n2和n3.
示例命令如下:
[root@localhost ~]# head -n2 /etc/passwd|cut -c1,3,10 ro0 bn: |
sort命令用作排序,其格式为sort [ -t 分隔符 ] [-kn,n2] [-nru]、这里的n1和n2表示的是数字,其他选项的含义如下。
如果sort不加任何选项,则从首字母符向后依次按ASCII码值进行比较,最后将他们按升序输出。示例命令如下:
[root@localhost ~]# head -n5 /etc/passwd|sort adm:x:3:4:adm:/var/adm:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin root:x:0:0:root:/root:/bin/bash |
-t选项后面跟分隔符,-k选项后面跟单个数字表示对第几个区域的字符串排序,-n选项表示使用纯数字排序。示例命令如下:
[root@localhost ~]# head -n5 /etc/passwd|sort -t: -k3 -n root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin |
-k选项后面跟数字n1和n2表示对第n1和n2区域内的字符串排序, -r选项则表示反向排序。示例命令如下:
[root@localhost ~]# head -n5 /etc/passwd|sort -t: -k3 -r lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin root:x:0:0:root:/root:/bin/bash |
这里的-k3,5表示对第3区域至第五区域间的字符串排序。
wc命令统计文档的行数、字符数或词数。该命令的常用选项-l(统计行数)、-m(统计字符数)和-w(统计词数)。示例命令如下:
[root@localhost ~]# wc /etc/passwd 20 28 885 /etc/passwd [root@localhost ~]# wc -l /etc/passwd 20 /etc/passwd [root@localhost ~]# wc -m /etc/passwd 885 /etc/passwd [root@localhost ~]# wc -w /etc/passwd 28 /etc/passwd |
如果wc不跟任何选项,直接跟文档,则会把行数、词数和字符数依次输出。
uniq命令用来删除重复的行,该命令只有-c选项比较常用,它表示统计重复的行数,并把行数写在前面。我们先来编写一个文件,示例命令如下:
[root@localhost ~]# cat testb.txt 111 222 111 333 使用uniq前,必须先给文件排序,否则不管用。示例命令如下: [root@localhost ~]# uniq testb.txt 111 222 111 333 [root@localhost ~]# sort testb.txt |uniq 111 222 333 [root@localhost ~]# sort testb.txt |uniq -c 2 111 1 222 1 333 |
Tee命令后跟文件名,其作用类似于重定向>,但它比重定向多一个功能,即把文件写入后谜案所跟的文件时,还显示在屏幕上。该命令用于管道符|后。示例命令如下:
[root@localhost ~]# echo "aaaaaaaaaaaaaaa" |tee testb.txt aaaaaaaaaaaaaaa [root@localhost ~]# cat testb.txt aaaaaaaaaaaaaaa |
tr命令用于替换字符,常用来处理文档中出现的特殊符号,如DOS文档中出现的符^M。该命令常用的选项有以下两个。
tr命令常用于把小写字母变成大写字母,如tr ‘[a-z]’ ‘ [A-Z]’。示例命令如下:
[root@localhost ~]# head -n2 /etc/passwd |tr '[a-z]' '[A-Z]' ROOT:X:0:0:ROOT:/ROOT:/BIN/BASH BIN:X:1:1:BIN:/BIN:/SBIN/NOLOGIN |
tr命令还可以替换一个字符,示例命令如下:
[root@localhost ~]# grep 'root' /etc/passwd |tr 'r' 'g' goot:x:0:0:goot:/goot:/bin/bash opegatog:x:11:0:opegatog:/goot:/sbin/nologin |
不过替换、删除以及去重复等操作都是针对一个字符来讲的,有一定的局限性。如果是针对一个字符串,就不能再使用了,所以先只需要简单了解一下tr命令即可
split命令用于切割文档,常用的选项为-b和-l。
[root@localhost ~]# mkdir split_dir [root@localhost ~]# cd !$ cd split_dir [root@localhost split_dir]# cp /etc/passwd ./ [root@localhost split_dir]# split -b 500 passwd [root@localhost split_dir]# ls passwd xaa xab |
如果split不指定目标文件名,则会以xaa、下、xbb...这样的文件名来存取切割后的文件。当然,我们也可以指定目标文件名,如下所示:
[root@localhost split_dir]# rm -f xa* [root@localhost split_dir]# split -b 500 passwd 123 [root@localhost split_dir]# ls 123aa 123ab passwd |
-l:表示依据行数来切割文档,示例命令如下:
[root@localhost split_dir]# rm -f 123a* [root@localhost split_dir]# split -l 10 passwd [root@localhost split_dir]# wc -l * 20 passwd 10 xaa 10 xab 40 总用量 |
符号$可以用作变量前面的标识符,还可以和!结合起来使用。示例命令如下:
[root@localhost split_dir]# cd .. [root@localhost ~]# ls testb.txt testb.txt [root@localhost ~]# ls !$ ls testb.txt testb.txt |
!$表示上条命令中的最后一个变量,本例中上条命令最后是testb.txt,那么在当前命令下输入!$则代表testb.txt。
通常,我们都是在一行中输入一个命令,然后回车就运行了。如果想在一行中运行两个或两个以上的命令,需要在命令之间加符号;。示例命令如下:
[root@localhost ~]# mkdir testdir ; touch test1.txt ; touch test2.txt; ls -d test* test1.txt test2.txt testb.txt testdir |
示例命令如下:
[root@localhost ~]# cd ~ [root@localhost ~]# pwd /root [root@localhost ~]# su aming [aming@localhost root]$ cd ~ [aming@localhost ~]$ pwd /home/aming [aming@localhost ~]$ |
如果想把一条命令放在后台之执行,则需要加上符号&,它通常用于命令运行时间较长的情况。比如,可以用在sleep后,如下所示:
[root@localhost ~]# sleep 30 & [1] 40772 [root@localhost ~]# jobs [1]+ 运行中 sleep 30 & |
前面讲过重定向符号>和>>,它们分别表示取代和追加的意思。当我们运行一个命令报错时,报错信息会输出到当前屏幕。如果想重定向到一个文本,则要用重定向符号2>>或者2>>,它们分别表示错误重定向和错误追加重定向。示例命令如下:
[root@localhost ~]# ls aaaa [root@localhost ~]# ls aaaa 2> /tmp/error [root@localhost ~]# cat /tmp/error [root@localhost ~]# ls aaaa 2>> /tmp/error [root@localhost ~]# cat /tmp/error |
中括号内为字符组合,代表字符组合中的任意一个,可以是一个范围(1-3,a-z),用法如下:
[root@localhost 10]# cd [root@localhost ~]# cd /tmp/10 [root@localhost 10]# ls -d test* test1.txt test2.txt test3.txt testdir [root@localhost 10]# ls -d test[1-3].txt test1.txt test2.txt test3.txt [root@localhost 10]# ls -d test[12b].txt test1.txt test2.txt [root@localhost 10]# ls -d test[1-9].txt test1.txt test2.txt test3.txt [root@localhost 10]# ls -d test[1-9a-z].txt test1.txt test2.txt test3.txt |
前面提到了分号可作为多条命令间的分隔符,其实还有两个可以用与多条命令中间的特殊符号,那就是&&和||。
列出以下几种情况:
使用;时,不管command1是否执行成功,都会执行command2.
使用&&时,只有command1执行成功后,command2才会执行,否则command2不执行
使用||时,command执行成功后则command2才会执行,否则执行command2,即command1和command2中总有一条命令会执行。接下来,做个测试
[root@localhost 10]# rm -rf test* [root@localhost 10]# touch test1 test3 [root@localhost 10]# ls test2 && touch test2 [root@localhost 10]# ls test2 |
本例中,只有当ls test2执行成功后,才会执行touch test2.因为test2不存在,ls test2没有执行成功,所以&&后面的touch并没有执行。
[root@localhost 10]# ls test2 || touch test2 [root@localhost 10]# ls test* test1 test2 test3 |
本例中,若ls test2执行不成功,则会执行touch test2。因为test2不存在,所以ls test2没有执行成功,转而执行||后面的touch test2,所以增加了test2这个文件。
在计算机科学中,对“正则表达式”的定义是:它使用单个字符串来描述或匹配一系列符合某个句法规则的字符串。在很多文本编辑器或其他工具里,正则表达式通常用来检索和替换那些符合某个模式的文本内容。许多程序设计语言也都支持利用正则表达式进行字符串操作。对于系统管理员来讲,正则表达式贯穿在我们的日常运维工作中,无论是查找某个文档,还是查询某个日志文件并分析其内容,都会用到正则表达式。
其实正则表达式只是一种思想、一种表示方法。只要我们使用的工具支持这种表示方法,那么这个工具就可以处理正则表达式的字符串。常用的工具有grep、sed、awk等,其中grep、sed和awk都是针对文本的行进行操作的,下面分别介绍一下这3种工具的使用方法。
该命令的格式为:grep [-cinvABC] ‘word’ filename,其常用的选项如下所示。
先来测试 -A 、-B和 -C这三个选项的用法。
-A2会把包含halt的行以及这行下面的两行都打印出来:
[root@localhost ~]# grep -A2 'halt' /etc/passwd halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin |
-B2会把包含halt的行以及这行上面的两行都打印出来:
[root@localhost ~]# grep -B2 'halt' /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt |
-C 会把包含halt的行以及这行上下各两行都打印出来。
[root@localhost ~]# grep -C2 'halt' /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin |
示例命令如下:
[root@localhost ~]# grep -n 'root' /etc/passwd 1:root:x:0:0:root:/root:/bin/bash 10:operator:x:11:0:operator:/root:/sbin/nologin |
示例命令如下:
[root@localhost ~]# grep -nv 'nologin' /etc/passwd 1:root:x:0:0:root:/root:/bin/bash 6:sync:x:5:0:sync:/sbin:/bin/sync 7:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 8:halt:x:7:0:halt:/sbin:/sbin/halt 20:test:x:1000:1000::/home/test:/bin/bash 21:aming:x:1001:1001::/home/aming:/bin/bash |
示例命令如下:
[root@localhost ~]# grep '[0-9]' /etc/inittab # multi-user.target: analogous to runlevel 3 # graphical.target: analogous to runlevel 5 |
示例命令如下:
[root@localhost ~]# grep -v '[0-9]' /etc/inittab # inittab is no longer used when using systemd. # # ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM. # # Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target # # systemd uses 'targets' instead of runlevels. By default, there are two main targets: # # # To view current default target, run: # systemctl get-default # # To set a default target, run: # systemctl set-default TARGET.target # |
示例命令如下:
[root@localhost ~]# cat /etc/sos.conf [plugins] #disable -rpm. selinux, dovecot [tunables] #rpm.rpmva - off #general.syslogsize - 15 #grep -v '^#' /etc/sos.conf [plugins] [tunables] |
[root@localhost ~]# grep -v '^#' /etc/sos.conf |grep -v '^$' [plugins] [tunables] [plugins] [tunables] |
在正则表达式中,^表示行的开始,$表示行的结尾,那么空行则可以用^$表示。如何打印出不以英文字母开头的行呢?我们先来自定义一个文件,如下所示
[root@localhost ~]# mkdir /tmp/1 [root@localhost ~]# cd /tmp/1 [root@localhost 1]# vi test.txt [root@localhost 1]# cat test.txt 123 abc 456 abc2323 #laksdjf Alllllll |
先写几行字符串,用来做实验 如下所示
[root@localhost 1]# grep '^[^a-zA-Z]' test.txt 123 456 #laksdjf [root@localhost 1]# grep '[^a-zA-Z]' test.txt 123 456 abc2323 #laksdjf |
前面提到过中括号[]的应用,如果时数字就用[0-9]这样的形式(当遇到类似[15]的形式时,表示只含有 1或者5).如果要过滤数字以及大小写字母,则要写成类似[0-9a-zA-Z]的形式。另外,[^字符]表示除了[]内字符之外的字符。请注意,把^写到方括号里面和外面是有区别的。
示例命令如下:
[root@localhost 1]# grep 'r.o' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin |
.表示任意一个字符。上例中,r.o表示把r与o之间有一个任意字符的行过滤出来。
[root@localhost 1]# grep 'ooo*' /etc/passwd root:x:0:0:root:/root:/bin/bash lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin |
*表示零个或多个*前面的字符。上例中,ooo*表示oo,ooo,oooo或者更多的o。
[root@localhost 1]# grep '.*' /etc/passwd |wc -l 21 [root@localhost 1]# wc -l /etc/passwd 21 /etc/passwd |
上例中,.*表示零个或多个任意字符,空行也包含在内,它会把/etc/passwd文件里面的所有行都匹配到,也可以不加|wc -l 看一下效果
[root@localhost 1]# grep '.*' /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin test:x:1000:1000::/home/test:/bin/bash aming:x:1001:1001::/home/aming:/bin/bash |
示例命令如下:
[root@localhost 1]# grep 'o\{2\}' /etc/passwd root:x:0:0:root:/root:/bin/bash lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin |
这里用到了符号{},其内部为数字,表示前面的字符要重复的次数。需要强调的是, {}左右都需要加上转义字符\。另外,使用 “{ }”还可以表示一个范围,具体格式为{n1,n2},其中n1<n2,表示重复n1到n2次前面的字符,n2还可以为空,这时表示大于等于n1次。
除grep工具外,也常常用到egrep这个工具,后者是前者的扩展版本,可以完成grep不能完成的工作。下面说几个egreep不同于grep的几个用法。为了后续试验方便把test.txt编辑成如下文件
[root@localhost 1]# cat test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 |
示例命令如下:
[root@localhost 1]# egrep 'o+' test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash [root@localhost 1]# egrep 'oo+' test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash [root@localhost 1]# egrep 'ooo+' test.txt roooot:x:0:0:/rooooot:/bin/bash |
和egrep不同,这里egrep使用的是符号+,它表示匹配1个或多个+前面的字符,这个 “+”
是不是支持被grep直接使用的。包括上面的{}。也是可以直接被egrep使用,而不用家\转义。示例如下:
[root@localhost 1]# egrep 'o{2}' /etc/passwd root:x:0:0:root:/root:/bin/bash lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin |
示例命令如下:
[root@localhost 1]# egrep 'o?' test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 [root@localhost 1]# egrep 'ooo?' test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash [root@localhost 1]# egrep 'oooo?' test.txt roooot:x:0:0:/rooooot:/bin/bash |
示例命令如下:
[root@localhost 1]# egrep 'aaa|111|ooo' test.txt roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 |
示例命令如下
[root@localhost 1]# egrep 'r(oo|at)o' test.txt operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash |
这里的()表示一个整体,上例中会把包含rooo或者rato的行过滤出来,另外也可以把()和其他符号组合在一起,例如(oo)+就表示1个或者多个oo。如下所示
[root@localhost 1]# egrep '(oo)+' test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash |
其实grep工具的功能还不够强大,它实现的只是查找功能,二不能把查找的内容替换。以前用vi操作文档的时候,可以查找也可以替换,但是限于在文本内部操作,二不能输出到屏幕上。sed工具以及后面要介绍的awk工具就能把替换的文本输出到屏幕上,而且还有其他更丰富的功能。sed和awk都是流式编辑器,是针对文档紧凑型来操作的
Sed命令的格式为: sed -n ‘n’p filename, 单引号内的n是一个数字,表示第几行。-n选项的作用是只显示我们要打印的行,无关紧要的内容不显示。
示例命令如下
[root@localhost 1]# sed -n '2'p /etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin |
可以去掉-n选项对比一下差异。要想把所有行都打印出来,可以使用命令sed -n ‘1,$’ p filename 如下所示:
[root@localhost 1]# sed -n '1,$'p test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 |
当然也可以指定一个区间打印,如下所示:
[root@localhost 1]# sed -n '1,3'p test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin |
示例命令如下
[root@localhost 1]# sed -n '/root/'p test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin |
这种用法就类似于grep了,在grep中使用的特殊字符(如^、 $、.、*等)同样也能在sed中使用,如下所示:
[root@localhost 1]# sed -n '/^1/'p test.txt 1111111111111111111111111111111 [root@localhost 1]# sed -n '/in$/'p test.txt operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin [root@localhost 1]# sed -n '/r..o/'p test.txt operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash [root@localhost 1]# sed -n '/ooo*/'p test.txt rot:x:0:0:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash |
sed命令加上-e选项可以实现多个行为,如下所示:
[root@localhost 1]# sed -e '1'p -e '/111/'p -n test.txt rot:x:0:0:/root:/bin/bash 1111111111111111111111111111111 |
示例命令如下:
[root@localhost 1]# sed '1'd test.txt operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 [root@localhost 1]# sed '1,3'd test.txt roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 [root@localhost 1]# sed '/oot/'d test.txt 1111111111111111111111111111111 |
这里参数d表示删除的动作,它不仅可以删除指定的单行以及多行,而且可以删除指定的单行及多行,而且可以删除匹配某个字符的行,还可以删除从某一行开始到文档最后一行的所有行。不过,这个操作仅仅实在显示屏幕上并不是显示这些行而已,文档还好好的
示例命令让如下:
[root@localhost 1]# sed '1,2s/ot/to/g' test.txt rto:x:0:0:/roto:/bin/bash operator:x:11:0:operator:/roto:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 |
上例参数s就表示替换的动作,参数g表示本行全局替换,如果不加g则只替换本行出现的第一个,这个用法其实和vi的替换大同小异。
除了可以使用/作为分隔符外,我们还可以使用其他特殊字符,例如#和@。如下所示:
[root@localhost 1]# sed 's#ot#to#g' test.txt rto:x:0:0:/roto:/bin/bash operator:x:11:0:operator:/roto:/sbin/nologin operator:x:11:0:operator:/rototo:/sbin/nologin roooto:x:0:0:/rooooto:/bin/bash 1111111111111111111111111111111 [root@localhost 1]# sed 's@ot@to@g' test.txt rto:x:0:0:/roto:/bin/bash operator:x:11:0:operator:/roto:/sbin/nologin operator:x:11:0:operator:/rototo:/sbin/nologin roooto:x:0:0:/rooooto:/bin/bash 1111111111111111111111111111111 |
现在思考一下:如何删除文档中所有的数字或字母? 示例命令如下:
[root@localhost 1]# sed 's/[0-9]//g' test.txt rot:x:::/root:/bin/bash operator:x:::operator:/root:/sbin/nologin operator:x:::operator:/rootot:/sbin/nologin roooot:x:::/rooooot:/bin/bash |
[0-9]表示任意的数字。这里你也可以写成[a-zA-ZA]或者[0-9a-zA-Z].如下所示:
[root@localhost 1]# sed 's/[a-zA-Z]//g' test.txt ::0:0:/:// ::11:0::/:// ::11:0::/:// ::0:0:/:// 1111111111111111111111111111111 |
[root@localhost 1]# sed 's/\(rot\)\(.*\)\(bash\)/\3\2\1/' test.txt bash:x:0:0:/root:/bin/rot operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 |
小括号在sed中属于特殊符号,必须在前面家转义字符\,替换时则携程类似\1、\2、或\3的形式。上例中用()把想要替换的字符打包成了一个整体。有这个转义字符\,会让这个表达式看起来乱糟糟的有个办法可以省略它。如下所示;
[root@localhost 1]# sed -r 's/(rot)(.*)(bash)/\3\2\1/' test.txt bash:x:0:0:/root:/bin/rot operator:x:11:0:operator:/root:/sbin/nologin operator:x:11:0:operator:/rootot:/sbin/nologin roooot:x:0:0:/rooooot:/bin/bash 1111111111111111111111111111111 |
没错,正如你看到的,就是这个-r选项让这个表达式更加清晰了。除了调换两个字符串的位置,还常常用sed在某一行前后增加指定内容,如下所示:
[root@localhost 1]# sed 's/^.*$/123&/' test.txt 123rot:x:0:0:/root:/bin/bash 123operator:x:11:0:operator:/root:/sbin/nologin 123operator:x:11:0:operator:/rootot:/sbin/nologin 123roooot:x:0:0:/rooooot:/bin/bash 1231111111111111111111111111111111 |
示例命令如下:
[root@localhost 1]# sed -i 's/ot/to/g' test.txt [root@localhost 1]# cat test.txt rto:x:0:0:/roto:/bin/bash operator:x:11:0:operator:/roto:/sbin/nologin operator:x:11:0:operator:/rototo:/sbin/nologin roooto:x:0:0:/rooooto:/bin/bash 1111111111111111111111111111111 |
这样就可以直接更改test.txt文件中的内容了。但必须注意,在修改前最好先备份一下文件,以免改错。
awk也是流式编辑器,针对文档中的行来操作,一行一行地执行。awk兼具sed的所有功能,而且更加强大。
示例命令如下:
[root@localhost 1]# head -n2 test.txt |awk -F ':' '{print $1}' rto operator |
本例中,-F选项的作用是指定分隔符。如果不加-F选项,则以空格或者tab为分隔符。Print为打印动作,用来打印某个字段。$1为第一个字段。$2为第2个字段,以此类推。但$0比较特殊,它表示整行:
[root@localhost 1]# head -n2 test.txt |awk -F':' '{print $0}' rto:x:0:0:/roto:/bin/bash operator:x:11:0:operator:/roto:/sbin/nologin |
注意awk的格式,-F后面紧跟单引号,单引号里面为分隔符。Print的动作要用{}括起来,否则会报错。print还可以打印自定义的内容,但是自定义的内容要用双引号括起来。如下所示:
[root@localhost 1]# head -n2 test.txt |awk -F':' '{print $1"#"$2"#"$3"#"$4}' rto#x#0#0 operator#x#11#0 |
示例命令如下:
[root@localhost 1]# awk '/oo/' test.txt roooto:x:0:0:/rooooto:/bin/bash |
示例命令如下:
[root@localhost 1]# awk -F ':' '$3=="0"' /etc/passwd root:x:0:0:root:/root:/bin/bash |
awk中可以用逻辑符号进行判断,比如==就是等于,也可以理解为精确匹配。另外还有>、>=、<、<=、!=等。值得注意的是,在和数字比较时,若把比较的数字用双引号引起来,那么awk不会认为是数字,而会认为是字符,不加双引号则会认为是数字。
[root@localhost 1]# awk -F ':' '$3>"500"' /etc/passwd shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin |
本例中,本想把uid大于等于500的行打印出来,但是结果并不理想。这是因为awk把所有的数字当作字符了,就跟上一章中提到的sort排序原理一样。但,不加双引号就得到了想要的结果:
[root@localhost 1]# awk -F ':' '$3>500' /etc/passwd polkitd:x:999:998:User for polkitd:/:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin test:x:1000:1000::/home/test:/bin/bash aming:x:1001:1001::/home/aming:/bin/bash |
本例中,!=表示不匹配,它除了针对某一个字段的字符进行逻辑比较外,还可以在两个段之间进行逻辑比较。如下所示:
[root@localhost 1]# awk -F ':' '$3<$4' /etc/passwd adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin |
另外还可以使用&&和||,他们分别表示“并且”和“或者”。&&的用法如下:
[root@localhost 1]# awk -F ':' '$3>"5" && $3<"7"' /etc/passwd shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown |
||的用法如下:
[root@localhost 1]# awk -F ':' '$3>1000 || $7=="/bin/bash"' /etc/passwd root:x:0:0:root:/root:/bin/bash test:x:1000:1000::/home/test:/bin/bash aming:x:1001:1001::/home/aming:/bin/bash |
awk常用的变量有OFS、NF和NR,OFS和-F选项有类似的功能,也是用来定义分割符的,但是它是在输出的时候定义,NF表示用分隔符分隔后一共有多少段,NR表示行号。
OFS的用法示例如下:
[root@localhost 1]# head -5 /etc/passwd |awk -F ':' '{OFS="#"} {print $1,$3,$4}' root#0#0 bin#1#1 daemon#2#2 adm#3#4 lp#4#7 |
还有更高级的用法:
[root@localhost 1]# awk -F ':' '{OFS="#"} {if ($3>1000) {print $1,$2,$3,$4}}' /etc/passwd aming#x#1001#1001 |
变量NF的具体用法如下:
[root@localhost 1]# head -n3 /etc/passwd | awk -F ':' '{print NF}' 7 7 7 [root@localhost 1]# head -n3 /etc/passwd | awk -F ':' '{print $NF}' /bin/bash /sbin/nologin /sbin/nologin |
这里NF是多少段,$NF是最后一段的值。变量NR的具体用法如下:
[root@localhost 1]# head -n3 /etc/passwd | awk -F ':' '{print NR}' 1 2 3 |
我们还可以使用NR作为判断条件,如下所示:
[root@localhost 1]# awk 'NR>15' /etc/passwd polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin test:x:1000:1000::/home/test:/bin/bash aming:x:1001:1001::/home/aming:/bin/bash |
NR也可以配合段匹配一起使用,如下所示:
[root@localhost 1]# awk -F ':' 'NR<20 && $1 ~ /root/' /etc/passwd root:x:0:0:root:/root:/bin/bash |
awk可以更改段值,示例命令如下:
[root@localhost 1]# head -n 3 /etc/passwd |awk -F ':' '$1="root"' root x 0 0 root /root /bin/bash root x 1 1 bin /bin /sbin/nologin root x 2 2 daemon /sbin /sbin/nologin |
awk也可以对各个段的值进行数学运算,示例命令如下:
[root@localhost 1]# head -n2 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin [root@localhost 1]# head -n2 /etc/passwd |awk -F ':' '{$7=$3+$4}' [root@localhost 1]# head -n2 /etc/passwd |awk -F ':' '{$7=$3+$4; print $0}' root x 0 0 root /root 0 bin x 1 1 bin /bin 2 |
awk还可以计算某个段的总和,示例命令如下:
[root@localhost 1]# awk -F ':' '{(tot=tot+$3)}; END {print tot}' /etc/passwd 4606 |
这里的END是awk特有的语法,表示所有的行都已经执行。其实awk连同sed都可以写成一个脚本文件,而且有它们特有的语法。在awk中使用if判断、for循环都可以,只是日常生活中没有必要使用那么复杂的语句。如下所示:
[root@localhost 1]# awk -F ':' '{if ($1=="root") {print $0}}' /etc/passwd root:x:0:0:root:/root:/bin/bash |
前言
Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。然而Shell本身并不是内核的一部分,它只是站在内核的基础上编写的一个应用程序,但是 Shell 也有着它的特殊性,就是开机立马启动,并呈现在用户面前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用 Linux。
Shell 也是一种脚本语言,是系统命令的集合,可以使用逻辑判断、循环等语法,可以自定义函数,我们编写完源码后不用编译,直接运行源码即可。
Shell脚本是在Linux的shell中运行的,所以称为shell脚本。本质上,shell脚本就是一些命令的集合。shell脚本可以实现自动化运维,所以能帮助我们很方便的管理服务器;比如我们可以指定一个任务计划,定时的去执行某个shell脚本已满足需求。
第一行一定是:#/bin/bash 该命令说明,该文件使用的是,bash语法,如果不设置改行,则该脚本不会被执行。 以#开头的作为解释说明。Shell脚本通常以sh为后缀,用于区分这是一个Shell脚本。
下面来编写一个shell脚本,如下所示:
[root@localhost ~]# mkdir shell
[root@localhost ~]# cd shell
[root@localhost shell]# vi 1.sh //写入如下内容
#!/bin/bash
touch /tmp/1.txt
chmod 600 /tmp/2.txt
mv /tmp/1.txt /tmp/2.txt
接下来执行1.1编写的脚本,如下所示:
[root@localhost shell]# bash 1.sh
其实shell脚本还有一种执行方法,但前提是脚本本身要有执行权限,所以在执行前我们需要给脚本加一个x权限。如下所示:
[root@localhost shell]# ./1.sh
-bash: ./1.sh: Permission denied
[root@localhost shell]# chmod +x 1.sh //赋予权限
[root@localhost shell]# ./1.sh //执行脚本
[root@localhost shell]# bash -x 1.sh
+ touch /tmp/1.txt
+ chmod 600 /tmp/2.txt
+ mv /tmp/1.txt /tmp/2.txt
[root@localhost shell]# bash -n 1.sh
date +%Y-%m-%d #年(以四位数字格式打印年份)月日
date +%y-%m-%d #年(以两位数字格式打印年份)月日
date +%F #年(以四位数字格式打印年份)月日
date +%H:%M:%S #小时分钟秒
date +%T #小时分钟秒
date +%w #一周中的第几天
date +%W #一年中的第几周
使用命令date +%s 显示从 1970 年 1 月 1 日 00:00:00 UTC 到目前为止的秒数。示例如下:
使用命令 date -d @164082388 显示输入秒数之前的时间。示例如下:
date -d "+1 hour" #一个小时后
date -d "-1 hour" #一个小时前
date -d "+1day" #一天后
date -d "-1 day" #一天前
在shell脚本中使用变量可以节省时间并且使我们的脚本更加专业,所以当我们编写一个脚本时,就可以使用变量来代替某个使用频繁并且长度很长的字符串。变量的格式:“变量名=变量的值”。
当我们引用某个命令的结果时,可以使用变量替代,示例如下:
[root@localhost ~]# a=`date +%w` #将命令结果赋值给变量
[root@localhost ~]# echo $a
6
[root@localhost ~]# a=$(date +%w) #将命令结果赋值给变量
[root@localhost ~]# echo $a
6
由上例可知,如果我们需要将命令结果赋值给变量,可以采用示例中两种形式的其一来实现。
示例如下:
[root@localhost ~]# read -p "请输入一个数字:" n
请输入一个数字:10
[root@localhost ~]# echo $n
10
[root@localhost ~]# read -p "请输入一个数字:"
请输入一个数字:100
[root@localhost ~]# echo $REPLY
100
示例如下:
[root@localhost shell]# vi bian.sh #创建一个名为bian.sh的脚本
[root@localhost shell]# cat bian.sh
#!/bin/bash
echo "\$1=$1"
echo "第二个参数是$2"
echo "第三个参数是$3"
echo "本脚本一共有$#个参数"
echo "\$0是$0"
[root@localhost shell]# sh bian.sh #执行脚本
$1=
第二个参数是
第三个参数是
本脚本一共有0个参数
$0是bian.sh
[root@localhost shell]# sh bian.sh a b #再次执行脚本
$1=a
第二个参数是b
第三个参数是
本脚本一共有2个参数
$0是bian.sh # $0就是脚本的名字
在 Bash 脚本中,内置变量用于存储和传递脚本运行时的信息。这些变量通常以 $ 符号开头。以下是一些常见的内置变量及其用途:
1.$0:
2.$1 到 $9:
3.$#:
4.$@:
5.$*:
6.$$:
7.$?
8.$_:
理解这些内置变量,你可以编写更加动态和强大的脚本,能够更好地处理用户输入和脚本运行环境。
shell脚本中的变量常用于数学运算当中,示例如下:
[root@localhost shell]# vi sum.sh #创建一个名为sum.sh的脚本
[root@localhost shell]# cat sum.sh
#!/bin/bash
a=1
b=2
sum=$[$a+$b] #还可以采用sum=$(($a+$b))这种写法
echo "$a+$b=$sum"
[root@localhost shell]# sh sum.sh #执行脚本
1+2=3
在shell脚本中,逻辑判断语句通常用于根据条件执行不同的命令。带有else的逻辑判断语句会在条件成立时执行if后面的命令,否则执行else后面的命令。而不带有else的逻辑判断语句只会在条件成立时执行if后面的命令,否则什么都不做。
原理上,逻辑判断语句通过比较两个值的大小、相等性或其他关系来判断条件是否成立。常见的比较操作符有:
基础结构:
if 判断语句; then
command
fi
示例如下:
[root@localhost shell]# vi if1.sh
[root@localhost shell]# cat if1.sh
#!/bin/bash
a=10
if [ $a -gt 4 ]
then
echo ok
fi
[root@localhost shell]# sh -x if1.sh #执行脚本
+ a=10
+ '[' 10 -gt 4 ']'
+ echo ok
ok
[root@localhost shell]# sh -n if1.sh
在这个示例中,我们创建了一个名为if1.sh的shell脚本。脚本首先定义了一个变量a,并将其值设置为10。接下来,我们使用if语句来判断变量a的值是否大于4。如果条件成立(即a大于4),则执行echo ok命令。最后,我们使用fi来结束if语句。
当我们执行这个脚本时,首先会显示+ a=10,表示变量a被赋值为10。接下来,+ '[' 10 -gt 4 ']'表示判断a是否大于4,结果为真。然后,+ echo ok表示执行echo ok命令,输出"ok"。所以最后的输出结果是"ok"。
基础结构:
if 判断语句 ; then
command
else
command
fi
示例如下:
[
root@localhost shell]# vi if2.sh
[root@localhost shell]# cat if2.sh
#!/bin/bash
a=10
if [ $a -gt 4 ]
then
echo ok
else
echo "not ok"
fi
[root@localhost shell]# sh -x if2.sh #执行脚本
+ a=10
+ '[' 10 -gt 4 ']'
+ echo ok
ok
在这个示例中,我们创建了一个名为if2.sh的shell脚本。脚本首先定义了一个变量a,并将其值设置为10。接下来,我们使用if语句来判断变量a的值是否大于4。如果条件成立(即a大于4),则执行echo ok命令。否则,执行echo "not ok"命令。最后,我们使用fi来结束if语句。
当我们执行这个脚本时,首先会显示+ a=10,表示变量a被赋值为10。接下来,+ '[' 10 -gt 4 ']'表示判断a是否大于4,结果为真。然后,+ echo ok表示执行echo ok命令,输出"ok"。所以最后的输出结果是"ok"。
示例如下:
[root@localhost shell]# vi if3.sh
[root@localhost shell]# cat if3.sh
#!/bin/bash
a=3
if [ $a -gt 4 ]
then
echo ok
elif [ $a -gt 8 ]
then
echo "very ok"
else
echo "not ok"
fi
[root@localhost shell]# sh -x if3.sh #执行脚本
+ a=3
+ '[' 3 -gt 4 ']'
+ '[' 3 -gt 8 ']'
+ echo 'not ok'
not ok
在这个示例中,我们创建了一个名为if3.sh的shell脚本。脚本首先定义了一个变量a,并将其值设置为3。接下来,我们使用if语句来判断变量a的值是否大于4。如果条件成立(即a大于4),则执行echo ok命令。否则,我们使用elif语句来判断a是否大于8。如果条件成立(即a大于8),则执行echo "very ok"命令。最后,如果前面的所有条件都不成立,则执行else后面的命令,输出"not ok"。最后,我们使用fi来结束if语句。
当我们执行这个脚本时,首先会显示+ a=3,表示变量a被赋值为3。接下来,+ '[' 3 -gt 4 ']'表示判断a是否大于4,结果为假。然后,+ '[' 3 -gt 8 ']'表示判断a是否大于8,结果也为假。因此,程序将执行else后面的命令,输出"not ok"。所以最后的输出结果是"not ok"。
示例如下:
[root@localhost shell]# vi if4.sh
[root@localhost shell]# cat if4.sh
#!/bin/bash
a=10
if [ $a -gt 4 ]
then
if [ $a -lt 20 ]
then
echo "ok"
else
echo "very ok"
fi
else
echo "not ok"
fi
[root@localhost shell]# sh -x if4.sh #执行脚本
+ a=10
+ '[' 10 -gt 4 ']'
+ '[' 10 -lt 20 ']'
+ echo ok
ok
在这个示例中,我们创建了一个名为if4.sh的shell脚本。脚本首先定义了一个变量a,并将其值设置为10。接下来,我们使用if语句来判断变量a的值是否大于4。如果条件成立(即a大于4),则进入内部的if语句。
在内部的if语句中,我们再次判断a的值是否小于20。如果条件成立(即a小于20),则执行echo "ok"命令。否则,执行echo "very ok"命令。
最后,如果外部的if语句的条件不成立(即a不大于4),则执行else后面的命令,输出"not ok"。最后,我们使用fi来结束if语句。
当我们执行这个脚本时,首先会显示+ a=10,表示变量a被赋值为10。接下来,+ '[' 10 -gt 4 ']'表示判断a是否大于4,结果为真。然后,+ '[' 10 -lt 20 ']'表示判断a是否小于20,结果也为真。因此,程序将执行内部if语句中的echo "ok"命令,输出"ok"。所以最后的输出结果是"ok"。
if [ $a -gt 5 ] && [ $a -lt 10 ] == if [ $a -gt 5 -a $a -lt 10] # -a表示 and
if [ $b -gt 5 ] || [ $b -lt 3] == if [ $b -gt 5 -o $b -lt 3 ] # -o表示 or
注意:root用户对文件的读写比较特殊,即使一个文件没有给root用户读或者写的权限,root也可以读或者写。
命令 if [ -z "$a" ]; 表示当变量a的值为空时会怎么样,如下图所示:
命令 if [ -n "$a" ]; 表示当变量a的值不为空时会怎么样,如下图所示:
说明一下
在这个命令中,! 是一个逻辑运算符,表示“非”或“不是”。它通常与 -z 一起使用,用于检查一个字符串是否为空。
具体来说,[ ! -z "$a$" ] 这个条件判断语句的意思是:如果变量 $a 不为空(即长度大于0),则执行后面的代码块。反之,如果 $a 为空(即长度为0),则不执行后面的代码块。
加这个 ! 和不加的区别如下:
[ -z "$a$" ]:这个命令会检查变量 $a 是否为空。如果为空,那么条件成立,执行后面的代码块;如果不为空,那么条件不成立,不执行后面的代码块。
[ ! -z "$a$" ]:这个命令使用了逻辑非运算符 !,它会反转前面的条件。所以,这个命令实际上是检查变量 $a 是否不为空。如果不为空,那么条件成立,执行后面的代码块;如果为空,那么条件不成立,不执行后面的代码块。
总结一下,[ ! -z "$a$" ] 和 [ -z "$a$" ] 的作用是相反的。前者检查变量 $a 是否不为空,后者检查变量 $a 是否为空。
命令 if grep -q '123' 1.sh; then 表示如果1.sh中含有’123’会怎么样,其中-q表示即使过滤出内容也不要打印出来,如下图所示:
if (($a<1)); then 等同于 if [ $a -lt 1 ];then 二者都可以用来进行判断,需要注意的是,当我们未对变量a进行赋值时则会报错,如下图所示:
注意:[ ]中不能使用<,>,==,!=,>=,<=这样的符号,需要时要使用固定写法 -gt (>); -lt(<); -ge(>=); -le(<=);-eq(==); -ne(!=)。
case判断的基础格式如下:
case 变量 in
value1) #不限制value的个数
command
;;
value2)
command
;;
*) #此处*代表其他值
command
;;
esac
为了让我们能够更加清晰的理解case逻辑判断,接下来我们编写一个脚本来进行实验,内容如下:
[root@localhost shell]# vi case.sh
#!/bin/bash
read -p "Please input a number:" n #提示用户输入一个数字并将其存储在变量n中
if [ -z "$n" ] #检查变量n是否为空
then
echo "Please input a number." #如果n为空,则输出提示信息
exit 1 #退出脚本并返回错误码1
fi
n1=`echo $n|sed 's/[0-9]//g'` #使用sed命令将n中的非数字字符替换为空,然后将结果存储在变量n1中。
if [ -n "$n1" ] #检查变量n1是否不为空
then
echo "Please input a number." #如果n1不为空,说明n中包含非数字字符,输出提示信息
exit 1 #退出脚本并返回错误码1
fi
if [ $n -lt 60 ] && [ $n -ge 0 ] #判断输入的数字是否在0到60之间(包括0和60)
then
tag=1
elif [ $n -ge 60 ] && [ $n -lt 80 ] #判断输入的数字是否在60到80之间(包括60和80)
then
tag=2
elif [ $n -ge 80 ] && [ $n -lt 90 ] #判断输入的数字是否在80到90之间(包括80和90)
then
tag=3
elif [ $n -ge 90 ] && [ $n -le 100 ] #判断输入的数字是否在90到100之间(包括90和100)
then
tag=4
else # 如果输入的数字不在上述范围内,则设置tag为0
tag=0
fi
case $tag in #根据tag的值执行相应的命令
1)
echo "not ok" #如果tag为1,输出"not ok"
;;
2)
echo "ok" #如果tag为2,输出"ok"
;;
3)
echo "very ok" #如果tag为3,输出"very ok"
;;
4)
echo "oook" #如果tag为4,输出"oook"
;;
*)
echo "The number range is 0-100." #如果tag为其他值,输出"The number range is 0-100."
;;
esac # 结束case语句
执行结果:
基础结构如下:
for 变量名 in 循环条件;
do
command
done
这是一个Bash脚本,用于计算1到10的和。脚本中使用了for循环,循环变量为i,循环条件为seq 1 10,表示从1到10的整数序列。在循环体内,每次将i的值累加到sum变量中,并输出当前的i值。最后输出累加后的sum值。
下面进行一个简单的实验演示,演示如下:
[root@localhost shell]# vi for1.sh
#!/bin/bash #是脚本的shebang,指定脚本使用的解释器为bash
sum=0 #初始化一个名为sum的变量,初始值为0
for i in `seq 1 10` #定义一个for循环,循环变量为i,循环条件为seq 1 10,即从1到10的整数序列
do #循环体的开始
sum=$[$sum+$i] #将sum的值加上当前循环变量i的值,并将结果赋值给sum
echo $i #输出当前循环变量i的值
done #循环体的结束
echo $sum #输出累加后的sum值
执行结果:
基础结构:
while 条件; do
command
done
示例1:
[root@localhost shell]# vi while1.sh
#!/bin/bash
while : #此处冒号表示死循环
do
load=`w|head -1|awk -F 'load average:' '{print $2}'|cut -d. -f1` #赋值给变量load
if [ $load -gt 10 ] #判断负载平均值是否大于10
then #如果满足条件,执行以下操作
top|mail-s "load is high: $load" asldkfls011.com #发送一封邮件通知管理员。这里使用了top命令获取系统进程信息,并通过mail命令发送邮件。邮件的主题是"load is high: $load",收件人地址为asldkfls011.com。
fi
sleep 30 #等待30秒后再次执行循环
done
运行结果:
示例2:
[
root@localhost shell]# vi while2.sh #创建一个脚本
#!/bin/bash
while :
do
read -p "Please input a number: " n #提示用户输入一个数字,并将输入的值存储在变量n中
if [ -z "$n" ] #检查变量n是否为空,如果为空,则执行下面的代码块
then
echo "you need input sth." #输出提示信息,告诉用户需要输入内容
continue #跳过当前循环的剩余部分,直接进入下一次循环
fi
n1=`echo $n|sed 's/[0-9]//g '` #使用sed命令将变量n中的非数字字符替换为空,然后将结果存储在变量n1`中
if [ -n "$n1" ] #检查变量n1是否不为空,如果不为空,说明用户输入了非数字字符,执行下面的代码块
then
echo "you just only input numbers." #出提示信息,告诉用户只能输入数字
continue #跳过当前循环的剩余部分,直接进入下一次循环
fi
break
done
echo $n
运行结果:
break在脚本中表示跳出该层循环,示例如下:
[root@localhost shell]# vi break1.sh
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
break
fi
echo $i
done
echo aaaaa
执行结果:
在Shell脚本中,break命令用于跳出当前循环。当执行到break时,程序会立即退出当前的循环结构(如for、while等),并继续执行循环之后的代码。
的示例中,break1.sh脚本使用了一个for循环来遍历数字1到5。在每次循环中,首先输出当前的数字,然后判断这个数字是否等于3。如果等于3,就执行break命令,跳出循环。否则,继续执行循环。
由于break命令的存在,当i等于3时,循环会被中断,因此只会输出1、2和3。之后,脚本会继续执行循环之后的代码,即输出"aaaaa"。
当在shell脚本中使用continue时,结束的不是整个循环,而是本次循环。忽略continue之下的代码,直接进行下一次循环。示例如下:
[root@localhost shell]#vi continue1.sh #创建一个shell脚本
#!/bin/bash
for i in `seq 1 5 `
do
echo $i
if [ $i == 3 ]
then
continue #此处continue表示若 $i == 3 则结束本次循环
fi
echo $i
done
echo $i
执行结果:
在Shell脚本中,continue命令用于结束当前循环的剩余部分,并立即开始下一次循环。当执行到continue时,程序会跳过当前循环体中continue之后的所有代码,直接进入下一次循环。
示例中,continue1.sh脚本使用了一个for循环来遍历数字1到5。在每次循环中,首先输出当前的数字,然后判断这个数字是否等于3。如果等于3,就执行continue命令,跳过本次循环的剩余部分(即不执行echo $i),直接进入下一次循环。否则,继续执行循环。
由于continue命令的存在,当i等于3时,不会输出3,而是直接进入下一次循环。因此,最终只会输出1、2、4和5。之后,脚本会继续执行循环之后的代码,即输出最后一个变量i的值(此时i为5)。
当我们在shell脚本中遇到exit时,其表示直接退出整个shell脚本。示例如下:
[root@localhost shell]# vi exit1.sh
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i == 3 ]
then
exit
fi
echo $i
done
echo aaaa
执行结果:
在Shell脚本中,exit命令用于立即退出整个脚本。当执行到exit时,程序会立即终止当前脚本的执行,并返回一个退出状态码(默认为0,表示成功)。
示例中,exit1.sh脚本使用了一个for循环来遍历数字1到5。在每次循环中,首先输出当前的数字,然后判断这个数字是否等于3。如果等于3,就执行exit命令,直接退出整个脚本。否则,继续执行循环。
由于exit命令的存在,当i等于3时,循环会被中断,并且整个脚本也会立即退出。因此,只会输出1和2,而不会输出3、4、5以及"aaaa"。
shell脚本中的函数就是先把一段代码整理到了一个小单元中,并给这个小单元命名,当我们用到这段代码时直接调用这个小单元的名字就可以了,这样很方便,省时省力。但我们需要注意,在shell脚本中,函数一定要写在前面,因为函数要被调用的,如果还未出现就被调用就会出错。
基础格式:
function f_name()
{
command
}
Input() { echo $1 $2 $3 $0 } |
函数调用
Input 1 a b
调用 input 函数,并传递三个参数 1,a和b
示例如下:
[root@localhost shell]# vi fun1.sh
#/bin/bash
input()
{
echo $1 $2 $# $0 # 函数的参数:$1 $2 $# ;$0则是脚本的名字
}
input 1 a b
执行结果:
示例如下:
[root@localhost shell]# vi fun2.sh
#!/bin/bash
sum()
{
s=$[$1+$2]
echo $s
}
sum 1 2
执行结果:
示例如下:
[root@localhost shell]# vi fun3.sh
#!/bin/bash
ip()
{
ifconfig |grep -A1 "$1: " |tail -1 |awk '{print $2}'
}
read -p "Please input the eth name: " e
myip=`ip $e`
echo "$e address is $myip"
执行结果:
首先我们需要先定义一个数组a=(1 2 3 4 5);
命令 echo ${a[@]} 读取数组中的全部元素。示例如下:
命令 echo ${#a[@]} 获取数组的元素个数。示例如下:
命令 echo ${a[2]} 读取第三个元素,数组从0开始。示例如下:
echo ${a[*]} 等同于 ${a[@]} 作用为显示整个数组。示例如下:
a[1]=100; echo ${a[@]} 替换指定的元素值。示例如下:
a[5]=2; echo ${a[@]} 如果下标不存在则会自动添加一个元素。示例如下:
a[7]=6; echo ${a[@]} 跳着添加元素时,中间未赋值的元素,不显示且无值。示例如下:
命令unset a[1] 用于删除单个元素。示例如下:
命令unset a 用于删除整个数组。示例如下:
在进行实验操作之前,需要对一个数组进行赋值 a=(`seq 1 5`) 。
命令echo ${a[@]:0:3} 表示从第一个元素开始,截取3个元素,并打印出来。示例如下:
命令echo ${a[@]:1:4} 表示从第二个元素开始,截取4个元素,并打印出来。示例如下:
echo ${a[@]:0-3:2} 表示从倒数第3个元素开始,截取2个元素,并打印出来。示例如下:
使用命令echo ${a[@]/b/100} 表示用100替换b,但不会保存替换,只是打印出来。示例如下:
使用命令a=(${a[@]/b/100}) 表示用100替换b,这种方法不仅可以打印出来还可以保存替换。示例如下:
如果感觉对您有帮助动动你发财的小手一键三连呦!
后续还会持续更新以及脚本案例
因篇幅问题不能全部显示,请点此查看更多更全内容