shell脚本耕升(一)


篇幅较长,可能还有后续更新,建议收藏
此篇是我读《 linux shell 脚本攻略》所写的的个人笔记
文章主体脉络由书中得来
相关命令用法由书中或互联网或系统帮助,或个人实验得来
例子由自己编写而来

我为什么都标个人原创?

1.本人所有文章都是个人笔记,不是专业写博客的
2.此文章不产生任何盈利,与原书也发生不了任何纠纷
3.例子,命令用法,注释都是个人编写
4.例如此书,转载必须填写原网址,我该如何填写一本书的网址?
5.文章内容可能从一篇,或者很多篇博客,书籍,又或者是系统命令帮助而来,我没有很多精力来记录他们,或者询问能否转载

写博客的意义
1.所有文章只是作为个人在线笔记
2.投公开发表是觉得可能会对初学者提供一些帮助

文件描述符

文件描述符是与一个打开的文件或数据流相关联的整数。也是用于访问文件的一个抽象指针。存取文件离不开被称为“文件描述符”的特殊数字。0、1和2分别是stdin、stdout和stderr的预留描述符。

  • 0——stdin(标准输人)
  • 1——stdout(标准输出)
  • 2——stderr(标准错误)

例如我们在源码安装mysql初始化的时候会有密码需要暂时保存一下那我们可以使用echo将输出文本重定向到一个文件中

[root@zzyyssxx ~]# echo '123456' > pwd.txt
[root@zzyyssxx ~]# cat pwd.txt 
123456
[root@zzyyssxx ~]# echo 'abcdef' >>  pwd.txt
[root@zzyyssxx ~]# cat pwd.txt 
123456
abcdef
[root@zzyyssxx ~]# echo '2233' >  pwd.txt
[root@zzyyssxx ~]# cat pwd.txt 
2233

和>>并不相同。尽管这两个操作符都可以将文本重定向到文件,但是前者会先清空文件,再写入内容;而后者会将内容追加到现有文件的下一行。

当使用重定向操作符时,重定向的内容不会出现在终端,而是直接被导入文件。重定向操作符默认使用标准输出。如果想使用特定的文件描述符,你必须将描述符置于操作符之前。

相同的 > 等同于 1> ,>> 等同于 1>>

输入错误命令时会出现错误信息,同时会返回一个非0状态码

[root@zzyyssxx ~]# lshdfsjf
-bash: lshdfsjf: 未找到命令

这里我们输入一个不合规参数,会返回标准错误信息

[root@zzyyssxx ~]# ls +
ls: 无法访问+: 没有那个文件或目录
[root@zzyyssxx ~]# echo $?
2
[root@zzyyssxx ~]# ls + > out.txt
ls: 无法访问+: 没有那个文件或目录
[root@zzyyssxx ~]# cat out.txt 
[root@zzyyssxx ~]# ls + 2> out.txt
[root@zzyyssxx ~]# cat out.txt 
ls: 无法访问+: 没有那个文件或目录

显然的标准输出没有被写到文件里,标准错误被写到了文件里面

也可以将错误和输出重定向到不同的文件

cmd 2>stderr.txt 1>stdout.txt

也可以将stderr转换成stdout,然后重定向到一个文件中

cmd 2>&1 output.txt
或 cmd &> output.txt

当我们不想看到错误的输出时可以

cmd 2> /dev/null

dev/null是一个特殊的设备文件,这个文件接收到的任何数据都会被丢弃。因此,null设备通常也被称为黑洞。

当对stderr或stdout进行重定向时,重定向的文本将传入文件。因为文本已经被重定向到文件中,也就没剩下什么东西可以通过管道(I)传给接下来的命令,而这些命令是通过stdin来接收文本的。。

tee命令可以一方面将数据重定向到文件,另一方面还可以提供一份重定向的数据副本作为后续命令的stdin

作用:从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件

语法格式:tee [参数] [文件]

常用参数:

-a	附加到既有文件的后面,而非覆盖它
-i	忽略中断信号
— help 	查看帮助信息
— version	显示版本信息
[root@zzyyssxx ~]# echo '123' | tee 1.txt 2.txt
123
[root@zzyyssxx ~]# ls
1.txt  2.txt 
[root@zzyyssxx ~]# cat 1.txt 
123
[root@zzyyssxx ~]# cat 2.txt 
123

有时候,我们需要对文本块(多行文本)像标准输人一样进行重定向。考虑一个特殊情况:文本就位于shell脚本中。可以按照下面的方法成:

[root@zzyyssxx ~]# cat <<EOF> 123.txt
> 1
> 2
> 3
> EOF
[root@zzyyssxx ~]# ls
123.txt
[root@zzyyssxx ~]# cat 123.txt 
1
2
3
[root@zzyyssxx ~]# cat 1.sh 
#!/bin/bash

cat <<EOF> 111.txt
a
b
c
EOF
 
[root@zzyyssxx ~]# chmod +x 1.sh  #授权
[root@zzyyssxx ~]# ls
123.txt  1.sh
[root@zzyyssxx ~]# ./1.sh   #执行
[root@zzyyssxx ~]# ls
111.txt  123.txt  1.sh
[root@zzyyssxx ~]# cat 111.txt 

自定义文件描述符

除了预留的0,1,2描述符,我们可以使用exec命令创建自定义的文件描述符。文件的打开模式通常来说,会使用3种模式。

  • 只读模式
  • 截断模式
  • 追加模式

exec命令用于调用并执行指定的命令,也可以调用其他的命令。如果在当前终端中使用命令,则当指定的命令执行完毕后会立即退出终端。

语法格式:exec [参数]

常用参数:

-c	在空环境中执行指定的命令
[root@zzyyssxx ~]# exec 3<111.txt  #使用文件描述符3打开并读取文件
[root@zzyyssxx ~]# cat <&3
a
b
c
//如果要再次读取就不能,需要再次通过exec重新分配文件扫描符
[root@zzyyssxx ~]# cat <&3

//与find连用
[root@zzyyssxx ~]# find /root/ -name "*.txt" -exec ls {} \;
/root/123.txt
/root/111.txt
//需要注意的是连用的时候要输入完整的命令
[root@zzyyssxx ~]# find /root/ -name "*.txt" -exec ll {} \;
find: ‘ll’: 没有那个文件或目录
find: ‘ll’: 没有那个文件或目录
[root@zzyyssxx ~]# find /root/ -name "*.txt" -exec ls -l {} \;
-rw-r--r-- 1 root root 6 4月  12 13:58 /root/123.txt
-rw-r--r-- 1 root root 6 4月  12 14:04 /root/111.txt

创建一个文件描述符用于写入(截断模式)

[root@zzyyssxx ~]# cat <&3
[root@zzyyssxx ~]# exec 3>111.txt 
[root@zzyyssxx ~]# cat 111.txt 
[root@zzyyssxx ~]# echo new >&3
[root@zzyyssxx ~]# cat 111.txt 
new
[root@zzyyssxx ~]# exec 4>>111.txt
[root@zzyyssxx ~]# echo 110 >&4    
[root@zzyyssxx ~]# cat 111.txt 
new
old
110

数组和关联数组

数组是shell脚本重要的组成部分,它借助索引将多个独立数据存储为1个集合。

Bash同时支持普通数组和关联数组。普通数组只能使用整数作为数组索引
,而关联数组可以使用字符串作为数组索引。关联数组在很多操作中相当有用。Bash从4.0版本开始支持关联数组。

可以使用一列值定义一个数组,#这些值将会存储在以0为起始索引的连续位置上

[root@zzyyssxx ~]# array_var=(1 2 3 4 5)
[root@zzyyssxx ~]# echo ${array_var[0]}
1

另外,还可以将数组定义成一组索引-值

[root@zzyyssxx ~]# array_var[0]="a"
[root@zzyyssxx ~]# array_var[1]="b"
[root@zzyyssxx ~]# array_var[2]="c"
[root@zzyyssxx ~]# echo $array_var[2]  //不加括号会默认打印第一个索引的值
a[2]
[root@zzyyssxx ~]# echo ${array_var[2]}
c

以清单形式打印数组中所有值

[root@zzyyssxx ~]# echo ${array_var[*]}
a b c
[root@zzyyssxx ~]# echo ${array_var[@]}
a b c

打印数组长度,也就是元素个数

[root@zzyyssxx ~]# echo ${#array_var[@]}
3
[root@zzyyssxx ~]# echo ${#array_var[*]}
3

定义关联数组

在关联数组中,我们可以用任意的文本作为数组索引。而在普通数组中,只能用整数作为数组索引。关联数组需要先声明再使用。
首先,需要使用单独的声明语句将一个变量名声明为关联数组。声明语句如下:

[root@zzyyssxx ~]# declare -A acb_array
语法格式: declare [参数] [目录]

declare: 用法:declare [-aAfFgilrtux] [-p] [name[=value] ...]

常用参数:

-a	声明数组变量
-f	仅显示函数
-F	不显示函数定义
-i	先计算表达式,把结果赋给所声明变量
-p	显示给定变量的定义的方法和值,当使用此选项时,其他的选项将被忽略
-r	定义只读变量
-x	将指定的Shell变量转换成环境变量
[root@zzyyssxx ~]# declare 
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'
CALSS_PATH=.:/usr/local/jdk1.8.0_221/lib/dt.jar:/usr/local/jdk1.8.0_221/lib/tools.jar:/usr/local/jdk1.8.0_221/jre/lib
COLUMNS=83
DIRSTACK=()
EUID=0
GROUPS=()
HISTCONTROL=ignoredups
HISTFILE=/root/.bash_history
HISTFILESIZE=1000
HISTSIZE=1000
HOME=/root
HOSTNAME=zzyyssxx
HOSTTYPE=x86_64
ID=0
IFS=$' \t\n'
JAVA_HOME=/usr/local/jdk1.8.0_221
......
USER=root
XDG_RUNTIME_DIR=/run/user/0
XDG_SESSION_ID=4097
_=-h
acb_array=()
array_var=([0]="a" [1]="b" [2]="c")
colors=/root/.dircolors

可以看到里面包含许多环境变量

[root@zzyyssxx ~]# declare -p SSH_CLIENT //这是ssh相关参数
declare -x SSH_CLIENT="39.170.63.135 58535 22"
[root@zzyyssxx ~]# echo $SSH_CLIENT
39.170.63.135 58535 22

篇幅有限,此处不过多描述

可以用两种方法将元素添加到关联数组中。

  • 1.用内嵌索引-值列表法,写一个索引-值列表:
[root@zzyyssxx ~]# acb_array=([index1]=jerry [index2]=tom)
  • 2.使用独立的索引-值进行赋值:
[root@zzyyssxx ~]# acb_array[ind1]=apple
[root@zzyyssxx ~]# acb_array[ind2]=orange
[root@zzyyssxx ~]# echo ${acb_array[ind2]}
orange
[root@zzyyssxx ~]# echo ${acb_array[ind1]}
apple
[root@zzyyssxx ~]# echo ${acb_array[0]} //直接打印索引0这里为空

每一个数组元素都有一个索引用于查找。普通数组和关联数组具有不同的索引类型。

//列出数组索引
[root@zzyyssxx ~]# echo ${!acb_array[*]}
index1 index2 ind1 ind2
[root@zzyyssxx ~]# echo ${!acb_array[@]}
index1 index2 ind1 ind2

别名

alias命令用来设置指令的别名。我们可以使用该命令可以将一些较长的命令进行简化。使用alias时,用户必须使用单引号 ‘ ‘ 将原来的命令引起来,防止特殊字符导致错误。

语法格式:alias [参数]

常用参数:

-p	打印已经设置的命令别名
[root@zzyyssxx ~]# alias -p
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'

[root@zzyyssxx ~]# alias cd='cd $@;ls --color=auto' //添加别名
[root@zzyyssxx ~]# alias -p
alias cd='cd ;ls --color=auto'  //新添加的别名
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'

//测试一下
[root@zzyyssxx ~]# cd /usr/local/
aegis         games                       lib      sbin
bin           include                     lib64    share
cloudmonitor  jdk1.8.0_221                libexec  src
etc           jdk-8u221-linux-x64.tar.gz  nginx

alias命令的作用只是暂时的。一旦关闭当前终端,所有设置过的别名就失效了。为了使别名设置一直保持作用,可以将它放入~.bashrc文件中。因为每当一个新的shell进程生成时,都会执行 ~/.bashr中的命令。

[root@zzyyssxx ~]# echo 'alias cd="cd $@;ls --color=auto"' >> ~/.bashrc  //新进程也同样生效

如果需要删除别名,只用将其对应的语句从~l.bashrc中删除,或者使用unalias命令。另一种创建别名的方法是定义一个具有新名称的函数,并把它写入 ~/.bashr

[root@zzyyssxx ~]#  alias rm='cp $@ ~/backup; rm $@'

这样写可以防止误操作,定期清理即可
当你创建别名时,如果已经有同名的别名存在,那么原有的别名设置将被新的取代。

别名也存在相关安全隐患,万一服务器被入侵常用命令修改为其他命令会造成巨大损失,轻则提桶跑路,重则背上法律责任。
那么,如果避免这一点呢,答案就是转义

[root@zzyyssxx ~]# cd /usr/local/
aegis         games                       lib      sbin
bin           include                     lib64    share
cloudmonitor  jdk1.8.0_221                libexec  src
etc           jdk-8u221-linux-x64.tar.gz  nginx
[root@zzyyssxx ~]# \cd /usr/local/
[root@zzyyssxx local]# 

字符\对命令实施转义,使我们可以执行原本的命令,而不是这些命令的别名替身。

获取终端信息

编写命令行shell脚本的时候,总是免不了大量处理当前终端的相关信息,
比如行数、列数、光标位置和遮盖密码字段等。

put命令将通过 terminfo 数据库对您的终端会话进行初始化和操作。通过使用 tput,您可以更改几项终端功能,如移动或更改光标、更改文本属性,以及清除终端屏幕的特定区域。

语法格式:tput [参数]

setb 用于设置背景颜色
setf 用于设置前景颜色
cols 获取行数
lines 获取列数

获取,设置日期与延时

当我们备份数据库时一般需要根据日期以及时间来执行操作。而延时一般在监控任务脚本,比如每5秒执行一次脚本。

我们能够以多种格式打印日期,也可以在命令行中设置日期。在类UNIX系统中,日期被存储为一个整数,其大小为自世界标准时间1970年1月1日0时0分0秒起所流逝的秒数。这种计时方式称之为纪元时或UNIX时间,一般叫时间戳。


读取日期

[root@zzyyssxx ~]# date
20220418日 星期一 09:17:16 CST
[root@zzyyssxx ~]# date +%s
1650244686

将得到的时间戳进行转换,因为用的是云主机所以不用校准时间

//输出给定日期的字符串
[root@zzyyssxx ~]# date --date "Jan 20 2010" +%A
星期三
[root@zzyyssxx ~]# date "+%d %B %Y"
18 四月 2022
[root@zzyyssxx ~]# date "+%Y %B %d"
2022 四月 18

类似的这样的脚本可以检查一组命令花费的时间

#!/bin/bash

start=$(date +%s)
commands;
end=$(date +%s)
    
difference=$(( end -start ))

echo time is $difference seconds

延时

编写以循环的方式监控脚本时,需要设置时间间隔,可以利用sleep来延时

[root@zzyyssxx ~]# cat test.sh 
#!/bin/bash
echo -n count:
tput sc
count=0;
while true;
do
if [ $count -lt 30 ];then
let count++;
sleep 1;
tput rc
tput ed
echo -n $count;
else exit 0;
fi
done

变量count初始化为0,随后每循环一次便增加1。echo语句打印出count的值。我们用tput sc存储光标位置。在每次循环中,我们通过恢复之前存储的光标位置,在终端中打印出新的count值。恢复光标位置的命令是tput rc。tptit ed清除从当前光标位置到行尾之间的所有内容,使得旧的count值可以被清除并写人新值。循环内的1秒钟延时是通过sleep命令来实现的

调试脚本

调试功能是每一种编程语言都应该实现的重要特性之一,当出现一些始料未及的情况时,用它来生成脚本运行信息。调试信息可以帮你弄清楚是什么原因使得程序发生崩溃或行为异常。

//
[root@zzyyssxx ~]# sh -x test.sh 
。。。。。。。略
+ echo -n 29
29+ true
+ '[' 29 -lt 30 ']'
+ let count++
+ sleep 1
+ tput rc
+ tput ed
+ echo -n 30
30+ true
+ '[' 30 -lt 30 ']'
+ exit 0

-x 将脚本中执行过的每一行都输出到stdout,当我们只关注脚本某些命令可以使用调试打印。

set -x:在执行时显示参数和命令
set +:禁止调试
set -v:当命令进行读取时显示输入
set +v:禁止打印输入
[root@zzyyssxx ~]# cat debug.sh 
#!/bin/bash

for i in {1..6}
do
set -x
echo $i
set +x
done

echo "script executed"
[root@zzyyssxx ~]# sh -x debug.sh 
+ for i in '{1..6}'
+ set -x
+ echo 1
1
+ set +x
+ echo 2
2
+ set +x
+ echo 3
3
+ set +x
+ echo 4
4
+ set +x
+ echo 5
5
+ set +x
+ echo 6
6
+ set +x
script executed

也可以利用shebang来调试,将#!/bin/bash变为 #!/bin/bash -xv

#!/bin/bash -xv

for i in {1..6}
do
set -x
echo $i
set +x
done

echo "script executed"

[root@zzyyssxx ~]# chmod +x debug.sh 
[root@zzyyssxx ~]# ./debug.sh 
#!/bin/bash -xv

for i in {1..6}
do
set -x
echo $i
set +x
done
+ for i in '{1..6}'
+ set -x
+ echo 1
1
+ set +x
+ echo 2
2
+ set +x
+ echo 3
3
+ set +x
+ echo 4
4
+ set +x
+ echo 5
5
+ set +x
+ echo 6
6
+ set +x

echo "script executed"
script executed

函数与参数

定义函数:

function  函数名 ()
{
命令;
流程语句;
}

函数名 ()
{
命令;
流程语句;
}

定义完函数后只需要使用函数名就可以调用某个函数

函数名 ;//调用函数

参数可以传递给函数,并由脚本进行访问

函数名 arg1 arg2//传递参数
fname ( )
{
echo $1, $2 ;       #访问参数1和参数2
echo " $@";       #以列表的方式一次性打印所有参数
echo "$*";          #类似于$0,但是参数被作为单个实体
return 0 ;           #返回值
}

参数可以传递给脚本并通过script :$0(脚本名)访问
$1是第一个参数。

$2是第二个参数。

$n是第n个参数。

“$@”被扩展成”$1" “$2” "$3"等。

"$”被扩展成"$1c$2c 3 " " 3" " 3""@”用得最多。由于"$"将所有的参数当做单个字符串,因此它很少被使用。

递归函数

在Bash中,函数同样支持递归(可以调用自身的函数)。例如,F( ) { echo $1; F hello;sleep 1; }。

  • Fork炸弹
:() { :|:& }; :

这个递归函数能够调用自身,不断地生成新的进程,最终造成拒绝式服务攻击(Dos)。函数调用前的&将子进程放入后台。因为这段代码会分支出大量的进程,所以被称为Fork炸弹。

http://en.wikipedia.org/wiki/Fork_bomb fork炸弹相关解释

可以通过ulimit -u 20来限制用户进程数量
永久生效可以

echo   '"用户名" - nproc 20 ' >> /etc/security/limits.conf

导出函数

函数也能像环境变量一样用export导出,这样一来函数的作用域就可以扩展到子进程中。

export -f 函数名

读取命令序列输出

shell脚本可以轻松地将多个命令或工具组合起来生成输出。一个命令的输出可以作为另一个命令的输入,而这个命令的输出又会传递至另一个命令。

[root@localhost ~]# ls
anaconda-ks.cfg  nmap-7.92-1.x86_64.rpm
[root@localhost ~]# ls | cat -n   		## 管道符|将前一个命令的参数传给下一个命令
     1  anaconda-ks.cfg
     2  nmap-7.92-1.x86_64.rpm
[root@localhost ~]# ls | cat -n > out.txt
[root@localhost ~]# ls 
anaconda-ks.cfg  nmap-7.92-1.x86_64.rpm  out.txt
[root@localhost ~]# cat out.txt 
     1  anaconda-ks.cfg
     2  nmap-7.92-1.x86_64.rpm
     3  out.txt

##子shell
[root@localhost ~]# ouput=$(ls | cat -n)
[root@localhost ~]# echo $ouput
1 123.txt 2 anaconda-ks.cfg 3 nmap-7.92-1.x86_64.rpm 4 out.txt

##反引用`在tab键上面
[root@localhost ~]# ouput=`ls | cat`
[root@localhost ~]# echo $ouput
123.txt anaconda-ks.cfg nmap-7.92-1.x86_64.rpm out.txt

##加双引号保留换行符
[root@localhost ~]# abc=$(ls | cat -n)
[root@localhost ~]# echo "$abc"
     1  123
     2  123.txt
     3  anaconda-ks.cfg
     4  nmap-7.92-1.x86_64.rpm
     5  out.txt

不按回车键的方式读取字符“n”

read命令的功能是用于读取单行数据内容,一般是从标准输入中读取数值,用于给变量赋值。

|语法格式:read [参数]

常用参数:说明
-a定义一个数组,以空格为间隔符进行赋值
-d定义一个结束标志
-p设置提示信息
-e在输入的时候可以使用命令补全功能
-n定义输入文本的长度
-r禁用转义符(\)
-s输入字符不在屏幕显示
-t限定最长等待时间
-u从文件描述符中读入信息
[root@localhost ~]# read -n 2 asd  #-n指定2个字符, 输入2个字符后会自动回车
23[root@localhost ~]# echo $asd
23

##静默输入
[root@localhost ~]# read -s pass  ##输入后不会显示在屏幕上
[root@localhost ~]# echo $pass
1234

##添加提示信息
[root@localhost ~]# read -p "input:"
input:123

##利用界定符代替回车结束输入
[root@localhost ~]# read -d ":" zxc   ##指定界定符为:
123:[root@localhost ~]# echo $zxc
123

字段分隔符与迭代器

[root@localhost ~]# cat test.sh 
# !/bin/bash
#用途:演示IFS的用法
line="root:x :0:0:root:/root:/bin/bash"
oldIFS=$IFS;
IFS=":"
count=0
for item in $line;
do
[ $count -eq 0 ] && user=$item;
[ $count -eq 6 ] && shell=$item;
let count++
done;
IFS=$o1dIFS
echo $user \'s she1l is $shell;

for循环

[root@localhost ~]# for i in {a..z};do echo $i;done
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z

[root@localhost ~]# for ((i=0;i<10;i++)) { echo $i; }
0
1
2
3
4
5
6
7
8
9

##生成连续ip地址
[root@localhost ~]# for i in 192.168.15.{1..10};do echo $i;done
192.168.15.1
192.168.15.2
192.168.15.3
192.168.15.4
192.168.15.5
192.168.15.6
192.168.15.7
192.168.15.8
192.168.15.9
192.168.15.10

##起始为1,步进为2,到10 结束
[root@localhost ~]# for i in $(seq 1 2 10);do echo $i;done
1
3
5
7
9

条件测试,循环等补充知识点

命令使用

cat 查看

[root@localhost ~]# cat passwd 
root:x:0:0:root:/root:/bin/bash

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 ~]# cat -n passwd    ##显示行号包括所有空白行
     1  root:x:0:0:root:/root:/bin/bash
     2  
     3  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
     4  
     5  halt:x:7:0:halt:/sbin:/sbin/halt
     6  
     7  mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
     8  
     9  
    10  
    11  operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost ~]# cat -s passwd  ##压缩连续的空白行
root:x:0:0:root:/root:/bin/bash

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 ~]# cat  passwd | tr -s '\n'  ## tr -s 删除重复出现的字符序列
root:x:0:0:root:/root:/bin/bash
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

script,scriptreplay - 录制与查看终端会话

script命令可以用作交互终端会话过程的记录,保留用户输入和系统输出的全过程。以备之后查询与学习。

在下述语法中,输出文件是用于存储所有的上机会话过程。如果未指定输出文件,所有的会话过程将会写到当前工作目录的默认文件typescript中。

语法格式:script [参数] [文件]

常用参数:
-a	把会话记录附加到typescript文件或指定的文件后面,保留先前的内容
-c	运行指定的命令而非交互shell
-r  子进程中返回退出代码
-f  如果需要在输出到日志文件的同时,也可以查看日志文件的内容
-q  可以使script命令以静默模式运行
-t  指明输出录制的时间数据
-V  输出script的版本信息,然后退出
-h  输出script的help信息,然后退出
[root@localhost ~]# which script
/usr/bin/script
[root@localhost ~]# which scriptreplay
/usr/bin/scriptreplay

[root@localhost ~]# script -V
script,来自 util-linux 2.23.2
[root@localhost ~]# script 
Script started, file is typescript
[root@localhost ~]# exit
exit
Script done, file is typescript
[root@localhost ~]# ll
总用量 8
-rw-r--r--. 1 root root 208 114 22:31 passwd
-rw-r--r--. 1 root root 181 117 18:01 typescript

##静默模式
[root@localhost ~]# script -q
[root@localhost ~]# exit
exit
//没有任何提示


[root@localhost ~]# script -c "ls -l"
Script started, file is typescript
总用量 4
-rw-r--r--. 1 root root 208 114 22:31 passwd
-rw-r--r--. 1 root root   0 117 22:40 typescript
Script done, file is typescript

[root@localhost ~]# ls
passwd  typescript
[root@localhost ~]# cat typescript 
脚本启动于 20240117日 星期三 224013秒
总用量 4
-rw-r--r--. 1 root root 208 114 22:31 passwd
-rw-r--r--. 1 root root   0 117 22:40 typescript

Script done on 20240117日 星期三 224013//没加-a之前的内容全被清空了 相当于重定向

[root@localhost ~]# script -t 2> time.log -a typescript  ## -t将时序导入stderr,2> 将stderr导入time.log
Script started, file is typescript
[root@localhost ~]# ls
passwd  time.log  typescript
[root@localhost ~]# touch 123
[root@localhost ~]# ls
123  passwd  time.log  typescript
[root@localhost ~]# exit
exit
Script done, file is typescript
[root@localhost ~]# ls
123  passwd  time.log  typescript
[root@localhost ~]# cat time.log   //时间序列数据
0.605960 49
0.031052 1
2.154059 1
0.127346 2
0.248924 30
0.002781 41
0.000425 1
1.548721 1
0.151315 1
0.223722 1
0.209794 1
0.142308 1
0.288548 1
1.160007 1
0.359510 4
0.672967 1
0.247383 4
0.632045 1
0.112081 1
0.216661 2
0.497288 41
0.002472 1
1.507865 1
0.183600 2
0.448379 35
0.002940 41
0.000538 1
2.332258 1
0.247890 1
0.151661 1
0.169472 8
[root@localhost ~]# cat typescript 
脚本启动于 20240117日 星期三 224013秒
总用量 4
-rw-r--r--. 1 root root 208 114 22:31 passwd
-rw-r--r--. 1 root root   0 117 22:40 typescript

Script done on 20240117日 星期三 224013秒
脚本启动于 20240117日 星期三 224336[root@localhost ~]# ls
passwd  time.log  typescript
[root@localhost ~]# touch 123
[root@localhost ~]# ls
123  passwd  time.log  typescript
[root@localhost ~]# exit
exit

Script done on 20240117日 星期三 224351// 未设置时间同步


[root@localhost ~]# scriptreplay time.log typescript   //按时间序列数据回放
总用量 4
-rw-r--r--. 1 root root 208 114 22:31 passwd
-rw-r--r--. 1 root root   0 117 22:40 typescript

Script done on 20240117日 星期三 224013秒
脚本启动于 20240117日 星期三 224336

find 文件查找

find命令的功能是根据给定的路径和条件查找相关文件或目录,可以使用的参数很多,并且支持正则表达式,结合管道符后能够实现更加复杂的功能,是系统管理员和普通用户日常工作必须掌握的命令之一。

语法格式:find [路径] [参数]

常用参数:
-name	匹配名称
-iname 匹配名称且忽略大小写
-perm	匹配权限(mode为完全匹配,-mode为包含即可)
-user	匹配所有者
-group	匹配所有组
-mtime -n +n	匹配修改内容的时间(-n指n天以内,+n指n天以前)
-atime -n +n	匹配访问文件的时间(-n指n天以内,+n指n天以前)
-ctime -n +n	匹配修改文件权限的时间(-n指n天以内,+n指n天以前)
-nouser	匹配无所有者的文件
-nogroup	匹配无所有组的文件
-newer f1 !f2	匹配比文件f1新但比f2旧的文件
-type b/d/c/p/l/f	匹配文件类型(后面的字幕字母依次表示块设备、目录、字符设备、管道、链接文件、文本文件)
-size	匹配文件的大小(+50KB为查找超过50KB的文件,而-50KB为查找小于50KB的文件)
-prune	忽略某个目录
-exec …… {}\;	后面可跟用于进一步处理搜索结果的命令
[root@localhost ~]# find . -print  ##打印当前目录下所有文件
.
./.bash_logout
./.bash_profile
./.bashrc
./.cshrc
./.tcshrc
./.bash_history
./passwd
./.viminfo
./typescript
./time.log
./123
//-print是默认的不加也可以

##反向匹配所有不以log结尾的
[root@localhost ~]# find .  ! -name "*.log"
.
./.bash_logout
./.bash_profile
./.bashrc
./.cshrc
./.tcshrc
./.bash_history
./passwd
./.viminfo
./typescript
./123

##路径匹配
[root@localhost ~]# find / -path "*sysconfig*"
find: ‘/proc/15773’: 没有那个文件或目录
/run/initramfs/state/etc/sysconfig
/run/initramfs/state/etc/sysconfig/network-scripts
/etc/sysconfig
/etc/sysconfig/ip6tables-config
/etc/sysconfig/iptables-config
。。。

## 基于目录深度的搜索
[root@localhost ~]# find / -maxdepth 1 -type f 
/nohup_xxl-job-admin.log
[root@localhost ~]# find / -maxdepth 2 -type f 
/boot/.vmlinuz-3.10.0-693.el7.x86_64.hmac
/boot/System.map-3.10.0-693.el7.x86_64
/boot/config-3.10.0-693.el7.x86_64
/boot/symvers-3.10.0-693.el7.x86_64.gz
/boot/vmlinuz-3.10.0-693.el7.x86_64
。。。

##-newer 找出比对比文件更长的文件
[root@localhost ~]# find . -type f -newer time.log 
./.bash_history
./passwd
./.viminfo
./typescript
./123


##-delete 删除找出的文件
[root@localhost ~]# find . -type f   -newer time.log -delete
[root@localhost ~]# ls
time.log

##-exec 表示将找到的文件交给下一步{} 代表找到的文件
[root@localhost ~]# find . -type f  -name "*.log" -exec mv {} 123 \;
[root@localhost ~]# ls
123

xargs 格式化从标准输出的数据,传递给下一个参数

xargs命令默认接收的信息中,空格是默认定界符,所以可以接收包含换行和空白的内容。

[root@localhost ~]# find . -type f -name "*.txt" -print | xargs echo
./abc.txt ./123.txt

[root@localhost ~]# find . -type f -name "*.txt" -print | xargs  rm -f
[root@localhost ~]# ls
123

tr 转换

r命令是一款批量字符转换、压缩、删除的文本工具,但仅能从标准输入中读取文本内容,需要与管道符或输入重定向操作符搭配使用。

语法格式:tr [参数] 字符串1 字符串2

常用参数:
-c	反选字符串1的补集(取反)
-d	删除字符串1中出现的所有字符
-s	删除所有重复出现的字符序列
##大小写转换
[root@localhost ~]# echo "HELLO WORLD" | tr 'A-Z' 'a-z'
hello world
[root@localhost ~]# echo "HELLO WORLD" | tr L l
HEllO WORlD
[root@localhost ~]# echo "HELLO WORLD" | tr H h
hELLO WORLD

##通过数字映射加密
[root@localhost ~]# echo 12345 | tr '0-9' '9876543210'
87654               ##已加密 通过映射将原来的12345转换为87654

##反过来替换解密
[root@localhost ~]# echo 87654  | tr '9876543210' '0-9'
12345

##删除不想要的数据
[root@localhost ~]# echo "Hello 123 world 456" |  tr -d '0-9'
Hello  world       ##将stdin中的数字删除并打印出来

##字符串补集
[root@localhost ~]# echo hello 1 char 2 next 3 | tr -d -c '0-9 \n'
 1  2  3
[root@localhost ~]# echo hello 1 char 2 next 3 | tr -d -c '0-9'
123

##压缩连续的重复字符
[root@localhost ~]# echo hello      char    next  | tr -s ' '
hello char next
//压缩的重复的连续空格

简单实践

//计算下列数字和
[root@localhost ~]# cat sum.txt 
1
2
3
4
5
7
9
[root@localhost ~]# cat sum.txt | echo $[ $(tr '\n' '+') 0 ]
31
//将每行的换行符替换为+号,但是最后一行9+ 没有操作数所有需要在外面加个0
  • tr可以像使用集合一样使用各种不同的字符类,这些字符类如下所示。
xdigit十六进制字符
alnum字母和数字
alpha字母
cntrl控制(非打印)字符
digit数字
grap图形字符
lower小写字母
print可打印字符
punct标点符号
space空白字符
upper大写字母
[root@localhost ~]# echo hello 1 char 2 next 3 | tr '[:lower:]' '[:upper:]'
HELLO 1 CHAR 2 NEXT 3
//小写转大写

sort,uniq 排序单一,与重复

语法格式:sort [参数] 文件

常用参数:
-b	忽略每行前面开始出的空格字符
-c	检查文件是否已经按照顺序排序
-d	除字母、数字及空格字符外,忽略其他字符
-f	将小写字母视为大写字母
-i	除040至176之间的ASCII字符外,忽略其他字符
-m	将几个排序号的文件进行合并
-M	将前面3个字母依照月份的缩写进行排序
-n	依照数值的大小排序
-o <输出文件>	将排序后的结果存入制定的文件
-r	以相反的顺序来排序
-t <分隔字符>	指定排序时所用的栏位分隔字符
-k	指定需要排序的栏位
##按字母排序
[root@localhost ~]# cat apple.txt 
banana
pear
apple
orange
raspaberry
[root@localhost ~]# sort apple.txt 
apple
banana
orange
pear
raspaberry

##按开头数字排序
[root@localhost ~]# cat number.txt 
45
12
3
98
82
67
24
56
9
2324
2
4
a
c
bc
a
[root@localhost ~]# sort number.txt 
12
2
2324
24
3
4
45
56
67
82
9
98
a
a
bc
c

##按数字大小排序
[root@localhost ~]# sort -n number.txt 
a
a
bc
c
2
3
4
9
12
24
45
56
67
82
98
2324


##通过列进行排序
[root@localhost ~]# cat data.txt 
1   centos  790
2   linux   300
3   euleros 500
4   ubantu  1000
5  oracle   501
6   unix     10000
[root@localhost ~]# sort -nk 1 data.txt //n以大小排序 k指定列 
1   centos  790
2   linux   300
3   euleros 500
4   ubantu  1000
5  oracle   501
6   unix     10000

[root@localhost ~]# sort -nk 3 data.txt 
2   linux   300
3   euleros 500
5  oracle   501
1   centos  790
4   ubantu  1000
6   unix     10000
[root@localhost ~]# sort -nkr 3 data.txt  //k指定行号 放最后面
sort: 区块起始处的编号无效:在"r" 处的计数无效
[root@localhost ~]# sort -nrk 3 data.txt 
6   unix     10000
4   ubantu  1000
1   centos  790
5  oracle   501
3   euleros 500
2   linux   300

uniq 消除重复行

语法格式:uniq [参数] 文件

常用参数:
-c	打印每行在文本中重复出现的次数
-d	每个重复纪录只出现一次
-u	只显示没有重复的纪录
[root@localhost ~]# cat cont.txt 
hack
hack
apple
oppo
xiaomi
apple

[root@localhost ~]# uniq cont.txt  ##消除相邻重复行
hack
apple
oppo
xiaomi
apple

[root@localhost ~]# uniq -c  cont.txt ##消除重复行并统计重复次数,空行也算
      2 hack
      1 apple
      1 oppo
      1 xiaomi
      1 apple
      1 

[root@localhost ~]# sort cont.txt | uniq -d  ##打印文件重复行
apple
hack

校验和与核实

校验和(checksum)程序用来从文件中生成校验和密钥,然后利用这个校验和密钥核实文件完整性。一份文件可以通过网络或任何存储介质分发到不同的地点。出于多种原因,数据有可能丢失或损坏。这时就需要利用校验和来验证文件完整性,特别是数据库备份脚本。

-md5sum 一个32字符的16进制串

[root@localhost ~]# md5sum sum.txt 
d6fcd506544a5b3a46f7f9d21dced3cf  sum.txt
[root@localhost ~]# md5sum sum.txt > sum.md5
[root@localhost ~]# md5sum -c sum.md5 
sum.txt: 确定

-sha1sum 一个40字符的16进制串

[root@localhost ~]# sha1sum sum.txt 
b5a29a0473702a7a3a2068aa55bc5a8fe7bb0592  sum.txt
[root@localhost ~]# sha1sum sum.txt > sum.sha1
[root@localhost ~]# sha1sum -c sum.sha1
sum.txt: 确定

临时文件命名与随机随机数

编写shell脚本时,我们经常需要存储临时数据。最适合存储临时数据的位置是/tmp(该目录中的内容在系统重启后会被清空)。有两种方法可以为临时数据生成标准的文件名。

  • 利用随机数
[root@localhost ~]# echo $RANDOM  ##返回一个随机数
991

temp_file=" / tmp /file-$RANDOM"

  • 利用进程id
temp_file=" /tmp/var.s$"  ##.$$ 作为添加的后缀会被扩展成当前运行脚本的进程ID。


[root@localhost ~]# cat test.sh 
#!/bin/sh

temp_file=/tmp/var.$$
echo $temp_file

[root@localhost ~]# chmod +x test.sh 
[root@localhost ~]# ./test.sh 
/tmp/var.12280

分割文件与日志

dd 拷贝与转换文件

dd命令来自于英文词组“disk dump”的缩写,其功能是用于拷贝及转换文件。使用dd命令可以按照指定大小的数据块来拷贝文件,并在拷贝的过程中对内容进行转换。

语法格式:dd 参数 对象

常用参数:
-v	显示版本信息
-h	显示帮助信息
[root@localhost ~]# dd if=/dev/zero bs=100k count=1 of=data.file
记录了1+0 的读入
记录了1+0 的写出
102400字节(102 kB)已复制,0.000726083 秒,141 MB/秒
[root@localhost ~]# ls
data.file  
[root@localhost ~]# du -sh *   ##查看文件大小
100K    data.file

split 切割文件

split命令可以将大文件分割成较小的文件,在默认情况下将按照每1000行切割成一个小文件 。

语法格式:split [参数] [切割文件][文件名]

常用参数:
-b	指定每多少字节切成一个小文件
--help 	查看帮助信息
--version 	显示版本信息
-C	与参数”-b”相似,但是在切割时将尽量维持每行的完整性 
-d   后缀使用数字
-a   指定后缀长度
-l   指定行数分割
[root@localhost ~]# split -b 10k data.file  ##以10k切割
[root@localhost ~]# ls
data.file  xaa  xab  xac  xad  xae  xaf  xag  xah  xai  xaj

[root@localhost ~]# split -b 10k data.file -d -a 3 ##-d以数字切割 -a 指定后缀长度
[root@localhost ~]# ls
data.file  x000  x001  x002  x003  x004  x005  x006  x007  x008  x009  xaa  xab  xac  xad  xae  xaf  xag  xah  xai  xaj
//除了k,还可以使用M (MB)、c (GB)、c (byte)、w (word)

[root@localhost ~]# split -b 10k data.file -d -a 3 split_file ## 修改前缀名字
[root@localhost ~]# ls
data.file  split_file000  split_file001  split_file002  split_file003  split_file004  split_file005  split_file006  split_file007  split_file008  split_file009

[root@localhost ~]# split -8 /etc/passwd   ##按行切割
[root@localhost ~]# ls
data.file  xaa  xab  xac
[root@localhost ~]# cat xa
xaa  xab  xac  
[root@localhost ~]# cat xaa
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

[root@localhost ~]# rm -f x*
[root@localhost ~]# split -l 8 /etc/passwd    ##l可省略
[root@localhost ~]# ls
data.file  xaa  xab  xac
[root@localhost ~]# cat xaa
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

csplit 根据文本特点分割文件

语法参数:csplit [参数]

常用参数:
-b<输出格式>或--suffix-format=<输出格式> 	预设的输出格式其文件名称为xx00,xx01等,用户可以通过改变<输出格式>来改变输出的文件名
-f<输出字首字符串>或--prefix=<输出字首字符串> 	预设的输出字首字符串其文件名为xx00,xx01等,如果制定输出字首字符串为“hello”,则输出的文件名称会变成hello00,hello、01……
-k或--keep-files 	保留文件,就算发生错误或中断执行,与不能删除已经输出保存的文件
-n<输出文件名位数>或--digits=<输出文件名位数> 	预设的输出文件名位数其文件名称为xx00,xx01……如果用户指定输出文件名位数为“3”,则输出的文件名称会变成xx000,xx001等
-q或-s或--quiet或--silent 	不显示指令执行过程
-z或--elide-empty-files 	删除长度为0 Byte文件。 
[root@localhost ~]# cat server.log 
SERVER一1
[connection] 192.168.0.1 success
[connection] 192.168.0.2 failed
[diaconnect] 192.168.0.3 pending
[connection] 192.168.0.4 success
SERVER-2
[connection] 192.168.0.1 failed
[connection] 192.168.0.2 fai1ed
[disconnect] 192.168.0.3 success
[connection] 192.168.0.4 failed

[root@localhost ~]# csplit server.log /SERVER/ -n 2 -s {*} -f server -b "%02d.log" ; rm -f server00.log
[root@localhost ~]# ls
data.file  server01.log  server02.log  server.log
[root@localhost ~]# cat server01.log 
SERVER一1
[connection] 192.168.0.1 success
[connection] 192.168.0.2 failed
[diaconnect] 192.168.0.3 pending
[connection] 192.168.0.4 success
[root@localhost ~]# cat server02.log 
SERVER-2
[connection] 192.168.0.1 failed
[connection] 192.168.0.2 fai1ed
[disconnect] 192.168.0.3 success
[connection] 192.168.0.4 failed

/SERVER/用来匹配某一行,分割过程即从此处开始。
/[REGEX]/表示文本样式。包括从当前行(第一行)直到(但不包括)包含“SERVER"的匹配行。
{*}表示根据匹配重复执行分割,直到文件末尾为止。可以用{整数}的形式来指定分割执行的次数。
-s使命令进入静默模式,不打印其他信息。
-n指定分割后的文件名后缀的数字个数,例如01、02、03等。
-f指定分割后的文件名前缀(在上面的例子中,server就是前缀)-b指定后缀格式。例如“%02d.log”,类似于C语言中printf的参数格式。在这里文件名=前缀+后缀=server + %02d.log。
因为分割后的第一个文件没有任何内容(匹配的单词就位于文件的第一行中),所以我们删掉分割的第一行也就是server00.log

根据扩展名提取文件名

有一些脚本是依据文件名进行各种处理的。我们可能会需要在保留扩展名的同时修改文件名、转换文件格式(保留文件名的同时修改扩展名)或提取部分文件名。shell具有的内建功能可以依据不同的情况来切分文件名。

##提取名称
[root@localhost ~]# file_jpg="sample.jpg"
[root@localhost ~]# name=${file_jpg%.*}
[root@localhost ~]# echo file name is $name
file name is sample


${VAR%.*〕的含义是:
从$VARIABLE中删除位于%右侧的通配符(在前例中是.*)所匹配的字符串。通配符从右向左进行匹配。
给VAR赋值,VAR=sample.jpg。那么,通配符从右向左就会匹配到.jpg,因此,从$VAR中删除匹配结果,就会得到输出“sarnple”。

%属于非贪婪操作,他找出从右到左匹配通配符的最短结果
%%属于贪婪操作, 他会找出符合条件的最长字符串
例如:
[root@localhost ~]# var=123.abc.256.789.txt
[root@localhost ~]# echo ${var%%.*}
123
[root@localhost ~]# echo ${var%.*}
123.abc.256.789


##提取扩展名
[root@localhost ~]# extension=${file_jpg#*.}
[root@localhost ~]# echo $extension
jpg

#与%类似只是顺序相反。
#是从左往右进行匹配

[root@localhost ~]# var=123.abc.256.789.txt
[root@localhost ~]# echo ${var#*.}
abc.256.789.txt
[root@localhost ~]# echo ${var##*.}
txt
//当文件有多个扩展名的时候,使用##贪婪匹配能更准确的提取出扩展名

批量重命名与移动

[root@localhost ~]# cat rename.sh 
#!/bin/bash
#文件名rename.sh
count=1;
for img in *.log
do
new=image-$count.${img##*.}

mv "$img" "$new" 2> /dev/null

if [ $? -eq 0 ];then

echo "Renaming $img to $new"
let count++

fi
done

[root@localhost ~]# ls
data.file  rename.sh  server01.log  server02.log  server.log

[root@localhost ~]# ./rename.sh 
Renaming server01.log to image-1.log
Renaming server02.log to image-2.log
Renaming server.log to image-3.log

交互输入自动化

##简单交互
[root@localhost ~]# cat test.sh 
#!/bin/bash

read -p "你想打印什么?" print1

echo $print1
[root@localhost ~]# chmod +x test.sh 
[root@localhost ~]# ./test.sh 
你想打印什么?123
123

read 从标准输入中读取数值

read命令的功能是用于读取单行数据内容,一般是从标准输入中读取数值,用于给变量赋值。

语法格式:read [参数]

常用参数:
-a 	定义一个数组,以空格为间隔符进行赋值
-d 	定义一个结束标志
-p	设置提示信息
-e	在输入的时候可以使用命令补全功能
-n	定义输入文本的长度
-r	禁用转义符(\)
-s	输入字符不在屏幕显示
-t	限定最长等待时间
-u	从文件描述符中读入信息
##简单交互自动化 \n等于回车
[root@localhost ~]# echo -e  "123\n" | ./test.sh 
123


##简单升级一下
[root@localhost ~]# cat test.sh 
#!/bin/bash

read -p "你想打印什么?" print1
read -p "你还想打印什么?" print2
echo $print1
echo $print2
[root@localhost ~]# ./test.sh 
你想打印什么?234
你还想打印什么?567
234
567


[root@localhost ~]# echo -e  "123\nabc\n" | ./test.sh 
123
abc

##输入较多时可以制作输入文件
[root@localhost ~]# echo -e  "cvb\n789\n"  > input.txt
[root@localhost ~]# cat input.txt 
cvb
789

[root@localhost ~]# ./test.sh < input.txt 
cvb
789

除此之外还可以使用一些函数例如expect

文件管理

UNIX将操作系统中的一切都视为文件。文件与每一个操作息息相关,而我们可以利用它们进行各种与系统或进程相关的处理工作。例如,我们所使用的命令终端就是和一个设备文件关联在一起的。

有各种不同形式的文件,比如目录、普通文件、块设备、字符设备、符号链接、套接字和命名管道等。

文件的属性包括文件名、大小、文件类型、文件内容修改时间(modification time)、文件访问时间(access time)、文件属性更改时间(change time)、i节点、链接以及文件所在的文件系统

生成任意大小的文件

创建特定文件最简单的办法就是使用dd命令,此法在上文分割文件也提到过,在此详细介绍一下。

##if代表输入文件,of代表输出文件,bs指定大小,count指定块
[root@localhost ~]# dd if=/dev/zero of=junk.data bs=1M count=1
记录了1+0 的读入
记录了1+0 的写出
1048576字节(1.0 MB)已复制,0.00126048 秒,832 MB/秒
[root@localhost ~]# du -sh junk.data 
1.0M    junk.data
/// /dev/zero是一个字符设备,它会不断返回0值字节。

如果不指定输入参数(if),默认情况下dd会从stdin中读取输入。与之类似,如果不指定输出参数(of),则dd会将stdout作为默认输出。

文本文件的交集与差集

comm 比较差异

comm命令可用于两个文件之间的比较。它有很多不错的选项可用来调整输出,以便我们执行交集、求差(difference)以及差集操作。

  • 交集:打印出两个文件所共有的行。
  • 求差:打印出指定文件所包含的且不相同的那些行。
  • 差集:打印出包含在文件A中,但不包含在其他指定文件中的那些行。
[root@localhost ~]# cat A.txt 
apple
orange
gold
silver
steel
[root@localhost ~]# cat B.txt 
orange
gold
cookies
carrot
[root@localhost ~]# comm A.txt B.txt  //comm必须使用排过序的文件作为输入
apple
                orange
comm: 文件1 没有被正确排序
comm: 文件2 没有被正确排序
                gold
        cookies
        carrot
silver
steel
[root@localhost ~]# sort A.txt -o A.txt ;sort B.txt -o B.txt 
//-o 将排过序结果输入指定文件
[root@localhost ~]# comm A.txt B.txt 
apple
        carrot
        cookies
                gold
                orange
silver
steel
##输出的第一列包含只在A.txt中出现的行,第二列包含只在B.txt中出现的行,第三列包含A.txt和B.txt中相同的行。各列以制表符(t)作为定界符。

有一些选项可以按照我们的需求进行格式化输出,
例如:
-1 从输出中删除第一列
-2 从输出中删除第二列
-3 从输出中删除第三列

//删除第一,二列,便只会打印出他们的交集
[root@localhost ~]# comm A.txt B.txt -1 -2
gold
orange

//取出全集,并删除制表符转化为单列
[root@localhost ~]# comm A.txt B.txt -3 | sed 's/^\t//'
apple
carrot
cookies
silver
steel

查找并删除重复文件

此处的重复文件指内容一样的文件,而不是名字相同的文件。
在前面校验和里面提到过,相同的文件内容的校验和也是相同的。

##创建测试文件及副本
[root@localhost ~]# echo "hello"  > test ; cp test test_copy1 ; cp test test_copy2;
[root@localhost ~]# echo "next" > other

[root@localhost ~]# vim remove.sh
#!/ bin/ bash
#文件名:remove_duplicates.sh
#用途:查找并删除重复文件,每一个文件只保留一个样本
ls -lS | awk "BEGIN {
getline;getline;
name1=$8 ; size=$5
}
{name2=$8 ;
if (size==$5)
{
"md5sum "name1 | getline; csum1=$1;
"md5sum "name2 | getline; csum2=$1;
if ( csum1==csum2 )
{print name1 ; print name2 }};
size=$5; name1=name2 ;
}'| sort -u > duplicate_files
cat duplicate_files | xargs -I { } md5sum { } | sort | uniq -w 32 | awk '{ print "^"$2"$" }'| sort -u > duplicate_sample
echo Removing . .
comm duplicate.files duplicate_sample -2 -3 | tee /dev/stderr | xargs rm
echo Removed duplicates files successfully.

创建不可删除文件

[root@localhost ~]# cat /etc/mtab 
rootfs / rootfs rw 0 0
sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=6058616k,nr_inodes=1514654,mode=755 0 0
securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev/shm tmpfs rw,seclabel,nosuid,nodev 0 0
.....
#可以查看分区设备路径与文件系统类型

chattr 修改文件属性

不可修改属性是保护文件不被修改的安全手段之一。最有代表性的例子就是/etc/shadow文件。该文件由当前系统中所有用户的加密密码组成。我们通过密码才能够登录系统。用户通常用passwd命令修改自己的密码。执行passwd时,它实际上就修改了letc/shadow文件。我们可以将shadow文件设置为不可修改,这样就再没有用户能够修改密码了。

[root@localhost ~]# useradd test
[root@localhost ~]# passwd test
更改用户 test 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符  ##不影响修改
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
[root@localhost ~]# chattr +i /etc/shadow  ##添加不可修改属性
[root@localhost ~]# passwd test
更改用户 test 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd: 鉴定令牌操作错误 
[root@localhost ~]# echo '12345' | passwd --stdin test
更改用户 test 的密码 。
passwd: 鉴定令牌操作错误
[root@localhost ~]# chattr -i /etc/shadow   ##移除不可修改属性
[root@localhost ~]# echo '12345' | passwd --stdin test
更改用户 test 的密码 。
passwd:所有的身份验证令牌已经成功更新。
//移除不可修改属性后,可以正常修改

批量生成空白文件

touch 创建文件

touch命令的功能是用于创建空文件与修改时间戳。如果文件不存在,则会创建出一个空内容的文本文件;如果文件已经存在,则会对文件的Atime(访问时间)和Ctime(修改时间)进行修改操作,管理员可以完成此项工作,而普通用户只能管理主机的文件。

语法格式:touch [参数] 文件

常用参数:
-a	改变档案的读取时间记录
-m 	改变档案的修改时间记录
-r	使用参考档的时间记录,与 --file 的效果一样
-c	不创建新文件
-d	设定时间与日期,可以使用各种不同的格式
-t	设定档案的时间记录,格式与 date 命令相同
--no-create 	不创建新文件
--help	显示帮助信息
--version	列出版本讯息
#1
[root@localhost test1]# touch {1..100}.py
[root@localhost test1]# ls
100.py  13.py  17.py  20.py  24.py  28.py  31.py  35.py  39.py  42.py  46.py  4.py   53.py  57.py  60.py  64.py  68.py  71.py  75.py  79.py  82.py  86.py  8.py   93.py  97.py
10.py   14.py  18.py  21.py  25.py  29.py  32.py  36.py  3.py   43.py  47.py  50.py  54.py  58.py  61.py  65.py  69.py  72.py  76.py  7.py   83.py  87.py  90.py  94.py  98.py
11.py   15.py  19.py  22.py  26.py  2.py   33.py  37.py  40.py  44.py  48.py  51.py  55.py  59.py  62.py  66.py  6.py   73.py  77.py  80.py  84.py  88.py  91.py  95.py  99.py
12.py   16.py  1.py   23.py  27.py  30.py  34.py  38.py  41.py  45.py  49.py  52.py  56.py  5.py   63.py  67.py  70.py  74.py  78.py  81.py  85.py  89.py  92.py  96.py  9.py
#2
[root@localhost test1]# for i in {1..10}.php; do touch $i; done 
[root@localhost test1]# ls
10.php  1.php  2.php  3.php  4.php  5.php  6.php  7.php  8.php  9.php

列举文件类型统计信息

file 识别文件类型

ile命令的功能是用于识别文件的类型,也可以用来辨别一些内容的编码格式。由于Linux系统并不是像Windows系统那样通过扩展名来定义文件类型,因此用户无法直接通过文件名来进行分辨。file命令则是为了解决此问题,通过分析文件头部信息中的标识来显示文件类型,使用很方便。

语法格式:file [参数] 文件

常用参数:
-b	列出辨识结果时,不显示文件名称 (简要模式)
-c	详细显示指令执行过程
-f	指定名称文件,显示多个文件类型信息
-L	直接显示符号连接所指向的文件类别
-m	指定魔法数字文件
-v	显示版本信息
-z	尝试去解读压缩文件的内容
-i	显示MIME类别
[root@localhost ~]# file test
test: ASCII text
[root@localhost ~]# file -i test
test: text/plain; charset=us-ascii



[root@localhost ~]# cat filestat.sh 
#!/bin/bash
#文件名: filestat.sh
if [ $# -ne 1 ];then  
  echo $0 basepath;
  echo
fi
path=$1

declare -A statarray;
while read line;
do
  ftype=`file -b "$line"`
  let statarray["$ftype"]++;
done< <( find $path -type f -print)

for ftype in "${!statarray[@]}";
do
  echo $ftype : ${statarray["$ftype"]}
done

[root@localhost ~]# ./filestat.sh test
ASCII text : 1
[root@localhost ~]# ./filestat.sh test1
empty : 10

在脚本中声明了一个关联数组statarray,这样可以用文件类型作为数组索引,将每种文件类型的数量存入数组。每次遇到一个文件类型,就用1et增加计数。find命令以递归的方式获取文件路径列表。脚本中的ftype='file -b "$line"使用file命令获得文件类型信息。

<(find $path -type f -print)等同于文件名。只不过它用子进程输出来代替文件名。注意这里还有另外一个<。
${ !statarray [@]}用于返回一个数组索引列表。

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值