文章目录
篇幅较长,可能还有后续更新,建议收藏
此篇是我读《 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
2022年 04月 18日 星期一 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 1月 14 22:31 passwd
-rw-r--r--. 1 root root 181 1月 17 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 1月 14 22:31 passwd
-rw-r--r--. 1 root root 0 1月 17 22:40 typescript
Script done, file is typescript
[root@localhost ~]# ls
passwd typescript
[root@localhost ~]# cat typescript
脚本启动于 2024年01月17日 星期三 22时40分13秒
总用量 4
-rw-r--r--. 1 root root 208 1月 14 22:31 passwd
-rw-r--r--. 1 root root 0 1月 17 22:40 typescript
Script done on 2024年01月17日 星期三 22时40分13秒
//没加-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
脚本启动于 2024年01月17日 星期三 22时40分13秒
总用量 4
-rw-r--r--. 1 root root 208 1月 14 22:31 passwd
-rw-r--r--. 1 root root 0 1月 17 22:40 typescript
Script done on 2024年01月17日 星期三 22时40分13秒
脚本启动于 2024年01月17日 星期三 22时43分36秒
[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 2024年01月17日 星期三 22时43分51秒
// 未设置时间同步
[root@localhost ~]# scriptreplay time.log typescript //按时间序列数据回放
总用量 4
-rw-r--r--. 1 root root 208 1月 14 22:31 passwd
-rw-r--r--. 1 root root 0 1月 17 22:40 typescript
Script done on 2024年01月17日 星期三 22时40分13秒
脚本启动于 2024年01月17日 星期三 22时43分36秒
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 | 小写字母 |
可打印字符 | |
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 [@]}用于返回一个数组索引列表。