#79 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 的意思应该是指连临时文件都不用创建的模式。也不知道有什么文件系统支持这种模式。

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

2021-03-15

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

#75 使用 jq 命令解析 JSON 数据

2021-03-04

JSON Logo

jq 是我在命令行中解析 JSON 的一个常用工具,用起来非常顺手。

  • https://github.com/stedolan/jq
  • https://stedolan.github.io/jq/

用法

curl https://24pullrequests.com/users.json | jq

# 取第一个元素
curl https://24pullrequests.com/users.json | jq '.[0]'
# 取第一个元素的指定字段
curl https://24pullrequests.com/users.json | jq '.[0].nickname'

# 切片
curl https://24pullrequests.com/users.json | jq '.[:2]'

# 遍历
curl https://24pullrequests.com/users.json | jq '.[] | .nickname'

# 取字段
curl https://24pullrequests.com/users/changeworld.json | jq .nickname

# 取多个字段
curl https://24pullrequests.com/users/changeworld.json | jq '.nickname,.contributions_count'

# 获取列表长度
curl https://24pullrequests.com/users.json | jq length

# 列出 Keys
curl https://24pullrequests.com/users/changeworld.json | jq keys
curl https://24pullrequests.com/users/changeworld.json | jq "keys[]"
curl https://24pullrequests.com/users.json | jq ".[0] | keys[]"

# 列出 Keys 和 值类型
cat a.json | jq ".[0] | keys,map(type)"
cat a.json | jq ".[0] | to_entries | map([.key, (.value | type)])"
cat a.json | jq '.[0] | to_entries | map("\(.key) : \(.value|type)")[]'

还有很多更强大的用法,可以参考文档,我就会上面几个,在命令行中简单搜索 JSON 也够用了。

刚在文档中学会一招,重新组合 JSON:

curl https://24pullrequests.com/users/changeworld.json | jq '[.nickname, .organisations[].login]'
curl https://24pullrequests.com/users/changeworld.json | jq '{name:.nickname, orgs:[.organisations[].login]}'

👍🏻 Nice!!!

#73 高效搜索日志

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

#72 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))

#71 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'

#70 Linux 查看文件打开情况

2020-06-23

查看这个进程打开了哪写文件

-> % ps -ef | grep rsyslogd | grep -Fv grep
syslog      1068       1  0 1月06 ?       00:00:03 /usr/sbin/rsyslogd -n -iNONE

-> % sudo ls -l /proc/1068/fd 
总计 0
lr-x------ 1 root root 64  1月  8 16:54 0 -> /dev/null
l-wx------ 1 root root 64  1月  8 16:54 1 -> /dev/null
l-wx------ 1 root root 64  1月  8 16:54 10 -> /var/log/kern.log
l-wx------ 1 root root 64  1月  8 16:54 11 -> /var/log/auth.log
l-wx------ 1 root root 64  1月  8 16:54 2 -> /dev/null
lrwx------ 1 root root 64  1月  6 09:46 3 -> 'socket:[13299]'
lr-x------ 1 root root 64  1月  8 16:54 4 -> /dev/urandom
lrwx------ 1 root root 64  1月  8 16:54 5 -> 'socket:[27825]'
lrwx------ 1 root root 64  1月  8 16:54 6 -> 'socket:[27831]'
lr-x------ 1 root root 64  1月  8 16:54 7 -> /proc/kmsg
lrwx------ 1 root root 64  1月  8 16:54 8 -> 'socket:[22468]'
l-wx------ 1 root root 64  1月  8 16:54 9 -> /var/log/syslog

查看哪些进程打开了这个文件

-> % sudo find /proc/*/fd -ls | grep /var/log/syslog
  2551982      0 l-wx------   1 root             root                   64 1月  8 16:50 /proc/1068/fd/9 -> /var/log/syslog

-> % ps -fq 1068   
UID          PID    PPID  C STIME TTY          TIME CMD
syslog      1068       1  0 1月06 ?       00:00:03 /usr/sbin/rsyslogd -n -iNONE