Shawn's Blog

一个伪程序员的伪技术博客

0%

0X00 前言

如果秉承着「能用就行」的原则,那么这篇文章提到的东西基本都没什么卵用;如果秉承着「写更好的代码」的原则,那么这里提到的东西也许对你有所帮助。

内容主要取材自 Effective Python,主要是作为自己学习后的一个输出而总结的这篇博客

0X01 使用 % 的 C 风格格式化

首先是沿用自 C 风格的使用 % 进行的字符串格式化方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> name = 'Shawn'
>>> job = 'developer'
>>> text = 'I am %s, a %s' % (name, job)
>>> text
'I am Shawn, a developer'
>>> height = 123.456
>>> text = 'My height is %4d.' % height
>>> text
'My height is 123.'
>>> text = 'My height is %4f.' % height
>>> text
'My height is 123.456000.'
>>> text = 'My height is %4.8f.' % height
>>> text
'My height is 123.45600000.'

这种写法在写惯了 C 的人身上比较常见,比较熟悉而且也比较简单。不过这种写法有几个问题,首先就是当百分号右侧的变量数量发生变化或者类型发生变化的时候,程序很有可能因为类型转化出现不兼容的情况(当然了,本来是 %s %4d 对应字符串和数字,现在两个都是字符串了当然就出错了)。如果要解决这种问题的话,必须每次修改都要检查百分号左右的占位符和具体数值是否能对应的伤,而且一旦占位符多了之后还很容易看花眼。

还有一个问题就是填充数值的时候通常需要对具体的值进行一些处理,比如保留某几位长度之类的,这样一来表达式可能会很长,从而显得很混乱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
coffee_price_list = [
('Americano', 15),
('Latte', 25),
('Cappuccino', 30)
]

for index, (name, price) in enumerate(coffee_price_list):
content = '%d. %-12s ----- %.2f' % (index, name, price)
print(content)

# output
0. Americano ----- 15.00
1. Latte ----- 25.00
2. Cappuccino ----- 30.00

我们来看 for 循环里面那行,是不是确实看起来乱乱糟糟的,这还只是三个占位符,如果更多的话就会更混乱了。

第三个问题是如果要用同一个值来填充多个位置,那就需要在右侧重复多次(废话之:你想要几个就得写几个)。我们假设你有一个保证书模板,只需要填入姓名、错误和保证内容就可以生成出例如「我XX再也不YY了,我保证以后ZZ」的十万字长文。但是整篇文章里出现了大量的空位,需要填入这些 XX/YY/ZZ 怎么搞呢?你可能需要在后面写上不计其数的 '--------%s-------%s-------%s-------%s' % (xx, xx, yy, zz, zz, yy) 这种东西(别跟我说你要用 replace 那是另外的内容,这里只讨论字符串格式化😅)。

当然了这个问题也不是无解,我们使用 dict 来替换平时用的 tuple 就可以了,就是类似下面这种用法(虽然我从来没真的在代码里见过谁这么写)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
value_a = 'aaaaaaaaaaaaaaaa'
value_b = 'bbbbbbbbbbbbbbbb'
value_c = 'cccccccccccccccc'

content = '''
%(val_a)s, %(val_a)s, %(val_a)s
%(val_b)s, %(val_b)s, %(val_b)s
%(val_c)s, %(val_c)s, %(val_c)s
%(val_a)s, %(val_b)s, %(val_c)s
''' % {
'val_a': value_a,
'val_b': value_b,
'val_c': value_c
}

print(content)

# output

aaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbb, bbbbbbbbbbbbbbbb, bbbbbbbbbbbbbbbb
cccccccccccccccc, cccccccccccccccc, cccccccccccccccc
aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccc

虽然这种写法解决了多次重复使用的问题,但是加重了第二点也就是代码更冗长了,因为不仅要给变量做格式化,还要给每个占位符再设定一个 key 且为其匹配好。

最后就是因为每次都需要把 key 至少写两次(占位符那里一次,后面的字典里一次),甚至因为 value 过长还可能再把变量提出去单独定义一下,就会导致整个表达式非常长,比较容易出现 bug 且定位 bug 比较复杂。

0X02 另一种方法:format函数与方法

format 是我平时用的最多的一种方法了,比较常规的方法是调用str 对象的 format() 方法,例如下面这样

1
2
3
4
5
6
7
8
9
10
11
12
13
name_1 = 'shawn'
name_2 = 'bluce'

print('hello {}, hello {}.'.format(name_1, name_2))
print('hello {:<10}, hello {:<20}.'.format(name_1, name_2))
print('hello {1}, hello {0}.'.format(name_1, name_2))
print('hello {1}, hello {0}, hello {0}, hello {1}.'.format(name_1, name_2))

# output
hello shawn, hello bluce.
hello shawn , hello bluce .
hello bluce, hello shawn.
hello bluce, hello shawn, hello shawn, hello bluce.

第二种可能比较少见,不过规则比较简单,就是在花括号里写一个冒号,冒号右边可以用 C 方法格式化变量。第三四种就比较常见了,可以通过 index 来规定位置。

这种方法还有一些更高级的用法,例如在花括号里访问字典的 key 或者访问列表中的下标(好像也没见人这么用过)

1
2
3
4
5
6
7
8
9
data = [
{'name': 'shawn', 'gender': 'M'},
{'name': 'bluce', 'gender': 'F'}
]

print('{data[0][name]} and {data[1][name]} is {data[0][gender]} and {data[1][gender]}'.format(data=data))

# output
shawn and bluce is M and F

0X03 更好的方法 f-string 插值格式字符串

在 Python 3.6 中引入的这个特性可以解决上述提到的问题,语法要求格式化的字符串前面加上一个f做前缀,就类似于之前的b/r这种。这里也同样支持前面 format 那里用到的格式化方法,例如 f'{name_1:<10}, {value:.2f}' 这种。

一个简单的例子

1
2
3
4
5
6
7
name_1 = 'shawn'
name_2 = 'bluce'

print(f'hello {name_1}, hello {name_2}')

# output
hello shawn, hello bluce

我们现在再回过头来看一下最开始提到的四个问题:第一个,如果需要调整顺序,那么百分号左侧的正文要改,右侧的值也要改,就要改两次。现在没有百分号也就不再区分左右了,如果调整顺序那么就只调整一次就行,方便了很多。第二个,如果对填进去的值稍作处理可能会导致整个表达式变得很长。现在因为省略了百分号右边的内容,所以整个表达式还是精简了不少的。第三个,当某个变量/值要用多次的时候就需要左右共写两次那么多。用 f-string 方式的话,如果确实需要调用多次且每次都要进行修改(例如保留小数或是转成大写之类的),则可以考虑将其提取出去单独赋值,然后在格式化的时候用新值来代替,还能更加符合字符串格式化的语义。第四个是说如果使用 dict 的话会使代码变多,现在不用字典了当然也就没有这个问题了。

下面是几种用法,看起来 f-string 并没有代码量少很多,是因为这个例子并不能很明显的体现出代码量少的优势,但是已经体现出可读性和维护性的优势了。如果一眼看过去,明显是 f-string 的用法最简单清晰明了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
data = [
{'name': 'shawn', 'score': 78.5},
{'name': 'ami', 'score': 89.0},
{'name': 'jack', 'score': 92.0},
{'name': 'amber', 'score': 99.5}
]

print('------------- style_1 -------------')
for item in data:
style_1 = 'name: %-10s score: %2.2f' % (item['name'], item['score'])
print(style_1)

print('\n\n------------- style_2 -------------')
for item in data:
style_2 = 'name: {:10s} score: {:2.2f}'.format(item['name'], item['score'])
print(style_2)

print('\n\n------------- style_3 -------------')
for item in data:
style_3 = 'name: {name:10s} score: {score:2.2f}'.format(name=item['name'], score=item['score'])
print(style_3)

print('\n\n------------- f-string -------------')
for item in data:
f_string = f'name: {item["name"]:10s} score: {item["score"]:2.2f}'
print(f_string)

# output
------------- style_1 -------------
name: shawn score: 78.50
name: ami score: 89.00
name: jack score: 92.00
name: amber score: 99.50


------------- style_2 -------------
name: shawn score: 78.50
name: ami score: 89.00
name: jack score: 92.00
name: amber score: 99.50


------------- style_3 -------------
name: shawn score: 78.50
name: ami score: 89.00
name: jack score: 92.00
name: amber score: 99.50


------------- f-string -------------
name: shawn score: 78.50
name: ami score: 89.00
name: jack score: 92.00
name: amber score: 99.50

0X00 背景

前两段属于并不那么重要的“故事”部分,如果切实需要一些数据恢复的经验和方法的话可以直接跳到0X02部分,给个一键三连就好(哦不对这不是B站视频,那你白嫖好了)。

故事大概是这样的,之前我有一台 Synology 的成品 NAS 用来存储我自己的照片、视频、音乐、电脑数据备份和喜欢的电影,不过因为它是单盘位的用了大概三年之后它就几乎满了,我也就开始准备升级 NAS。后来经过一番调研发现自己的需求其实不太适合用 Synology 的成品,因为它性价比低的同时附带的很多软件功能我都不需要,相比于自建 NAS 所需要的计算机知识我也正好具备,就选择自己搭建一台 TrueNAS 出来。准备的配置是CPU Intel G6400 + RAM 8G X 2 + SSD 120G + SSD 240G + HDD 8T X 3 + HDD 4T,其中双核四线程的 CPU 给这台 NAS 用已经是完全没有问题了,同时因为使用 ZFS 也就配备了 16G 的内存,8T X 3 作为主要数据存储并且搭配了 240G 的缓存盘;另外那块 4T 是从老群晖上拆下来的,作为独立存储使用;最后最小的 SSD 用来装系统。当时还整理了一篇博客来记录这件事(最近有一点点小改动)。

本来我那个 4T 的盘拆下来之后就一直没用,直到前几天我突然想把它也塞到我现在的NAS里去,正好把 OMV 换成 TrueNAS,再加一个缓存盘,重构一下自用 NAS 的架构从此就不再管它了。然后我就把重要数据打包一股脑塞到了那个小硬盘里准备重建 NAS(此时我自己的照片、视频、备份文件全部都在这个小硬盘里)。等了几天需要用的 PCI-E 转 m.2 的扩展卡和 PCI-E 转 SATA 的扩展卡都到了之后就开始重新装机。

把扩展卡和 SSD 之类的都装好之后,我特意把小硬盘的线给拔了,防止在重装系统或者其他的时候误伤到它。然后开始一路顺利的安装好了 TrueNAS 并且配置好了 3 X 8T 的存储池之后关机,准备接上小硬盘电源开始往大存储池里恢复数据。

0X01 事发

拆开机箱盖、拆开侧面版、给小硬盘插上线、装好侧面版、装好机箱盖、插上电源网线、开机、坐到笔记本前、打开 Firefox 开发者版、打开 TrueNAS 管理页面、输入用户名密码、登陆、找到存储池、格式化刚刚接入的硬盘…… woc,woc,woc 我干了啥!!!

当我意识到我格式化了装满了重要数据的 4T 硬盘之后,本来整个人都很困,结果一秒钟之内就像喝了一百杯 Espresso 一样清醒了。当时脑子里的意识流差不多就是“卧槽完了、卧槽没了、卧槽出大事情了”,愣了几秒钟之后意识到了一个重要的事“刚刚格式化的是机械硬盘”,然后第一时间卸载了挂好的硬盘并且关机断电拆机拔线。

拔掉线之后冷静了一下觉得数据应该还能找回来,赶紧开始想办法。想办法的过程中又想到“我磁盘分区貌似是 xfs 来着,这个估计还难搞”,想到难搞之后整个人又是一愣🤣

然后我开始东找找西找找,看看有没有什么好办法可以恢复数据的。当时第一时间是想到找线下的数据恢复公司,但是找数据恢复公司的话就非常贵了,而且又非常麻烦,就打算想想办法有没有可能自己把数据恢复回来。又想到 DiskGenius 可以恢复,但是去官网看了一下这玩意儿好像还有点贵(官网售价 468 元人民币),当然如果肯定可以恢复回来 468 还是值得的,不过我还是想去找找看有没有便宜一点的方案。然后也陆续找了一些其他软件,要么就是没听过的小公司搞的,要么就是特别特别贵的。就在打算去买 DiskGenius 的时候发现了一个叫做 万兴恢复专家 的软件,当时第一反应是“这种名字一般都不靠谱,就像 21 天精通CPP一样不靠谱”。不过在调查了一下这个软件和公司之后,我发现背后的这家“万兴科技”是上市公司,且旗下有亿图和墨刀两大将,就打算去试试看了。

软件下载下来之后扫描了很长时间,虽然我费尽心思下载到的46G让子弹飞这种都没有扫描到,但是我最最最最重要的照片们几乎都扫描到了,然后我就眼睛都没眨的付费买了永久授权的这个软件,因为才160+块钱对于这么多重要数据来说真的不值一提,然后用了很长一段时间把数据恢复了回来。不过值得说的是,我的几十部电影无一幸存,但是自己拍的照片的幸存率超出 90%(估计的),这个我也没搞明白是为什么。

现在我已经把重要的数据都整理出来了,所以才有心情来整理这篇文章。说是一篇文章,但是前面这段可能对于看官来说都是废话,我也只是想记录一下这么个经历才把它写出来的。最后来总结一下遇到类似情况的时候应该怎么做吧。

0X02 总结

首先,首先,首先,数据恢复的最佳时机永远是在数据丢失之前做,换句话说就是 不要把自己的重要数据搞丢 。然后如果数据万一真的丢了,那么我总结了几点这次的经验也许对大家有那么一丢丢的帮助

  1. 还是强调不要删掉自己的重要数据,在删除东西之前最好 冷静 几秒钟,格式化磁盘之前最好 冷静 几分钟
  2. 如果万一删除了或者格式化了,请第一时间先弹出、卸载磁盘,保证 不要再像磁盘中写入任何数据
  3. 找到一个你信得过的数据恢复软件,接上存储设备先扫描看看(大多数收费的数据恢复软件扫描也是不要钱的,所以你可以先看看究竟能恢复出来多少数据,可以根据这个数量决定是不是要付费购买软件)
  4. 使用数据恢复软件将数据恢复出来 不要放在恢复的磁盘里 ,应该单独找个地方存储这些恢复出来的数据

什么?你说盗版软件?这种事情就不至于还用盗版软件了吧,别人的软件帮你找回了你最重要的数据,如果是万兴的年费版的话才99块钱,难道你最重要的数据连 99 块钱都不值?

最后需要注意的几个小问题

  1. 如果是误删和“快速格式化”的话,找回来的几率都还比较大
  2. 如果是复写性质的格式化,那基本上也就别想恢复的事儿了
  3. 即使数据恢复回来了,通常情况下文件名也都彻底乱掉了
  4. 如果误删或者格式化之后又写入过文件,写的越多越没戏
  5. 如果你有 100 个文件,恢复率 99% 的话可能会丢一个
  6. 如果 100 个文件压缩保存,恢复率 99% 的话就彻底废了

最后的最后还有嘿嘿,如果你想要彻底毁掉你的硬盘,不想给别人拿着你硬盘恢复数据的机会的话

  1. 格式化的时候不要选择“快速格式化”
  2. 使用专门的工具全盘复写几次
  3. 上一条做不到的话就往磁盘里塞电影,塞满删掉塞满删掉连续个三四次

(毕竟你卖二手电脑,二手相机的时候肯定不会想成为下一个冠希哥吧

好了,我能想到的就只有这些内容了,最后希望大家永远永远永远不会用到这里面数据恢复相关的经验~

0X00 前言

首先声明这篇博客针对的是中级 Linux 用户,如果你还不清楚 Linux 中的基本权限机制 user/group/otherrwx 的话需要先去了解一下对应的基础内容才行。既然标题上写了是“不那么基础的权限”,也就能看出来虽然内容不是很基础,但是也不会很高深。

另外,这篇博客里提到的好多内容都是并不复杂的东西,但是非常零碎,也许你用了十年 Linux 还是不知道其中的一些小知识点,不过也没什么,毕竟这些知识点的使用率真的很低。

如果你看完了这篇博客有那么一点点收获,那我也算是完成目标了;如果你看完后发现所有的内容都是你以前就知道的,那我只能说你对 Linux 权限这部分的掌握超过了大多数人。因为我确实给周围好多人分享过这些内容,从刚实习的朋友到比我工作经历多很多综合实力也强很多的人,几乎没有谁是完全了解这些内容的。(所以说虽然这篇博客并不难,并不是什么高深的知识,但是我比较有信息让你从中获得那么一点点的收获)

本博客不涉及某个命令的具体用法,只起到一个让你“知道自己哪里不知道”的作用。如果想要仔细了解某个命令或者某个机制,可以自行搜索相关资料。

0X01 root 究竟是谁

“root是谁?”这个问题听起来很蠢,但是实际上好多人并没有思考过这个问题。我们都知道 Linux 中有一个叫做 uid 的东西,其实 root 用户指的就是 uid为0的用户,而非用户名为root的用户,我们可以通过 id 命令来查看用户的 uid。

而且值得注意的一点是 uid 是会被回收利用的。也就是说你创建了一个用户,系统分配了 1002 这个 uid 后,如果删掉这个用户紧接着再创建一个新用户是会敷用 1002 这个 uid 的。假设你删掉了系统中的一个老用户,又创建了个新的用户,那么万一 uid 是重复的就有可能导致这个新用户拥有之前老用户的权限,这是一种很危险的操作。

阅读全文 »

0X00 前言

在正式开始之前我们先要搞明白一个事情,那就是「函数」和「方法」到底有什么区别。首先来看一下在 Python官方文档里的定义。

函数:可以接受零个或几个参数并向调用者返回一些值的一系列语句。

function: A series of statements which returns some value to a caller. It can also be passed zero or more arguments which may be used in the execution of the body.

方法:在类里定义的函数。

method: A function which is defined inside a class body. If called as an attribute of an instance of that class, the method will get the instance object as its first argument (which is usually called self).

但是一般大家并不会很认真的区分「函数」与「方法」,而且就算不区分也并不会对平时的交流甚至编码造成任何影响(起码我没有因为不认真区分它们导致交流出现分歧或者代码出现 bug 的时候)。所以这里列出来也只是提个醒,防止有人并不是很清楚这两个名词表示的含义。其实我就是因为要写这篇博客才去搜了一下它们到底有什么区别,以前都是管 Python 里的叫「方法」,管 C 里的叫「函数」,不知道有没有人也是有这种不良习惯的🤣

既然搞清楚了,那么这篇文章后面就用「函数」来称呼好了,因为这些特性与是否定义在类里没有任何关系。(估计这是我第一次也是最后一次认认真真区分这两个东西了 hhhhh)

阅读全文 »

0X00 背景

本来家里有一台群晖的 DS118 单盘位机器装了一块 4TB 红盘,用了三年后存储空间已经告急了。再加上买了相机,有很多照片需要存储;还有两台 MacBook 的 Time Machine 需要备份,而且还有不少下载的电影时不时想要回顾一下,空间就非常紧张了。虽然这台群晖的体验确实是挺好的,不过因为我这款性能太弱了,每次上传照片的时候创建索引都会卡死很久,期间几乎所有操作都是无效的,而且因为是单盘位既没有数据冗余也不能扩展空间,就想着是时候给家里的 NAS 升个级了。毕竟再过不了多久这块磁盘也就被塞满了,到时候再研究迁移就会导致中间断层一段时间,还是不太好。

一般来说自己家里的 NAS 有几种方案,这些方案各有其优劣,都有适合和不适合的群体,可以根据自己的实际情况进行选择。

上下文提到的 FreeNAS 同时表示 FreeNAS 和 TrueNAS

  1. 群晖:简单易用、几乎不需要任何计算机专业知识、体积小功耗还低、软件套件强大、软套件强大、软件套件强大,但是比较贵。群晖就有点像是电脑届的苹果:你花了更多的钱不需要付出什么额外的东西就能获得 80 分的使用体验,但是如果你对它不满意想要改造一下以便获取 90 分乃至 100 分的体验,那是很难的。就比如你想给群晖升级硬件配置,几乎是不可能的(有些机型允许升级内存)。系统虽然提供了 ssh 连接,但是由于定制化过高也导致我们不敢进去改一些配置;
  2. 黑群晖:不推荐,没有别的理由,只是因为盗版;
  3. 威联通等:比群晖便宜,易用性可能比群晖弱一点,不过也很适合不怎么具备专业知识的用户。如果想省心还想省钱,可以考虑用威联通之类的来替换群晖;
  4. 自建 TrueNAS:需要自己购买硬件攒机,需要自己安装操作系统进行配置,各种基础存储以外的功能都需要自己手动安装配置。所以不适合没有计算机专业知识储备的同学(当然你也可以先在虚拟机里尝试一下)。而且需要注意的是 FreeNAS 基于 FreeBSD,并不是Linux、并不是Linux、并不是Linux,所以即使有一些专业知识储备的同学也要注意到这一点。不过正式因为 FreeNAS 基于 FreeBSD 所以也就带来了完整且原生的 ZFS 支持。硬件方面毕竟是自己攒机装系统,性价比什么的当然就很高了;
  5. 自建 openmediavault:同样需要自己攒机装系统,所以性价比依旧很高。但是 OMV(openmediavault) 是基于 Debian 的,所以普适性更强一些。虽然系统性能上貌似不如 FreeNAS 不过用起来也还不错,因为我自己对 Linux 比较熟悉就也选择了这个方案;
  6. 自建纯 Linux:这个自由度是最高的,但是也是最折腾的。不仅要自己装机装系统,还得配置各种服务,其他方案上一键的 Samba 服务在自建 Linux 下都要配置一会儿。不过纯 Linux 的自由度是最高的,也能选用自己最熟悉的发行版本;
  7. Windows server:不推荐,理由和黑群晖相同,除非你愿意出钱买 Windows server 授权($501)
  8. 路由器插硬盘:这种方式适合对数据安全性要求低且功能要求更低的用。因为几乎只支持上传下载,而且可定制的功能还特别少,不过好在成本最低。如果只是插上去用其他设备看个电影什么的,路由器插硬盘的方案还是可以试试的

综合下来最后我选择了自建 openmediavault,因为群晖给我的很多功能我都用不上,比如说在线 Office 和 Video Station 等,反倒是性能太弱有点接受不了;TrueNAS 的话毕竟是 BSD 我没什么把握在出问题的时候可以修复它;纯 Linux 太折腾了,不想花那么多时间在上面。

阅读全文 »