shell的学习
一.简述shell:
shell在官方的定义是linux内核的外壳,提供外部与Linux内核之间交互的工具。shell是一种语言,具有跨平台移植性,有多个版本如csh,ksh,sh。个人用的最多的是bash,Linux 操作系统缺省的 shell 是Bourne Again shell,它是 Bourne shell 的扩展,简称 Bash。
查看当前使用的shell类别:
zhugeling@newbie-unknown85879:/home/zhugeling$ which sh /bin/sh zhugeling@newbie-unknown85879:/home/zhugeling$ ls -l /bin/sh lrwxrwxrwx 1 root root 4 Nov 13 12:01 /bin/sh -> bash
二、如何学习shell?
个人感悟:其实shell的语法跟C语言差不多,但是shell还有很多可供使用的很多强大的命令,如sed,awk,grep等,实用性更强。在学习过程中,基础语法并不是很多,但是结合正则表达是和各种命令之后,变得很灵活。个人的感觉是在理解shell的解析原理基础上要多练习,多练习,多练习!!!不然记不住那么多命令,参数等等。
三、学习shell基础语法
1、变量:
(1)shell的变量不需要特殊声明,引用的时候要在前面用$符号。
(2)变量的赋值,对空格比较敏感,如:
[root@localhost ~]# myvalue= 123 -bash: 123: command not found [root@localhost ~]# myvalue =123 -bash: myvalue: command not found [root@localhost ~]# myvalue=123 [root@localhost ~]# echo $myvalue 123
变量赋值时,”=”号两边不能有空格
另外引用变量时,尽量用{} 确定变量的范围,变量名不能含有内置变量(shell的特殊变量)。
双引号和单引号对变量的影响
单引号里用$引用的变量将失效:
zhugeling@newbie-unknown85879:~/tmp$ name=zhugeling zhugeling@newbie-unknown85879:~/tmp$ echo $name zhugeling zhugeling@newbie-unknown85879:~/tmp$ sayhello="Hello $name" zhugeling@newbie-unknown85879:~/tmp$ echo $sayhello Hello zhugeling zhugeling@newbie-unknown85879:~/tmp$ sayhello='Hello $name' zhugeling@newbie-unknown85879:~/tmp$ echo $sayhello Hello $name
(3)shell的特殊变量:
shell有一些特殊变量,这些变量经常在脚本用回用到,有必要学会如何使用。
$0 当前脚本的文件名
$n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。 $# 传递给脚本或函数的参数个数。 $* 传递给脚本或函数的所有参数。 $@ 传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同,下面将会用测试用例讲到。 $? 上个命令的退出状态,或函数的返回值。一般情况下,大部分命令执行成功会返回 0,失败返回 1。 $$ 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。
测试变量:
测试示例1:输出各个内置变量的含义值
[root@localhost tmp]# cat test.sh #!/bin/bash a=$0 b=$2 c=$# d=$* e=$@ f=$? g=$$ echo $a echo $b echo $c echo $d echo $e echo $f echo $g
输入1,2,3参数,输出:
[root@localhost tmp]# sh test.sh 1 2 3 test.sh 2 3 1 2 3 1 2 3 0 26583
测试示例2:研究∗和@的区别
zhugeling@newbie-unknown85880:~/tmp$ cat test.sh #!/bin/bash for var in "$*" do echo "* is $var" done for var in "$@" do echo "@ is $var" done zhugeling@newbie-unknown85880:~/tmp$ sh test.sh 1 3 5 6 * is 1 3 5 6 @ is 1 @ is 3 @ is 5 @ is 6
区别:∗在同一行了,而@则逐个换行了,怀疑是 $* 把所有参数当成一个变量了,我们修改脚本测试一下,加入自加变量并输出。
zhugeling@newbie-unknown85880:~/tmp$ cat test.sh #!/bin/bash i=0 j=0 for var in "$*" do echo "* is $var" i=$((i+1)) echo "i is $i" done for var in "$@" do j=$((j+1)) echo "j is $j" echo "@ is $var" done
zhugeling@newbie-unknown85880:~/tmp$ sh test.sh 1 3 5 6 * is 1 3 5 6 i is 1 j is 1 @ is 1 j is 2 @ is 3 j is 3 @ is 5 j is 4 @ is
可以确认他们的区别就是,当引用@和* 的使用,如果用双引号扩起来,则∗是会变成一个变量值,而@则是会按空格区分多个变量,如果输入的参数 类似 “a d”,也会被当成一个变量。
(4)获取用户输入为变量赋值:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash echo -e "input your name:" read name echo "your name is $name !" zhugeling@newbie-unknown85879:~$ sh test.sh input your name: zhugeling your name is zhugeling !
2、数组:
shell也是一种语言,数组也是必不可少的,数组也是一种变量,组合变量。
shell的数组下标是用中括号括起来,从0开始计算第一个数组变量,声明时可以直接赋给值,用空格分隔开变量值。
数组使用示例:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash #空数组arr1 arr1=() arr2=(1 2 3 4 5 6) echo ${arr2[0]} echo ${arr2[5]} zhugeling@newbie-unknown85879:~$ sh test.sh 1 6
数组可以参与循环的使用:
for循环引用数组的时候,数组的下标用@符号,其含义可以参看前面的特殊变量说明,将@换成*也可以实现下面的效果。
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash #空数组arr1 arr1=() arr2=(1 2 3 4 5 6) for var in ${arr2[@]} do echo "i am $var !" done zhugeling@newbie-unknown85879:~$ sh test.sh i am 1 ! i am 2 ! i am 3 ! i am 4 ! i am 5 ! i am 6 !
3、if判断语句:
算术比较运算符
num1 -eq num2 等于 [ 3 -eq $mynum ] num1 -ne num2 不等于 [ 3 -ne $mynum ] num1 -lt num2 小于 [ 3 -lt $mynum ] num1 -le num2 小于或等于 [ 3 -le $mynum ] num1 -gt num2 大于 [ 3 -gt $mynum ] num1 -ge num2 大于或等于 [ 3 -ge $mynum ] [ -z " $num" ] 等于零 = 两个字符相等 != 两个字符不等 -n 非空串 不为空 –b 当file存在并且是块文件时返回真 -c 当file存在并且是字符文件时返回真 -d 当pathname存在并且是一个目录时返回真 -e 当pathname指定的文件或目录存在时返回真 -f 当file存在并且是正规文件时返回真 -g 当由pathname指定的文件或目录存在并且设置了SGID位时返回为真 -h 当file存在并且是符号链接文件时返回真,该选项在一些老系统上无效 -k 当由pathname指定的文件或目录存在并且设置了“粘滞”位时返回真 -p 当file存在并且是命令管道时返回为真 -r 当由pathname指定的文件或目录存在并且可读时返回为真 -s 当file存在文件大小大于0时返回真 -u 当由pathname指定的文件或目录存在并且设置了SUID位时返回真 -x 当由pathname指定的文件或目录存在并且可执行时返回真。一个目录为了它的内容被访问必然是可执行的。 -o 当由pathname指定的文件或目录存在并且被子当前进程的有效用户ID所指定的用户拥有时返回真
语法:
(1)单分支:
if 判断条件;then ....... fi (2)双分支: if 判断条件;then ..... else .... fi
(3)多分支会用到elif: 注意elif 也要带 then
例如:
if [[ "$x" -le "100" ]];then echo "good." elif [[ "$x" -lt "60" ]];then echo "ok." else echo "Unknow argument...." fi
常见用法:
(1)判断脚本程序中前一句的执行情况:
$? 为0则是执行成功,1则失败
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash cd /home/zhugeling if [ $? -eq 0 ];then echo "success" else echo "fail" fi zhugeling@newbie-unknown85879:~$ sh test.sh success
(2)、判断用户输入的参数是否为空:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash read -p "input your name:" name if [ ! -n "$name" ] ;then echo "your input is null !" else echo "your input is $name" fi zhugeling@newbie-unknown85879:~$ sh test.sh input your name:zhugeling your input is zhugeling zhugeling@newbie-unknown85879:~$ sh test.sh input your name: your input is null !
(3)、判断文件夹是否存在,判断文件是否存在:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash read -p "input your file :" file if [ -d "$file" ] then echo "diretory $file found." else echo "diretory $file not found." fi if [ -f "$file" ] then echo "file $file found." else echo "file $file not found." fi zhugeling@newbie-unknown85879:~$ sh test.sh input your file :tmp diretory tmp found. file tmp not found. zhugeling@newbie-unknown85879:~$ sh test.sh input your file :test.sh diretory test.sh not found. file test.sh found.
(4)判断字符串或数值等于,大于,小于等:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash read -p "input your number1 :" number1 read -p "input your number2 :" number2 if [ $number1 -eq $number2 ] then echo "$number1 等于 $number2." elif [ $number1 -gt $number2 ] then echo "$number1 大于 $number2 " else echo "$number1 小于 $number2 " fi zhugeling@newbie-unknown85879:~$ sh test.sh input your number1 :12 input your number2 :13 12 小于 13 zhugeling@newbie-unknown85879:~$ sh test.sh input your number1 :18 input your number2 :16 18 大于 16 zhugeling@newbie-unknown85879:~$ sh test.sh input your number1 :12 input your number2 :12 12 等于 12.
上面判断的变量值还可以是字符串,用于字符串是否与预期的一致等。
判断字符串是否相等 不能用 -eq 要用: = 或 ==号:
if [ “var"="var2“ ] ;then
….
fi
4、shell的循环
(1)for循环
语法格式:
格式1:
for 变量 in 列表值1 列表值2 ... 列表n值 do 语句1 语句2 ... 语句n done
示例:看看下面两种循环条件的写法,保证 in后面的是一组有值的变量或常量。
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash read -p "input your number1 :" number1 read -p "input your number2 :" number2 for var in $(seq $number1 $number2) do echo "now is $var" done for var in {1..3} do echo "now $var" done zhugeling@newbie-unknown85879:~$ sh test.sh input your number1 :1 input your number2 :4 now is 1 now is 2 now is 3 now is 4 now 1 now 2 now 3
格式2:类似C语言格式
for ((初始值;判断条件;步长值))
do 语句1 语句2 ... 语句n done
示例:
#!/bin/bash
read -p "input your number1 :" number1 read -p "input your number2 :" number2 for((i=$number1;i<$number2;i++)) do echo "now is $i" done zhugeling@newbie-unknown85879:~$ sh test.sh input your number1 :1 input your number2 :3 now is 1 now is 2
(2)、while循环
格式1:
while [ $num -le $num2 ] ------两个变量之间 do 语句1 语句2 ... 语句n num =$((num+1)) ----------变量自加方式1 done
格式2:
while [ $num -le $num2 ] ------两个变量之间 do 语句1 语句2 ... 语句n num=` expr $num + 1` ---变量自加方式2 done
示例:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash read -p "input your number1 :" number1 read -p "input your number2 :" number2 i=$number1 #while [ $i -le $number2 ] while (($i<$number2)) do echo "now is $i" i=$(($i+1)) done zhugeling@newbie-unknown85879:~$ sh test.sh input your number1 :1 input your number2 :3 now is 1 now is 2
(3)、循环的常见用处:
按文件列表循环:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash read -p "inputh your dir:" dir for var in `ls $dir` do echo "file is $var" done zhugeling@newbie-unknown85879:~$ sh test.sh inputh your dir:/home/zhugeling/tmp file is test1.txt file is test2.txt file is test3.txt
批量创建用户或文件(这里以创建文件示例):
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash dir=/home/zhugeling/tmp cd $dir for var in {1..10} do touch filename$var done zhugeling@newbie-unknown85879:~$ sh test.sh zhugeling@newbie-unknown85879:~$ cd tmp/ zhugeling@newbie-unknown85879:~/tmp$ ls filename1 filename10 filename2 filename3 filename4 filename5 filename6 filename7 filename8 filename9
循环检测一个IP段的连通性:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash for var in {1..20} do ping -c2 10.83.3.$var &>/dev/null if [ $? -eq 0 ];then echo "10.83.3.$var is exist !" else echo "10.83.3.$var is not exist !" fi done zhugeling@newbie-unknown85879:~$ sh test.sh 10.83.3.1 is exist ! 10.83.3.2 is not exist ! 10.83.3.3 is not exist ! 10.83.3.4 is not exist ! 10.83.3.5 is not exist ! 10.83.3.6 is not exist ! 10.83.3.7 is not exist ! 10.83.3.8 is not exist ! 10.83.3.9 is not exist ! 10.83.3.10 is exist ! 10.83.3.11 is exist ! 10.83.3.12 is exist ! 10.83.3.13 is exist ! 10.83.3.14 is exist ! 10.83.3.15 is exist ! 10.83.3.16 is exist ! 10.83.3.17 is exist ! 10.83.3.18 is exist ! 10.83.3.19 is exist ! 10.83.3.20 is exist !
此外循环还可以用于统计数值,循环执行某些命令等用途。
5、shell的分支case
shell的分支是用case语句控制,语法如下:
case 值 in 模式1) 命令1 ... ;; 模式2) 命令2 ... ;; ...... esac
简单示例:
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash case $1 in start ) echo "server is starting !" ;; stop ) echo "server is stopping !" ;; restart) echo "server is restarting !" ;; *) echo "you input is error,please try again!" ;; esac zhugeling@newbie-unknown85879:~$ sh test.sh stop server is stopping ! zhugeling@newbie-unknown85879:~$ sh test.sh you input is error,please try again! zhugeling@newbie-unknown85879:~$ sh test.sh start server is starting !
加参数选项的示例:
#!/bin/bash
while getopts :b:d: OPT &> /dev/null; do case $OPT in b) echo "is b" echo $OPTARG ;; d) echo "is d" echo $OPTARG ;; *) echo "error " exit 7 ;; esac done zhugeling@newbie-unknown85879:~$ sh getopt.sh -d dd is d dd zhugeling@newbie-unknown85879:~$ sh getopt.sh -b hello is b hello
6、shell的函数
几乎所有语言都会有函数,shell也不例外,下面看看函数的语法:
格式:
function functionname ()
{
body;
}
调用方式:直接函数名调用
functionname
示例1:我将之前的用于case语句的实例,改写成用函数实现看看
zhugeling@newbie-unknown85879:~$ cat test.sh #!/bin/bash function stop () { echo "your chiose is stop ,stopping !" } function start () { echo "your chiose is start,startting !" } function restart () { echo "your chiose is restart,restartting !" } if [ "$1" = "stop" ];then stop elif [ "$1" = "start" ];then start elif [ "$1" = "restart" ];then restart else echo "your input is error ,try again !" fi zhugeling@newbie-unknown85879:~$ sh test.sh your input is error ,try again ! zhugeling@newbie-unknown85879:~$ sh test.sh stop your chiose is stop ,stopping ! zhugeling@newbie-unknown85879:~$ sh test.sh start your chiose is start,startting ! zhugeling@newbie-unknown85879:~$ sh test.sh restart your chiose is restart,restartting ! zhugeling@newbie-unknown85879:~$
这里注意对比字符串时,if里面的变量要双引号括起来,否则会报错,== 和 =在这里是等价的。
四、shell的环境变量
1、环境变量有哪些,如何保存和生效?
(1)环境变量有很多,按不同的作用域分有系统环境变量如path,用户环境变量如用户自定义的家目录,或者命令别名,应用环境变量如jdk环境变量。
如何查看:直接输入 env
查看某个环境变量:如查看path: echo $PATH
(2)按作用时间来看有临时环境变量,有永久环境变量:
比如我们的$HOME是一个永久环境变量,我们要临时修改它,直接export 一下:
zhugeling@newbie-unknown85879:~$ echo $HOME ---修改前 /home/zhugeling zhugeling@newbie-unknown85879:~$ export HOME='/home/tmp' zhugeling@newbie-unknown85879:/home/zhugeling$ zhugeling@newbie-unknown85879:/home/zhugeling$ echo $HOME ---修改后 /home/tmp
说明这种事临时生效,系统重启,或重新打开一个shell终端是会失效的。
环境变量永久生效的办法:直接修改配置文件或将修改保存到配置文件
(3)、常见环境变量的配置文件有哪些?
系统全局作用:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
生效: source 路径
对当前用户起作用:
~/.bash_profile
~/..bashrc
生效: source 路径
对应用本身起作用的(不同应用配置文件名和路径不一样):
/etc/ssh/sshd_config
/etc/ssh/ssh_config
生效:一般是重启应用或reload
2、shell的ps1/ps2/ps3有什么用:
(1)ps1:
先来个例子看看是什么作用的
zhugeling@newbie-unknown85880:~/tmp$ export PS1='\t >' 20:24:06 > 20:24:07 >
说明:从上例可以看到ps1可以通过export方式改变系统的终端提示符,ps1就是命令行提示符的环境变量。上面通过export改变环境变量的值,将显示结果改成时间而达到的效果。它还有更多其他参数如:
\a搜索 显示系统日期,格式:星期 日期 例:PS1="\d >" 结果:六 10月 24 > \A 显示系统时间,格式:HH:MM 例:PS1="\A >" 结果:21:04 > \t 显示系统时间,格式:HH:MM:SS (24小时制) 例:PS1="\t >" 结果:21:04:32 > \T 显示系统时间,格式:HH:MM:SS (12小时制) 例:PS1="\T >" 结果:09:04:32 > \h 显示主机名称(简称) 例:PS1="\h >" 结果:CentOS > \H 显示主机名称(全称)
默认值是:[\u@\h \W]$
刚刚只是临时修改,如果要长久生效怎么办?
放在个人家目录下的 .bashrc吧
如:
export PS1=’\t\h’
如果要全局生效呢?
我们来试试,我修改了/etc/profile的文件,后面追加 export PS1=’\t\h’,source之后,root用户立马生效,但是登录其他用户发现没效果。
排查发现用户下的.bashrc是有对PS1设置的:
if [ "$color_prompt" = yes ]; then PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' else PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' fi unset color_prompt force_color_prompt
经查资料,原来是登录读取配置文件的先后顺序也有关,最后读取的当然是最终起作用了:
/etc/profile ->.bash_profile -> .bashrc -> /etc/bashrc
那我们改 /etc/bashrc 试试:
root@newbie-unknown85880:~# source /etc/bash.bashrc 20:49:05newbie-unknown85880: 20:49:06newbie-unknown85880: 20:49:06newbie-unknown85880: 20:49:06newbie-unknown85880:tail -1 /etc/bash.bashrc export PS1='\t\h:'
果然如此。
Shell还有PS2,PS3,ps2是Shell的次提示符,PS3是Shell脚本中使用select时的提示符
举例:
默认显示如下:
zhugeling@newbie-unknown85881:~$ sh test.sh 1) mon 2) tue 3) wed 4) exit #? 1 Monday #? 2 Tuesday #? 3 Wednesday #? ^C
修改PS3=’\tmyshell’:
zhugeling@newbie-unknown85881:~$ export PS3='\tmysell:' zhugeling@newbie-unknown85881:~$ sh test.sh 1) mon 2) tue 3) wed 4) exit \tmysell:1 Monday \tmysell:2 Tuesday \tmysell:
其他的生效顺序,长久生效配置跟ps1是一样的。
3、登录方式不一样对环境变量的影响:
有时候我们会碰到,用不同的用户去启动一个应用时会报错,除了文件权限不一样之外,通常还有另外一个原因就是不同用户的环境便令不一样。
(1)登录shell和非登shel:
登录shell:是通过账号密码登录进入shell的(或者通过”–login”选项生成的shel)
非登录shell:不需要账号密码,直接通过bash命令或者桌面右键打开的终端。
root@newbie-unknown85879:/etc/ssh# echo $$ 26497 root@newbie-unknown85879:/etc/ssh# bash root@newbie-unknown85879:/etc/ssh# echo $$ ---shell的进程ID跟之前不一样 30154 root@newbie-unknown85879:/etc/ssh# exit exit
正常登录文件的读取调用关系:
执行/etc/profile中的命令,然后/etc/profile调用/etc/profile.d目录下的所有脚本;然后执行~/.bash_profile,~/.bash_profile调用~/.bashrc,最后~/.bashrc又调用/etc/bashrc
非登录shell:
执行~/.bashrc,~/.bashrc调用/etc/bashrc,然后调用/etc/profile.d/*下的所有脚本。
区别:非登录shell不会重新加载/etc/profile,而且顺序也不一样,如果你的环境变量设置是在/etc/profile和~/.bash_profile ,建议重新登录或source一下对应的配置文件。
登录方式:su user 和 su - user的区别:
su user :只是切换了用户,切换了权限,但是环境变量没变。
su - user : 切换了用户,切换了权限,也切换了环境变量
zhugeling@newbie-unknown85879:/home/zhugeling$ su root Password: root@newbie-unknown85879:/home/zhugeling# ---目录没换 zhugeling@newbie-unknown85879:/home/zhugeling$ su - root Password: root@newbie-unknown85879:~# ---目录换了
(2)交互式shell和非交互式shell:
我们把命令行终端这种输入一个命令,shell、去执行,然后输出结果,这种叫交互式shell,非交互式shell是指shell自己一直执行到执行完就退出shell的模式,脚本执行就是典型的非交互式shell。
我们可以通过echo $- 来判断当前是什么类型的shell:
root@newbie-unknown85879:/etc/ssh# echo $- --交互式 himBHs root@newbie-unknown85879:/etc/ssh# cat test.sh echo $- root@newbie-unknown85879:/etc/ssh# sh test.sh ---非交互式 hB
在环境变量上的区别:
交互式shell:就是当前登录用户的环境变量(根据登录方式不同也会有区别)
非交互式shell:继承当前登录用户的环境变量,在此基础上shell可以在脚本里加载其他环境变量,如通过export PATH=’..’ 等
五、正则表达式和通配符的区别
(1)、作用对象和范围不一样:
作用对象:
通配符用于Linux的shell命令(如文件名相关操作),如常结合find ,cp,mv等命令使用
正字表达是是用于文本字符串或者标准输出里的内容的过滤和匹配
作用范围:
通配符必须在unix环境或类Unix环境中使用
正则表达式在很多语言中都支持,是一种标准,可以写在代码中,跨平台性好。
(2)解释器不一样:
通配符是Linux系统本身支持的,由shell去解析执行。
正则表达式在使用过程中有可能是shell去解析,也有可能是awk,perl等支持正则的命令去解析,就看使用方式是怎么用。
如:
zhugeling@newbie-unknown85879:/home/zhugeling$ ls -l logrotate.sh_* |grep "*" ---这里前面*号是通配符,后面*号只是一个*号 -rw-r--r-- 1 zhugeling zhugeling 1628 Feb 1 12:13 logrotate.sh_v* zhugeling@newbie-unknown85879:/home/zhugeling$ ls -l logrotate.sh_* ---这里*号是通配符号 -rw-r--r-- 1 zhugeling zhugeling 1628 Feb 1 12:13 logrotate.sh_v* -rw-r--r-- 1 zhugeling zhugeling 969 Jan 30 20:34 logrotate.sh_v1 -rw-r--r-- 1 zhugeling zhugeling 1940 Jan 31 11:20 logrotate.sh_v2 -rw-r--r-- 1 zhugeling zhugeling 1628 Jan 31 12:11 logrotate.sh_v3 zhugeling@newbie-unknown85879:/home/zhugeling$ ls -l logrotate.sh_* |grep 'v*' --这里*号是正则修饰符 -rw-r--r-- 1 zhugeling zhugeling 1628 Feb 1 12:13 logrotate.sh_v* -rw-r--r-- 1 zhugeling zhugeling 969 Jan 30 20:34 logrotate.sh_v1 -rw-r--r-- 1 zhugeling zhugeling 1940 Jan 31 11:20 logrotate.sh_v2 -rw-r--r-- 1 zhugeling zhugeling