#68 scp 拷贝文件引发的一次故障

2021-07-15

背景:有一个服务会定时读某个目录下的文件,然后逐个进行处理。

今天出于某些原因,我通过 scp 拷贝文件到那个目录下,结果意外的发现服务挂掉了,就是因为读到了一个空文件,抛出一个没有被处理的错误。

我知道这是程序设计上存在的一个缺陷,但之前我们一直通过 rsync 在传输文件,从来没有遇到过这样的问题。

我查了文档并进行了实验,确认了 rsync 会在文件传输时创建一个 .文件名.随机串 的临时文件,然后在传输完成后重命名。而 scp 则不会这样做,它会创建空文件,然后逐渐填充内容。

If the target file does not yet exist, an empty file with the target file name is created, then filled with the source file contents. No attempt is made at "near-atomic" transfer using temporary files.

我的理解是,文档里面说的 near-atomic 应该就是指 rsync 那种模式,而 atomic 的意思应该是指连临时文件都不用创建的模式。也不知道有什么文件系统支持这种模式。

#65 网卡名称的变迁(ethX -> enpXsY)

2021-03-15

早几年前,Linux 系统的网卡名称都是 eth0、wlan0,后来都变了个风格,就拿我的电脑举例:enp7s0, wlp6s0。
这到底是是什么原因呢?
最近突然好奇,去查了资料才知道为什么。
在 Fedora 的技术资料中找到,这个和 Systemd 有关,然后又在 freedesktop 官网 Systemd 的手册中找到了详细的说明。
现在的命名方案有好长一段,估计要看个半个小时(如果感兴趣可以仔细阅读一下),解开我的疑问却不需要那么复杂。

#61 高效搜索日志

2020-07-22

即便上了 ELK,也不可能所有日志往里面塞,搜日志是程序员的日常工作之一。

grep log by time efficiently 为关键词搜到 SO 上的这个问题: What is the most efficient way to extract logs between two time stamps?

要求:

  1. 能够过滤出指定时间段日志
  2. 效率,保证即便是大日志也不至于等太久,减少对服务的不良影响

提问的人自己提出来的方案:

grep -a -A 1000000 "03/09" fileName.txt | grep -a -B 1000000 "03/10"

他认为不好,因为有点慢,而且有时会漏掉一些日志。
漏掉日志可能和他的日志格式有关,这倒应该问题不大,主要是这个方案看起来就确实效率不行。
PS:如果不用模式匹配,可以加上 -F,应该可以提高效率。

# 2020/07/22 10:00:00,100 INFO [main] com.example.LoggingApplication - Starting Application
awk '$2>="16:30:00"{s=1} s; $2>="17:00:00"{exit}' event.log

这个看起来很牛逼,主要是有一个 exit

#60 Shell 转义

2020-07-21

这篇文章不是要记录那些乱七八糟的转义规则,而是讲怎么借助工具避开它。

GitHub 上偶然看到一个仓库 chrissimpkins/shellescape,做了些实验之后,发现非常好用。
别看就几行代码,真的感觉世界都清静了。
PS:代码我没细看,人生短暂,我不想去记这些没用的知识点。

import re

_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search

def quote(s):
    if not s:
        return "''"
    if _find_unsafe(s) is None:
        return s
    return "'" + s.replace("'", "'\"'\"'") + "'"

示例:

commands = """
grep -F '"userId": "1"' /tmp/test.log
grep 'aaa.bbb.$100.*' /tmp/test.log
""".strip().splitlines()
for command in commands:
    print(quote(command))

#59 Shell 小技巧

2020-07-20

快速输入上一行命令的最后一个参数

touch nihao.txt
code !$

创建目录并进入

take xxx # oh-my-zsh

mkdir xxx && cd $_

重命名

for i in {0..100}; do touch aaa_bbb_$i.jpg; done
rename 's/_bbb//' aaa_*.jpg

# for zsh
autoload zmv
zmv -n '(*)_(*).jpg' '$2_$1.jpg'

#56 Shell 历史

2020-01-07

参考: https://en.wikipedia.org/wiki/Comparison_of_command_shells

Linux Shell

  • Thompson shell (sh), 1971, Unix v1 - v6
    只存在历史中
    在 Bourne shell 之前,贝尔实验室还出了一个 Mashey shell,只出现在 1976 年的 PWB UNIX 中(所以又叫 PWB shell),没有大范围使用
  • Bourne shell (sh), 1977, Unix v7
  • C shell (csh), 1978 img
  • TENEX C shell (tcsh), 1983 img
    是 TENEX 系统开发的 csh 兼容 Shell
  • Korn shell (ksh), 1983 img
  • Bash, 1989 img
  • Almquist shell (ash), 1989 img
  • zsh, 1990 img
  • Debian Almquist shell (dash), an ash fork, 1997
    主体随 ash 是 BSD 协议,不过,可能是为了保持对 bash 的兼容,采用了 bash 项目的一个文件
  • fish, 2005 img

分类

  • ksh, bash, zsh, ash, dash 都是 Bourne shell 兼容。
  • csh, tcsh 就是 csh 兼容。
  • fish

说明

  1. Unix 上的 sh 有版权问题, 所以 BSD 和 Linux 上的 /bin/sh 都指向某一种兼容 Shell (一般是默认 Shell)
  2. RHEL/CentOS 上,默认 Shell 是 Bash
  3. Debian/Ubuntu 上,默认 Shell 是 Dash
  4. FreeBSD 上默认采用 tcsh, 基于 FreeBSD 的 GhostBSD 采用 fish
  5. OpenBSD 上默认采用 pdksh (pd: Public Domain), 不知道和 tsh 什么关系。可能是 OpenBSD 维护的 tsh 兼容 Shell。
  6. NetBSD 上默认采用 ash
  7. 由于 ash 非常轻量级,BusyBox 采用了 dash fork
    然后 BusyBox 被 Alpine Linux, Tiny Core Linux 或者其他嵌入式 Linux (比如 OpenWrt) 采用,所以这些系统的默认 Shell 应该就是 ash

所以可能使用最广泛的 Shell 是 ash 和 bash。

其他:MacOS 上曾经默认采用 bash, 后来专向了 zsh

Windows 平台

  • command.com, 1980
  • cmd.exe, 1993
  • PowerShell, 2006

#54 GNU/Linux 上如何快速创建一个大文件?「from StackOverflow」

2019-12-25

StackOverflow 上看到好些种快速创建文件的命令,逐个测试,在 Ubuntu 下可用的方法有以下几种:

time dd if=/dev/zero of=test.img bs=10M iflag=fullblock,count_bytes count=10G
# 0.00s user 0.45s system 1% cpu 39.144 total

# 在 xfsprogs 包中:
# -n 表示不写入数据
time xfs_mkfile -n 10g test.img
# 0.01s user 0.01s system 2% cpu 0.669 total

# 在 VBox 挂载的虚拟磁盘上执行时遇到 “不支持的操作” 错误
# 在 EXT4 磁盘上没有遇到问题,速度很快
time fallocate -l 10G test.img

time truncate -s 10G test.img
# 0.00s user 0.00s system 46% cpu 0.005 total

time dd if=/dev/zero of=test.img bs=1 count=0 seek=10G
# 0.00s user 0.00s system 77% cpu 0.002 total

最后看到的 dd seek 方案深得征信深得朕心。

根据这个思路,Python 创建 10G 的文件应该这么写(也是我一直以来的写法):

GB = 1 << 30
with open('test.img', 'w') as _file:
    _file.seek(10 * GB - 1)
    _file.write(chr(0))