BNU-FZH

fengzhenhua@outlook.com

Python 推导式是一种强大且简洁的语法,适用于生成列表、字典、集合和生成器。在使用推导式时,需要注意可读性,尽量保持表达式简洁,以免影响代码的可读性和可维护性。

语法格式

语法格式
1
结果值1 if 判断条件 else 结果2  for 变量名 in 原列表

举例说明

代码

1
2
3
list1 = ['python', 'test1', 'test2']
list2 = [word.title() if word.startswith('p') else word.upper() for word in list1]
print(list2)

结果

1
['Python', 'TEST1', 'TEST2']

参考文章

Python2到Python3的问题修复

本文测试veusz绘制2D图形的能力,于是在官方网站下载了mandelbrot的经典例子:mandelbrot.vsz, 但是官方的例子是以python2构建的,而本文希望切换到python3解决问题。由于语法的兼容,需要做出少许修改:

  • 指明python2可以直接运行程序,但不是目前我想要的。
  • print函数,在python2中不需要加括号,但是python3中需要加括号,即print "hello world"print ("hello world")
  • python2中使用xrange函数,在python3中已经取消,合并到range函数。所以需要修改:xrange()range()
  • 错误:TypeError: ‘float‘ object cannot be interpreted as an integer. 造成错误的原因是range里面使用了小数,所以解决方法有两个:
    • python2中的/只保留整数部分,是int型。在python3中的/的结果是真正意义上的除法,得到的是一个小数,所以是float型,所以改用python3中的//得到一个整数,则问题解决。即:///
    • 如果不想任何改变仍要使用range的功能,则可以使用numpy中提供的arange,即
      1
      2
      for i in numpy.arange(0.0, 4.1, 0.8)
      print(i)

Python3下的mandelbrot.vsz

mandelbrot.vsz for pyton3
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
55
56
57
58
59
60
61
62
63
64
65
66
67
# Veusz script (version 0.7 or later)
# Jeremy Sanders (2005)
# for python3
# computes the Mandelbrot set in real time

size = 300
maxiters = 20
image = zeros( (size, size) )

print ("This takes some time, please wait")

minx = 100000
maxx = -100000
miny = 100000
maxy = -100000

for i in range(size):
for j in range(size//2):
c1 = -2+4.*i/size
c2 = 2-4.*j/size
x = c1
y = c2

minx=min(x, minx)
maxx=max(x, maxx)
miny=min(y, miny)
maxy=max(y, maxy)

n = 0
while n < maxiters and x**2+y**2 < 4.:
x1 = x**2-y**2+c1
y1 = 2*x*y+c2
x = x1
y = y1

n += 1

image[j, i] = n
image[size-j-1, i] = n

# set output data into veusz
SetData2D('image', image, xrange=(minx, maxx), yrange=(miny, maxy))

# construct the graph
To(Add('page'))
To(Add('graph'))

# Add a label
Add('label', label='The Mandelbrot Set', yPos=0.95,
alignHorz='centre', alignVert='top',
Text__size='30pt')

# add colorbar in front of image
Add('colorbar', name='colorbar1', image='image1',
direction='vertical', vertPosn='top')

# add image
Add('image', name='image1', data='image', min=1,
colorScaling='log', colorMap='heat', colorInvert=True)

# adjust axes
Set('x/min', -2.2)
Set('x/max', 1.2)
Set('y/min', 0.3)
Set('y/max', 1.9)

To('/')

运行结果

mandelbrot 集(python3 实现)

参考文章

Matplotlib 指定图形大小(单位厘米)

Matplotlib是Python中最常用的绘图工具之一。它可以用来画各种类型的图表,从简单的折线图到复杂的3D图。在Matplotlib中,可以指定图像的大小和分辨率。

本文将介绍如何在Matplotlib中指定图形大小,以及如何使用厘米单位。

确定图形大小

在Matplotlib中,可以使用plt.figure()函数来创建一个新的图像。该函数包括几个参数,其中一个是figsize,它可以用来指定图像的大小。

默认情况下,Matplotlib中的figsize参数是以英寸为单位的。但是,我们可以通过将figsize参数设置为一个包含两个数字的元组来指定图像的大小。第一个数字是图像的宽度,第二个数字是图像的高度。

例如,下面的代码将创建一个大小为4英寸x4英寸的图像:

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
import numpy as np

# 创建一个大小为4英寸x4英寸的图像
fig = plt.figure(figsize=(4, 4))
ax = fig.add_subplot(111)

# 在图像中绘制一个正弦波
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
ax.plot(x, y)

plt.show()

你可以看到,这个图像略微有些小,无法清晰地显示。如果我们需要一个更大的图像,我们可以通过增加figsize参数中的数字来实现。例如,如果我们需要一个8英寸x8英寸的图像,我们可以将figsize参数设置为(8,8)

但是,如果我们想要以厘米为单位指定图像的大小,该怎么办呢?

使用厘米单位

默认情况下,Matplotlib使用英寸作为图像大小的单位。但是,我们可以使用其他单位来指定图像的大小,包括厘米。

要在Matplotlib中指定图像大小为厘米,我们需要使用Figure.set_size_inches()函数。该函数包括两个参数,分别是图像的宽度和高度,以英寸为单位。我们可以将这些参数转换为厘米,然后将它们传递给set_size_inches()函数。

例如,下面的代码将创建一个大小为8厘米x8厘米的图像:

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
import numpy as np

# 创建一个大小为8英寸x8英寸的图像
fig = plt.figure(figsize=(8/2.54, 8/2.54))
ax = fig.add_subplot(111)

# 在图像中绘制一个正弦波
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
ax.plot(x, y)

plt.show()

你可以看到,这个图像与上面的图像相同,只是现在它的大小是以厘米为单位的。我们将figsize参数设置为(8/2.54, 8/2.54),其中8/2.54是以厘米为单位的8英寸。

注意,如果你使用的是Python2,你需要在8/2.54前面加上一个小数点,即.8/2.54

总结

在Matplotlib中,我们可以使用plt.figure()函数来创建一个新的图像,并使用figsize参数指定它的大小。默认情况下,figsize的大小是以英寸为单位的。我们可以通过将figsize设置为一个包含两个值的元组来调整它的大小。要使用厘米作为单位指定图像大小,我们需要使用Figure.set_size_inches()函数,并将宽度和高度转换为厘米。

文章参考

由于不想受限于MatLab,于是全面转入到Python工作,同时绘图工具使用免费开源的veusz, 在实现python导出数据,再使用veusz绘图时,第一步就是使用python正确的绘图,而分形图就是练习画这类图的绝佳素材。

Koch曲线

代码

Koch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import turtle

Division = 3.0
DirectionAangle = [('left',60),('right',120),('left',60)]

def call(name):
if name == 'left':
return turtle.left
else:
return turtle.right

def koch(n, length):
if n==0:
turtle.forward(length)
else:
for DA in DirectionAangle:
koch(n-1,length/Division)
call(DA[0])(DA[1])
koch(n-1,length/Division)

koch(n=4, length=400)
turtle.done()

运行结果

koch

Julia 集

代码

Julia
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
import matplotlib.pyplot as plot
import numpy as np
p=0.45 #初始值c的实部
q=-0.1428 #初始值c的虚部
N=800 #最大迭代次数
M=100 #迭代区域的界值
a=3.0 #绘制图的横轴大小
b=3.0 #绘制图的纵轴大小
step=0.005 #绘制点的步长

def iterate(z,N,M):
z=z*z+c
for i in range(N):
if abs(z)>M:
return i
z=z*z+c
return N

c=p+q*1j
i=np.arange(-a/2.0,a/2.0,step)
j=np.arange(b/2.0,-b/2.0,-step)
I,J=np.meshgrid(i, j)
ufunc=np.frompyfunc(iterate,3,1)
Z=ufunc(I+1j*J,N,M).astype(np.float64)
plot.imshow(Z,extent=(-a/2.0,a/2.0,-b/2,b/2.0))
cb = plot.colorbar(orientation='vertical',shrink=1)
cb.set_label('iteration counts')
plot.show()

运行结果

Julia集

Mandelbrot 集

代码

Mandelbrot
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
import numpy as np
import matplotlib.pyplot as plot

x0=0 #初始值z0的x0
y0=0 #初始值z0的y0
zoom=1.0 #放大倍率
N=100 #最大迭代次数
R=2 #迭代半径
a=4.0 #绘制图的横轴大小
b=3.0 #绘制图的纵轴大小
step=0.005 #绘制点的步长

def iterate(c,N,R):
z=c
for i in range(N):
if abs(z)>R:
return i
z = z*z+c
return N

x=np.arange(-a/(2.0*zoom)+x0,a/(2.0*zoom)+x0,step)
y=np.arange(b/(2.0*zoom)+y0,-b/(2.0*zoom)+y0,-step)
cx,cy=np.meshgrid(x, y)
c = cx + cy*1j
ufunc=np.frompyfunc(iterate,3,1)
Z=ufunc(c,N,R).astype(np.float64)
plot.imshow(Z,extent=(-a/2.0,a/2.0,-b/2,b/2.0))
cb = plot.colorbar(orientation='vertical',shrink=1)
cb.set_label('iteration counts')
plot.show()

运行结果

Mandelbrot 集

说明

本文参考文章: 使用 Python 绘制分形: Koch 曲线、Julia 集、Mandelbrot 集, 文章发布于2017年1月25日, 比较早。其使用的是python2, 而当前使用更加先进的python3更加合理,于是切换为python3代码后,出现几处错误,本文逐一更正。

python3 xrange 报错

由于python3取消了在python2中定义的xrange函数,取而代之的是range, 于是在修改后的代码中改xrangerange.

AttributeError: module 'numpy' has no attribute 'float'.

直接运行程序时,会提示:

1
2
3
4
AttributeError: module 'numpy' has no attribute 'float'.
`np.float` was a deprecated alias for the builtin `float`. To avoid this error in existing code, use `float` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.float64` here.
The aliases was originally deprecated in NumPy 1.20; for more details and guidance see the original release note at:
https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations. Did you mean: 'float16'?

于是根据提示:将np.float修改为np.float64解决问题。

ImportError: libtk8.6.so: cannot open shared object file: No such file or directory

造成这个问题的原因是系统没有安装程序tk,安装即可解决问题:

1
sudo pacman -S tk

Tkinter 模块(Tk 接口)是 Python 的标准 Tk GUI 工具包的接口 .TkTkinter 可以在大多数的 Unix 平台下使用,同样可以应用在 WindowsMacintosh 系统里。Tk8.0 的后续版本可以实现本地窗口风格, 并良好地运行在绝大多数平台中。详情参考:Python GUI编程(Tkinter)

Node.js 是一个流行的服务器端 JavaScript 运行时环境,用于构建高性能、可扩展的网络应用程序。Node.js 有两个主要的版本发布频道,分别是 Current 版本和 LTS 版本,它们之间有一些重要的区别。在本文中,我将详细介绍 Node.jsCurrent 版本和 LTS 版本的区别,并通过示例来说明这些区别。

Node.js 的版本发布周期

首先,让我们了解一下 Node.js 的版本发布周期,以便更好地理解 Current 版本和 LTS 版本之间的差异。

  • Current 版本:Current 版本是 Node.js 的最新开发版本,通常每隔几个月发布一次。它包含最新的功能、改进和实验性特性,但不太稳定。Current 版本的目标是提供给开发人员一个平台来尝试新的功能和实验性特性,以便反馈和测试。
  • LTS 版本:LTS(Long-Term Support)版本是 Node.js 的长期支持版本,通常每隔两年发布一次。LTS 版本的主要特点是稳定性和可靠性。它们接受持续的维护和安全更新,以确保企业和生产环境的稳定性。

区别一:稳定性和可靠性

最明显的区别是 Current 版本和 LTS 版本的稳定性和可靠性。LTS 版本在发布后会接受长达几年的维护和安全更新,因此非常适合用于生产环境,特别是对于企业应用程序。相比之下,Current 版本可能包含实验性特性和较新的代码,可能会引入不稳定性或潜在的问题,因此不建议在生产环境中使用。

示例:假设你正在开发一个在线电子商务网站,该网站需要高度稳定性和可靠性。在这种情况下,你可能会选择使用 LTS 版本,因为它提供了经过验证的稳定性和长期支持,有助于确保你的网站在生产环境中保持高可用性。另一方面,如果你正在开发一个实验性的新功能,可以选择 Current 版本,以便尝试最新的功能和改进。

区别二:新特性和改进

Current 版本是 Node.js 的最新开发版本,因此包含了最新的功能、改进和实验性特性。这使得它成为了尝试新技术和构建原型的理想选择。如果你想要访问最新的 ECMAScript 版本、Node.js API 或其他功能,Current 版本可能会更适合你。

示例:假设你需要使用最新的 ECMAScript 模块系统,以便更好地组织你的代码。在这种情况下,你可以选择 Current 版本,因为它可能会提供对这些新特性的更好支持。另外,如果你想尝试 Node.js 中的实验性功能,例如 Web Streams API,也可以选择 Current 版本来体验这些新功能。

区别三:性能优化

Current 版本通常包含了性能优化和改进,以提高应用程序的性能。这些优化可以包括更快的执行速度、更低的内存消耗和更好的并发处理能力。如果你对应用程序的性能有较高的要求,那么可能会考虑使用 Current 版本来获得这些优势。

示例:假设你正在开发一个实时多人游戏服务器,需要处理大量并发连接和快速响应时间。在这种情况下,你可能会选择 Current 版本,因为它可能包含了性能改进,有助于提高服务器的响应速度和并发处理能力,从而提供更好的游戏体验。

区别四:维护和安全更新

LTS 版本的一个关键优势是它们接受长期的维护和安全更新。这意味着即使在发布后的几年内,Node.js 社区仍然会修复潜在的安全漏洞和问题,以确保应用程序的安全性。对于企业和生产环境来说,这是一个非常重要的因素。

示例:假设你的公司运行着一个在线支付平台,处理大量敏感用户数据。在这种情况下,安全性至关重要。你可能会选择 LTS 版本,因为它提供了长期的维护和安全更新,帮助你保持应用程序的安全性,同时避免潜在的安全威胁。

区别五:社区支持

由于 LTS 版本在生命周期内接受长期的维护,因此通常拥有更广泛的社区支持。这意味着你可以更容易地找到解决问题的资源、文档和社区支持。相比之下,Current 版本可能会更加小众,社区支持可能有限。

示例:假设你在开发过程中遇到了一个复杂的问题,需要寻求帮助和支持。如果你使用的是 LTS 版本,你可能会更容易地在社区中找到有经验的开发者来帮助你解决问题,因为这个版本有更广泛的社区支持。

区别六:升级频率

LTS

本的升级频率较低,通常每隔两年发布一个新的 LTS 版本。这使得企业可以更轻松地规划和管理升级过程。相比之下,Current 版本的升级频率较高,可能每隔几个月就会发布一个新版本,需要更频繁的升级和迁移工作。

示例:如果你的公司有一个严格的升级策略,并且希望最小化升级带来的风险和工作量,那么你可能会更倾向于选择 LTS 版本。这样,你可以更轻松地规划升级,并确保稳定性和可靠性。

如何选择适合的版本?

选择 Node.js 的版本取决于你的具体需求和项目的性质。以下是一些指导原则,帮助你决定是使用 Current 版本还是 LTS 版本:

  1. 生产环境 vs. 开发环境:如果你的应用程序将在生产环境中运行,特别是对于企业级应用程序,通常建议使用 LTS 版本,以确保稳定性和安全性。对于开发和测试环境,你可以考虑使用 Current 版本来尝试最新的功能和改进。
  2. 功能需求:如果你需要访问最新的 ECMAScript 版本、Node.js API 或其他功能,Current 版本可能更适合你。如果你的应用程序对稳定性要求较高,那么应该优先选择 LTS 版本。
  3. 性能需求:如果你对应用程序的性能有较高的要求,可以考虑使用 Current 版本,因为它可能包含性能优化。但要确保你有充分的测试和性能监控,以确保新版本不会引入性能问题。
  4. 安全性:如果你处理敏感数据或对安全性有严格要求,LTS 版本是更安全的选择,因为它接受长期的维护和安全更新。
  5. 社区支持:如果你需要广泛的社区支持,特别是在解决问题时,LTS 版本通常会有更多的资源和支持可用。
  6. 升级策略:考虑你的公司或项目的升级策略。如果你希望最小化升级工作量,LTS 版本可能更适合你。

结论

Node.jsCurrent 版本和 LTS 版本之间存在明显的区别,涵盖了稳定性、新特性、性能、维护、社区支持和升级频率等方面。在选择适合你的版本时,需要综合考虑你的项目需求和优先级。无论你选择哪个版本,都要确保了解该版本的特性和限制,并采取适当的测试和监控措施,以确保应用程序的稳定性和性能。在任何情况下,Node.js 的强大生态系统和活跃的社区都将为你提供支持和资源,帮助你构建出色的应用程序。

很多人觉得内存频率越高速度越快,尤其是在英特尔12代酷睿处理器支持DDR5之后,不少人都想升级到DDR5,以为升级到DDR5就能获得更高的性能。事实真的是这样吗?如下图两内存,都是同样的芝奇的DDR4 3600MHz 16GBx2的内存,为什么会有500+的价格差?因为它们有不同的CL时序,一个是18-22-22-42,另一个是16-19-19-39,这点区别就值500+。那么内存CL时序到底是什么?对性能又有什么影响?

内存在存取数据时是以行列的方式进行,跟Excel表格类似,通过行列的方式定位数据。内存时序代表的是行列访问所需要的周期数(周期数不等于时间),主要有四个分别是CL,tRCD,tRP,tRAS。CL代表内存访问一次列所需的周期数,下图为16,tRCD表示内存访问一次行所需的最小周期数,下图中的19,tRP表示打开下一行的最小周期数,下图中为19,tRAS代表的是行活动到发出打开下一行的最小周期数。看起来很复杂,最重要的就是第一个,即CL。

CL全称CAS latency,内存访问一次列的延迟时间,在频率相同的情况下,CL值越小内存速度越快。就跟我们看小说一样,只要定位到了行,剩下的就是读行中的每一列,直到读完一行才会读下一行。内存只要定位到了数据所在的行,剩下的基本就是列操作,直到读完一行才会进行一次行操作。所以CL值对内存来说非常重要。

从下表中可以发现CL(表中的CAS Latency)的值是随着内存频率的增加而增大的。DDR5 4800MHz的CL值为40个时钟周期,比DDR4更大。这也是为什么DDR5比DDR4提升不大的原因,频率高了延迟更大。由于CL是时钟周期与内存频率相关不好比较,所以要换成时间。CL延迟时时间=1/(DDR频率/2)* CL周期数*1000。

公式中的1代表1秒钟,内存频率除以2,是因为DDR的全称是Double Data Rate即双倍数据传输速率,如下图SDR不用除,QDR要除以4才是内存的时钟频率,因此上图的DDR4,DDR5都要除以2 。乘1000是为了把微秒换成纳秒。所以上图DDR5 4800MHz的CL延迟时间=1/(4800/2)*40*1000=16.67ns。

内存的CL,tRCD等都是可以通过BIOS修改的,如下图修改成1600MHz 16CL和1500MHz 15CL哪个更快?用上面的公式算CL延迟都是10ns,所以几乎一样快。决定内存速度的除了CL延迟以外,还有其它几个。但CL影响更大。

现在我们来解决第一张图为什么后者比前者更贵的问题。2099的CL延迟时间为8.89ns,而1549的CL延迟时间为10ns,2099的存取速度更快,也因此更贵。

引用:对内存频率的误解!以为频率越高速度就越快,真相未必是这样

问题描述

今天突然发现台式机上的蓝牙无法启动,按钮开启后蓝牙仍然处于关闭状态。如图所示:

蓝牙无法打开

解决方案

1. 卸载并重新加载btusb内核模块(支持蓝牙设备的内核模块)

1
2
3
4
sudo rmmod btintel
sudo rmmod btusb
sudo modprobe btusb
sudo modprobe btintel

2024年08月29日升级ArchLinux后发现蓝牙再次无法打开,根据文章在Archlinux 下蓝牙突然无法打开的解决办法 修复此问题,于是做出如上修改。

2. 安装蓝牙工具和工具包

1
sudo pacman -S --needed --noconfirm bluez bluez-utils pipewire-pulse bluedevil blueman

3. 启动蓝牙服务

1
sudo service bluetooth start

4. 重新解除蓝牙设备的阻止

1
2
rfkill block bluetooth
rfkill unblock bluetooth

蓝牙设备可被检测到

蓝牙启动成功

收音机并未随着互联网时代的到来而消失,而是以互联网广播的形式继续存在,并且变得更加丰富。互联网广播除了传统的直播电台内容外,还有大量的自制节目,可供听众随意的点播、订阅,比如蜻蜓 FM、喜马拉雅等。

今天要介绍的是 Linux 下最优秀的在线广播收音机软件 Shortwave。使用 Shortwave 可以无限制收听世界各地的高品质广播电台,尤其是大量的音乐台、外语广播。

Shortwave

Shortwave 官方网站:https://www.shortwave.com, WindowsIOSAndroid用户请前往官方网站下载安装包安装。

ArchLinux 并没有将shortwave收录到官方仓库,用户可以使用paruyay安装

1
paru -S --noconfirm shortwave

LinuxMint 用户可以在软件中心(软件管理器)中搜索安装即可。

启动 Shortwave,界面简洁,没有任何复杂的设置,甚至可以说,没有设置。主界面罗列了各种流行电台,顶部一个搜索栏。

随便点击一个电台,即可播放。虽然是国外电台,但相应速度不错,也不会卡顿。

对于喜欢的电台可以点击电台详情,然后“添加到库”进行收藏。这个 Dance Wave 是以 DJ 动感舞曲为主的电台。

Music Lake 以纯净、轻音乐为主;Radio Paradise 则是320k高品质的音乐节目,音质很棒。

Shortwave 除了罗列各种流行电台,也可以搜索。如图所示,搜索美国知音的电台结果。想 1990 年代末、2000 年代初,薄荷君在高中、大学时期,经常用那嘈杂不堪、信号不稳的收音机,左右摇摆天线,收听 VOA Special English 慢速英语。

Shortwave 还可以缩小成 Mini 播放器模式。

如前所述,Shortwave 没有什么设置选项,只有一个关闭/打开消息通知。够简单易用吧?

众所周知在Windows下修改U盘的名称是相当简单的,但是查询Linux下修改U盘名称,搜到的教程都十分麻烦。本文给出一个比较简单的方法,就是借助磁盘工具Gparted修改,因为修改U盘名称毕竟是小概率事件,所以此操作简单易行。

  • Gnarted没有安装,则先安装命令:

    1
    sudo pacman -S gparted

  • 插入待修改名称的U盘

  • 点击右上角,选中待修改名称的U盘

选中待修改名称的U盘
  • 右键,选择卸载(U)
卸载挂载的U盘
  • 右键,选择文件系统卷标(L)
选中文件系统卷标
  • 在弹出的对话框输入新的U盘名称, 然后点上面的符号√应用全部操作, 修改名称完毕
应用修改

近期由于我的U盘仓库BNU-FZHU盘损坏,所以不得不升级zugit.sh脚本, 添加一键搬家功能,这可以方便的将仓库同步到新的U盘. 然而在更新后将新版本的zugit.sh推送到gitee时,发现报错:

推送至Gitee报错

看日志报出的错误,It is required that your private key files are NOT accessible by others,翻译就是需要私钥文件不能被其他人所访问。私钥是访问linux服务器的凭证,如果被别人获取到,就可能对服务器安全造成影响,这可能也就是这个问题的初衷吧。

按照错误提示,该文件不能被其他人访问,只要将所属组和其他人的read权限取消即可. 于是将Gitee对使用的ssh密钥~/.ssh/id_ed25519_1更改权限为600,即

1
chmod 600 ~/.ssh/id_ed25519_1