Linuxshell学习笔记(6)

文件查找和批量处理

寻找满足特定条件的文件路径,简称文件查找,是 shell 脚本的常见任务,因为条件复杂多样,这样的任务并不轻松。很多人使用 find 命令来做,但 find 只能覆盖一部分功能,其他的要自己进一步处理,而且 find 并不好用,和脚本其他部分配合也比较麻烦,容易出错。用 zsh 的话,基本不需要 find 命令,借助 zsh 自身的功能便足以应付多数场景,而且语法更优雅简洁不易出错。

简单例子

列出 /usr/bin 目录下以 zsh 开头的文件。

如果用 ls 的话,就平添了不少额外工作,因为 zsh* 已经匹配一次文件路径,结果出来了,传给 ls 后,ls 又去 stat 了一下那些文件,而这完全是多余工作,如果文件列表长的话,要多消耗不少时间。 

按文件属性查找

除了匹配文件路径外,很多时候我们还需要按文件属性查找,比如根据文件类型、权限、大小、修改时间等等。这里需要使用一个新东西,通配符修饰语。

ls -l 结果中第一位是 - 的文件,非目录、符号链接、设备文件、socket、FIFO 等等;

这个后面多的(.)便是通配符修饰语,专门用于按文件属性来匹配文件。点(.)代表普通文件。 

记:(.):列出当前目录下的普通文件

        (/F):列出当前目录下的非空目录,F 是 FULL,满的意思;

        (/^F):列出当前目录下的空目录,^ 是取反;

        (@,.x):列出当前目录下的符号链接文件和可执行的普通文件;

        (./fxxxx):列出符合xxxx权限的普通文件。

通配符修饰列表语

名称含义使用样例或补充说明
/目录
F非空/F(非空目录) /^F(空目录)
.普通文件
@符号链接
=socket 文件
pFIFO 文件
*可执行的普通文件
%设备文件
%b块设备文件
%c字符设备文件
r文件拥有着有读权限
w文件拥有着有写权限
x文件拥有着有执行权限
A文件拥有组用户有读权限
I文件拥有组用户有写权限
E文件拥有组用户有执行权限
R任何用户都有读权限
W任何用户都有写权限
X任何用户都有执行权限
s设置了 setuid 的文件
S设置了 setgid 的文件
t设置了粘滞位(sticky bit)的文件
f符合指定的权限f0644 f4755 f700
e暂无
+暂无
d指定设备号
l硬连接个数l-2(小于 2) l+3(大于 3)
U当前用户拥有
G当前用户所在组拥有
u指定用户 id 拥有u1000
g指定用户组 id 拥有g1000
a指定文件的 atime下文有说明
m指定文件的 mtime下文有说明
c指定文件的 ctime下文有说明
L指定文件大小下文有说明
^取反/^F
-暂无
M暂无
T暂无
N如果没匹配到,返回空而不报错
D包含隐藏文件(. 开头)
n按数值大小排序下文有说明
o递增排序下文有说明
O递减排序下文有说明
[n]只取第 n 个文件.[5]
[n1,n2]取第 n1 到 n2 个文件/[5,10]
:X暂无

更复杂的用法

按文件时间查找文件
# 列出最近一天修改过内容的文件
% print -l *(.m-1)

# 列出最近一个月没有读取过的文件
% print -l *(.aM+1)

m 后边可加单位,如果没有单位,默认是天。其他单位:M(月)、w(周)、h(小时)、m(分钟)、s(秒)。+ 是指定时间之前,- 是指定时间之内。

a 是最后访问时间(atime),但注意如果分区挂载时指定了 noatime 或者 realtime(可以查看 /proc/mount 确认),那么 atime 并不是真正的最后访问时间。m 是最后修改时间(mtime),这里指内容修改,而不包括文件属性(如权限)的修改。c 是最后状态修改时间(ctime),如果文件内容没有修改,而文件属性发生变化,这个时间会更新。

按文件大小查找文件
# 列出当前目录下所有空文件
% print -l *(.L0)

# 列出当前目录下小于 2k 的文件
% print -l *(.Lk-2)

# 列出当前目录下大于 1m 的文件
% print -l *(.Lm+1)

# 注意这样只能找到空文件,因为以 m 为单位的话,文件只能是 0 m 或者 1 m,不能 0.5 m
# 所以比 1 小就是 0 m,是空文件
% print -l *(.Lm-1)

默认的单位是字节,还可以使用 k、m 和 p(512 字节的块),也可以使用大写的 K、M、P,含义一样。

文件排序
# 按文件名排序,同一目录下的文件和目录名会一起排,而不是先排目录再排文件
% print -l **/*(.on)
bb.txt
cc/aa.txt
cc/dd.txt
zz.txt

# 按文件的目录深度逆序排,d 是从深往浅排,O 是逆序
% print -l **/*(.Od)
zz.txt
bb.txt
cc/dd.txt
cc/aa.txt

# 先按文件名排序,然后再按大小排序,这样大小相同的文件依然是按文件名排的
% print -l **/*(.onoL)
bb.txt
cc/aa.txt
cc/dd.txt
cc.txt

像第三个例子那样,可以排多次。

d 是从深往浅排,O 是逆序,先按文件名排序,然后再按大小排序;

可供排序的因素:n(文件名,如果不指定排序选项,默认按文件名排,即 on)、L(大小)、l(硬连接数)、a(atime)、m(mtime)、c(ctime)、d(所在目录深度,从深到浅排)。

组合使用

类型和类型之间要用逗号隔开,如果不指定类型,代表所有类型都可以,逗号前后的内容互不干扰(取反 ^ 操作只影响到逗号之前内容)。同一个类型可以同时加多个选项,依次添加即可。

# 当前目录下的两天内修改过的目录
# 加上小于 3 m 的普通文件从小到大排
# 再加上所有的符号链接文件(包括隐藏文件)
% print -l *(/m-2,.Lm-3oL,@D)

文件批量重命名

对文件进行批量重命名,是一个比较常见的场景。Zsh 中有一个非常方便的命令 zmv,它可以让批量重命名变得很简单。

# 使用前需要先加载进来
% autoload -U zmv

# 将所有 txt 文件扩展名改成 conf
# 参数要用单引号扩起来,$1 代表第一个参数中括号中的内容
%  zmv '(*).txt' '$1.conf'

# 如果加了 -W 参数,zmv 会自动识别文件名中需要保留的部分
%  zmv -W '*.txt' '*.conf'

# 调整文件名各部分的前后顺序
% zmv '(*).(*).txt' '$2.$1.txt'
# 加 -n 预览而不实际运行
% zmv -n '(*).(*).txt' '$2.$1.txt'
mv -- a.b.txt b.a.txt

# 0 1 2 ... 前添加 0,以便和 10 11 12 ... 宽度一致
% zmv '([0-9]).(*)' '0$1.$2'
# 去掉开头的一个 0
% zmv '(0)(*)' '$2'

# 文件整理到目录
% zmv '(*) - (*) - (*).txt' '$1/$2 - $3.txt'

# 转换大小写
% zmv '(*).txt' '${(U)1}.txt'
% zmv '(*).txt' '${(L)1}.txt'

不展开通配符 

有时我们不想展开通配符,比如我写了一个计算的函数叫做 calc:

calc() {
    zmodload zsh/mathfunc
    echo $(($*))
}

% calc 12+12
24

但如果我想计算 12 * 12:

% calc 12*12
zsh: no matches found: 12*12

如果不加引号的话,星号会被作为通配符使用,然后去找符合 12*12 的文件名,没找到所有报错了。但我并不想找文件。

noglob 命令可以禁止展开后边内容的通配符,这样就不需要加引号了。

% noglob calc 12*12
144

然后可以写个 alias:

% alias js="noglob calc"
% js 12*12
144

变量的进阶内容 

typeset 命令

typeset 命令用于对变量进行详细的设置。typeset -A 可以用来定义哈希表。

% typeset -A table=(aa bb cc dd)

但我们后续都使用 local,因为 local 的功能和 typeset 是一样的(除了不能用 -f 和 -g,这两个选项不常用),并且更短更容易输入。

强制字符串内容为小写或者大写

# 强制字符串内容为小写
% local -l str=abcABC && echo $str
abcabc

# 强制字符串内容为大写
% local -u str=abcABC && echo $str
ABCABC

 

设置变量为环境变量  export

% local -x str=abc
# 通常使用 export,功能一样
% export str=abc

环境变量可以被子进程读取。

设置变量为只读变量  readonly

% local -r str1=abc
# 通常使用 readonly,功能一样
% readonly str2=abc

% str1=bcd
zsh: read-only variable: str1
% str2=bcd
zsh: read-only variable: str2

设置数组不包含重复元素   -U

% local -U array=(aa bb aa cc) && echo $array
aa bb cc

设置整数的位数  -Z  n(n为位数)

# 如果位数不够,输出内容会用 0 补全
% local -Z 3 i=5 && echo $i
005

# 如果超出范围会被截断
% local -Z 3 i=1234 && echo $i
234

进制转换  -i n(n为进制数)

设置整数为其他进制显示:

% local -i 16 i=255
% echo $i
16#FF

可以设置 2 到 36 之间任意进制。设置几进制显示,并不影响计算,只是显示格式不同。

[#n] num 也可以显示十进制数为 n 进制:

% echo $(([#16] 255))
16#FF

可以用 n#num 来显示 n 进制整数为十进制:

% echo $((16#ff))
255

我们可以定义一系列函数来快捷地转换进制,不需要使用 bc 等外部命令:

0x() {
    echo $((16#$1))
}

0o() {
    echo $((8#$1))
}

0b() {
    echo $((2#$1))
}

p16() {
    echo $(([#16] $1))
}

p8() {
    echo $(([#8] $1))
}

p2() {
    echo $(([#2] $1))
}


# 其他进制转十进制
% 0x ff
255
% 0b 1101
13

# 十进制转其他进制
% p16 1234
16#4D2

同时对多个变量赋相同的值 {}

% local {i,j,k}=123
% echo $i $j $k
123 123 123

绑定字符串和数组 -T

% local -T DIR dir
% dir=(/a /b/c /b/d /e/f)
% echo $DIR
/a:/b/c:/b/d:/e/f

# 删除 dir 后,DIR 也会被删除(反之亦然)
% unset dir
% echo $+DIR
0

 Linux 下经常需要处理带分隔符冒号的字符串(比如 $PATH)。如果只修改其中某一个字段,比较麻烦。local -T 可以把字符串绑定到数组上,这样直接修改数组,字符串内容也会同步变化(反之亦然)。其实在 zsh 中,$PATH 字符串就是和 $path 数组绑定的,可以直接通过修改 $path 来达到修改 $PATH 的目的,这在某些场景会方便很多。

显示变量的定义方式 -p

% array=(aa bb cd)
% local -p array
typeset -a array=(aa bb cd)

% array+=(dd)
% local -p array
typeset -a array=(aa bb cd dd)

注:使用local时候发现,-Z、-U这种选项当你没有删除变量时再重新覆盖复制,选项不会变化!

什么地方该加双引号

用过 bash 的读者大概会对里边的双引号印象比较深刻,很多地方不加双引号都会出错,为了避免出错,很多人每个变量左右都加上双引号,麻烦不说,代码看起来也比较乱。

其实 zsh 中已经没有那些问题了,变量两边无需加双引号,不会出现莫名其妙的错误。但有些地方还是需要加双引号的。

需要加双引号的场景:

  1. 像这样的包含字符或者特殊符号的字符串 "aa bb \t \n *" 出现在代码中时,两边要加双引号,这个基本不需要说明。
  2. 在用 $() 调用命令时,如果希望结果按一个字符串处理,需要加上双引号,"$()",不然的话,如果命令结果中有空格,$() 会被展开成多个字符串。
  3. 如果想将数组当单个字符串处理,需要加双引号,array=(a b); print -l "$array"
  4. 其他的原本不是单个字符串的东西,需要转成单个字符串的场景,要加双引号。

其余情况通常都不需要加双引号,典型的情况:

  1. 任何情况下,字符串变量的两边都不需要加双引号,无论里边的内容多么特殊,或者变量存不存在,都没有关系,如 $str
  2. 如果不转换类型(比如数组转成字符串),任何变量的两边都不需要加双引号。
  3. $1 $2 $* 这些参数(其实它们也都是单个字符串),都不需要加双引号,无论内容是什么,或者参数是否存在。

以上的 7 种情况几乎覆盖了所有场景,如果有没覆盖到的,试一下即可(让里边的内容包含空格、换行和其他特殊字符等等,看看结果是否符合预期)。

  • 29
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值