资讯专栏INFORMATION COLUMN

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

bovenson / 2747人阅读

摘要:清空文件有时我们需要清空一个现有的文件使用输出重定向使用输入重定向也可以使用命令清空文件通常使用第一种方法即可,比较简单易懂。

导读

之前我们也偶尔接触过读写文件的方法,本篇会系统讲读写文件的各种方法。

写文件

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

% echo abc > test.txt

如果不想覆盖之前的文件内容,可以追加写入:

% echo abc >> test.txt

这样如果文件存在,内容会被追加写入进去;如果文件不存在,也会被创建。

创建文件

有时我们只想先创建个文件,等以后需要的时候再写入。

touch 命令用于创建文件(普通文件):

% touch test1.txt test2.txt

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

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

如果文件已经存在,touch 命令会更新它的时间(mtime、ctime、atime 一起更新,其余两种方法不会)到当前时间。另外下边的清空文件方法,也都可以用来创建文件。touch 命令的使用比较方便,但如果想尽量少依赖外部命令,可以使用后两种方法。

因为文件创建过程通常不存在性能瓶颈,不用过多考虑性能因素。如果需要创建大量文件,可以在自己的环境分别用这几种方法试验几次,看需要多少时间。

我在树莓派 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 
# 运行了几次,结果差不多
% 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

另外如果文件数量太多的话,方法二、三要按批次创建,因为一个进程能打开的 fd 总数是有上限的。

清空文件

有时我们需要清空一个现有的文件:

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

# 使用输入重定向
% >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+="
cd"
% str +="
$str"

echo $str > test.txt

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

% array=(aa bb cc)

% print -l $array > test.txt

如果是将一段内容比较固定的字符串写入到文件,可以这样:

# 在脚本中也是如此,第二行以后的行首 > 代表换行,非输入内容
# < test.txt < 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=$(

按行遍历文件

如果文件比较大,那读取整个文件会消耗很多资源,可以按行遍历文件内容:

% while {read i} {
> echo $i
> } 

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

读取指定行

如果只需要读取指定的某行或者某些行,不需要用上边的方法加自己计数。

# (f)2 是读取第二行
% echo ${"$(

读取文件到数组

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

% array=(${(f)"$(

读取指定数量的字符

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

% cat test.txt
1234567890
# -k5 是只最多读取 5 个字节,-u 0 是从 fd 0 读取,不然会卡住
% read -k 5 -u 0 str 

向文件中间插入内容

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

Zsh 并没有直接提供这样的功能,但我们可以迂回实现。

% echo -n 1234567890 > test.txt
# 先全部读进来
% str=$(> test.txt
# 将后半部分文件追加回去
% echo -n $str[6,-1] >> test.txt
% cat test.txt
12345abcde67890

但如果比较比较大的话,就不能将整个文件全部读进来,可以先在循环里用 read -k num 一次读固定数量的字符,然后写入一个中间文件,然后再 truncate 原文件,插入内容。最后再 cat 中间文件 >> 原文件 追加原来的后半部分内容即可。

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

总结

本文比较详细地介绍了各种读写文件的方法,基本可以覆盖常用的场景。

本文不再更新,全系列文章在此更新维护:github.com/goreliu/zshguide

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/10312.html

相关文章

  • Zsh 开发指南第十九篇 脚本实例讲解)

    摘要:导读本文将讲解一些比较简单的脚本实例。使用哈希表判断是否重复,删除哈希表里已经有的后续文件。思路建一个哈希表存放汉字与数字的对应关系。如果当前运行进程数达到最大进程并发数,则循环检查哈希表里的进程是否退出。 导读 本文将讲解一些比较简单的 zsh 脚本实例。 实例一:复制一个目录的目录结构 功能: 将一个目录及它下边的所有目录复制到另一个目录中(即创建同名目录),但不复制目录下的其他类...

    khlbat 评论0 收藏0
  • SpringBoot非官方教程 | 第十四篇:在springboot中用redis实现消息队列

    摘要:环境依赖创建一个新的工程,在其文件加入依赖创建一个消息接收者类,它是一个普通的类,需要注入到中。 这篇文章主要讲述如何在springboot中用reids实现消息队列。 准备阶段 安装redis,可参考我的另一篇文章,5分钟带你入门Redis。 java 1.8 maven 3.0 idea 环境依赖 创建一个新的springboot工程,在其pom文件,加入spring-boot-...

    APICloud 评论0 收藏0
  • Zsh 开发指南第十八篇 更多内置模块的用法)

    摘要:而且某些时候,我们可能需要操作一个巨大的哈希表,并不能全部将它载入到内存中。是一个很轻量的数据库,可以认为它就像一个保存在文件里的哈希表。的模块用于调度命令的运行。总结本文介绍了几个比较常用的内置模块,以后可能继续补充更多模块的用法。 导读 除了 zsh/mathfunc、zsh/net/socket、zsh/net/tcp,zsh 还内置了一些其他的内置模块。本文简单讲几个比较常用的...

    dkzwm 评论0 收藏0
  • linux

    摘要:包括转义字符引号的使用等等。使用隐写术的指纹文件隐写术是将消息隐藏到不被认为会出现的地方的做法。在很好地执行了隐写术的片段中,任何不是预期接收者的人都能看到消息,但是却完全无法意识到消息就在那儿。 CentOS 7 时区、日期时间查看以及设置杂烩 CentOS 7 中时区、日期时间的查看、设置。包括网络时间协议 NTP 的安装和同步,Timedatectl 命令查看当前的时区、日期日期...

    lijinke666 评论0 收藏0
  • 阿里P7程序员晒工资条,看完扎心了……

    摘要:前几天,有位老粉私信我,说看到某后学弟晒出阿里的工资单,他是真酸了想狠补下技术,努力冲一把大厂。为了帮到他,也为了大家能在最短的时间内做面试复习,我把面试系列都汇总在这一篇文章了。 前几天,有位老粉私信我,说看到某95后学弟晒出阿里P7的工资单,他是真酸了......想狠补下技术,努力冲一把...

    Jochen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<