Zsh 开发指南(第十四篇 文件读写)

导读

以前咱们也偶尔接触过读写文件的方法,本篇会系统讲读写文件的各类方法。git

写文件

写文件要比读文件简单一些,最经常使用的用法是使用 > 直接将命令的输出重定向到文件。若是文件存在,内容会被覆盖;若是文件不存在,会被建立。github

% echo abc > test.txt复制代码

若是不想覆盖以前的文件内容,能够追加写入:shell

% echo abc >> test.txt复制代码

这样若是文件存在,内容会被追加写入进去;若是文件不存在,也会被建立。数组

建立文件

有时咱们只想先建立个文件,等之后须要的时候再写入。bash

touch 命令用于建立文件(普通文件):微信

% touch test1.txt test2.txt

# 或者用 echo 输出重定向,效果和 touch 同样
# 加 -n 是由于不加的话 echo 会输出一个换行符
% echo -n >>test1.txt >>test2.txt

# 或者使用输入重定向
% >>test1.txt >>test2.txt </dev/null

# mkdir 用来建立目录,若是须要在新目录建立文件
% mkdir dir1 dir2复制代码

若是文件已经存在,touch 命令会更新它的时间(mtime、ctime、atime 一块儿更新,其他两种方法不会)到当前时间。另外下边的清空文件方法,也均可以用来建立文件。touch 命令的使用比较方便,但若是想尽可能少依赖外部命令,可使用后两种方法。ide

由于文件建立过程一般不存在性能瓶颈,不用过多考虑性能因素。若是须要建立大量文件,能够在本身的环境分别用这几种方法试验几回,看须要多少时间。性能

我在树莓派 3B 简单测试一下:测试

# 三个脚本,分别建立 1000 个文件
% cat test1 test2 test3
#!/bin/zsh 
touch test1{1..1000}.txt
#!/bin/zsh 
echo -n >>test2{1..1000}.txt
#!/bin/zsh 
>>test3{1..1000}.txt </dev/null复制代码
# 运行了几回,结果差很少
% time ./test1; time ./test2; time ./test3
./test1  0.02s user 0.03s system 86% cpu 0.058 total
./test2  0.02s user 0.02s system 70% cpu 0.056 total
./test3  0.03s user 0.01s system 72% cpu 0.055 total复制代码

另外若是文件数量太多的话,方法2、三要按批次建立,由于一个进程能打开的 fd 总数是有上限的。ui

清空文件

有时咱们须要清空一个现有的文件:

# 使用 echo 输出重定向
% echo -n >test.txt

# 使用输入重定向
% >test.txt </dev/null

# 也可使用 truncate 命令清空文件
% truncate -s 0 test.txt复制代码

一般使用第一种方法便可,比较简单易懂。非特殊场景尽可能不要用像 truncate 这样不常见的命令。

删除文件

删除文件的方法比较单一,用 rm 命令便可。

% rm test1.txt test2.txt

# -f 参数表明即便文件不存在也不报错
% rm -f test1.txt test2.txt

# -r 参数能够递归删除目录和文件
% rm -r dir1 dir2 test*.txt

# -v 参数表明 rm 会输出删除文件的过程
% rm -v test*.txt
removed 'test1.txt'
removed 'test2.txt'复制代码

删除文件必须借助 rm 命令。若是必定要不依赖外部命令的话,zsh/files 模块里也有一个 rm 命令,能够用 zmodload zsh/files 加载,而后 rm 就变成了内部命令,用法基本相同。

% zmodload zsh/files
% which -a rm
rm: shell built-in command
/usr/bin/rm复制代码

此外 zsh/files 中还有内置的 chgrp、chown、ln、mkdir、mv、rmdir、sync 命令。若是不想依赖外部命令,或者系统环境出问题了用不了外部命令,可使用这些。这能够做为命令不存在或者由于命令自己问题执行异常的一个 fallback 方案,来提升脚本的健壮性。

多行文本写入

一般咱们写文件时不会每一行都单独写入,这样效率过低。

能够先把字符串拼接起来,而后一次性写入,这样比屡次写入效率更高:

% str=ab
% str+="\ncd"
% str +="\n$str"

echo $str > test.txt复制代码

能够直接把数组写入到文件,每行一个元素:

% array=(aa bb cc)

% print -l $array > test.txt复制代码

若是是将一段内容比较固定的字符串写入到文件,能够这样:

# 在脚本中也是如此,第二行之后的行首 > 表明换行,非输入内容
# <<EOF 表明遇到 EOF 时会终止输入内容
# 里边也可使用变量
% > test.txt <<EOF
> aa
> bb
> cc dd
> ee
> EOF

% cat test.txt
aa
bb
cc dd
ee复制代码

用 mapfile 读写文件

若是不喜欢使用重定向符号,还能够用哈希表来操做文件。Zsh 有一个 zsh/mapfile 模块,用起来很方便:

% zmodload zsh/mapfile

# 这样就能够建立文件并写入内容,若是文件存在则会被覆盖
% mapfile[test.txt]="ab cd"
% cat test.txt
ab cd

# 判断文件是否存在
% (($+mapfile[test.txt])) && echo good
good

# 读取文件
% echo $mapfile[test.txt]
ab cd

# 删除文件
% unset "mapfile[test.txt]"

# 遍历文件
% for i (${(k)mapfile}) {
> echo $i
> }
test1.txt
test2.txt复制代码

从文件中间位置写入

有时咱们须要从一个文件的中间位置(好比从第 100 的字符或者第三行开始)继续写入,覆盖以后的内容。Zsh 并不直接提供这样的方法,但咱们能够迂回实现,先用 truncate 命令把文件截断,而后追加写。若是文件后边的内容还须要保留,能够在截断以前先读取进来(见下文读文件部分的例子),最后再写回去。

% echo 1234567890 > test.txt
# 只保留前 5 个字符
% truncate -s 5 test.txt
% cat test.txt
12345 
% echo abcde >> test.txt
% cat test.txt
12345abcde复制代码

读文件

读取整个文件

读取整个文件比较容易:

% str=$(<test.txt)
% echo $str
aa
bb
cc dd
ee复制代码

按行遍历文件

若是文件比较大,那读取整个文件会消耗不少资源,能够按行遍历文件内容:

% while {read i} {
> echo $i
> } <test.txt
aa
bb
cc dd
ee复制代码

read 命令是从标准输入读取一行内容,把标准输入重定向后,就变成了从文件读取。

读取指定行

若是只须要读取指定的某行或者某些行,不须要用上边的方法加本身计数。

# (f)2 是读取第二行
% echo ${"$(<test.txt)"[(f)2]}
bb复制代码

读取文件到数组

读取文件内容到数组中,每行是数组的一个元素:

% array=(${(f)"$(<test.txt)"})复制代码

读取指定数量的字符

有时咱们须要按字节数来读取文件内容,而不是按行读取。

% cat test.txt
1234567890
# -k5 是只最多读取 5 个字节,-u 0 是从 fd 0 读取,否则会卡住
% read -k 5 -u 0 str <test.txt
% echo $str
12345复制代码

向文件中间插入内容

有时咱们会遇到比较麻烦的场景,在某个文件中间插入一些内容,而先后的内容保持不变。

Zsh 并无直接提供这样的功能,但咱们能够迂回实现。

% echo -n 1234567890 > test.txt
# 先所有读进来
% str=$(<test.txt)
# 截断文件
% truncate -s 5 test.txt
# 插入内容
% echo -n abcde >> test.txt
# 将后半部分文件追加回去
% echo -n $str[6,-1] >> test.txt
% cat test.txt
12345abcde67890复制代码

但若是比较比较大的话,就不能将整个文件所有读进来,能够先在循环里用 read -k num 一次读固定数量的字符,而后写入一个中间文件,而后再 truncate 原文件,插入内容。最后再 cat 中间文件 >> 原文件 追加原来的后半部份内容便可。

另外这种从文件中间写入或者读取内容的场景,均可以使用 dd 命令实现,能够自行搜索 dd 命令的用法。

总结

本文比较详细地介绍了各类读写文件的方法,基本能够覆盖经常使用的场景。

全系列文章地址:github.com/goreliu/zsh…

付费解决 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等领域相关问题,灵活订价,欢迎咨询,微信 ly50247。

相关文章
相关标签/搜索