如何理解Rust的Option,Result和Future?

本文不以追求概念的精确为目标,而是帮助没有函数式编程经验的读者了解为什么要将普通的事情复杂化,引入这么多玄乎的概念!

包裹数据对象有什么用?

先来看一段java代码:

public class JavaApplication4 { 
   public static void main(String args[]) { 
      try { 
         int a[] = new int[5];
         a[5] = 30/0;  
      }  
      catch(ArithmeticException e){System.out.println("task1 is completed");}  
      catch(ArrayIndexOutOfBoundsException e){System.out.println("task 2 completed");}  
      catch(Exception e){System.out.println("common task completed");}
      System.out.println("rest of the code...");  
   }  
} 

上面的java代码对数字进行运算,其中可能涉及到出错,因此用大量的try,catch来捕获和处理错误。这当然可以解决问题和理解问题,但并不意味着必须这样处理!比如用下面那样的伪代码(pseudo):

(可能出错的数) (能够处理可能出错数的除法符号) (可能出错的另一个数)
R(30) /// R(0)

在上面的伪代码中,不再直接面对数据本身,而是面对被包裹的数,在这个世界里,运算符面对的也是被包裹数。

R(30) /// R(10) = R(3)
R(30) /// R(0) = R(被0除的错误)

多么简洁的结果!多么优秀的设计。当你体会到这个设计的用意,就会自然地接纳这个设计,从而用被包裹的数据思考问题,而不是用裸露的数据。不要被那些玄乎的名词吓唬住,什么Monad,functor,它们都可以看作是一种设计模式,帮助我们用新的视角来看待熟悉的事物。

つづく

可以把python安装从一台服务器复制到另一台服务器吗?

如果是完全一样的系统,答案是可以。

# 比如,把python3.6复制到另一台机器去。
tar -czvf py3.6.7.tar.gz /usr/local/bin/python3.6 /usr/local/bin/python3.6m /usr/local/bin/python3.6m-config /usr/local/bin/pip3.6 /usr/local/bin/pyvenv-3.6 /usr/local/bin/pydoc3.6 /usr/local/lib/python3.6

#在另一台服务器上:
tar -xzvf py3.6.7.tar.gz -C /

从源代码编译安装Python3.7到Centos6.4

从源代码安装的过程中遇到几个问题,以下是解决方案。

确认安装了libffi-dev

如果没有安装的话,最后会碰到ImportError: No module named ‘_ctypes’错误。

确认当前的的LANGUAGE和LANG环境变量是en_US.

如果遇到:Fatal Python error: initfsencoding: Unable to get the locale encodingLookupError: unknown encoding: xxx这样的错误。可能需要重置环境的LANG值。
printenv命令确定值。

export LANG=en_US
export LANGUAGE=en_US

如果碰到pip3的ssl模块没有安装

最大的可能是openssl不满足 python的最低要求。

找不到venv或者virtualenv?

执行python3.x -m venv –help 即可。

备份之前的准备工作

假设需要将源服务器S备份到目标服务器D,S和D需要满足哪些要求呢?为了减少对S服务器的干扰,尽量避免在S服务器上安装额外的软件。因此S服务器需要软件有:

D端通常拥有更多的自主权,建议安装的有:

  • ssh客户端,linux一般默认具备,win系统的话安装win32-openssh
  • ssh-agent,避免ssh交互手动输入phrase。
  • openssl,如果有加密内容和S服务器传递的话。
  • powershell,主要的备份代码由powershell编写。

配置文件的产生

产生关于S服务器的配置文件,D服务器对于S服务器知识全部来自于配置文件,配置文件有默认的模板,可以通过命令复制一个针对S的配置文件。

.\PasswordAssist.ps1 -Action CopyDemoConfigFile

此命令会提示你输入:

  • 关于备份什么内容,borg或者mysql
  • 服务器使用什么语言,python或者powershell
  • S服务器的IP或者主机名,该值会自动更新到配置文件中。

默认情况下,配置文件输出到当前目录的:

./myconfigs/ip地址/borg-config.json | mysql-config.json

配置文件的属性

简单介绍几个。

  • IdentityFile, D服务器的ssh private key文件路径
  • ServerPublicKeyFile,S服务器的public key文件,是D服务器上的路径,用来加密。比如mysql的密码用这个publickey加密,传送到S服务器之后,可由S服务器的privatekey来解密。
  • ServerPrivateKeyFile,S服务器的私钥的路径,是S服务器上的路径,用来解密。

备份时关于保留年备份、月备份、日备份的算法理解

备份时为了防止当前备份被污染,会定时将备份内容复制到其它目录,和当前备份写入目录隔离,以此达到保护备份的目的。但同时考虑到存储容量,会采取一定的策略删除部分拷贝,保留几个天备份,星期备份,月备份等等。

假设备份软件每隔5秒钟产生出一个备份拷贝,希望保留3个最新的秒备份,2个最新的分钟备份,2个小时备份,2个天备份,2个月备份,2个年备份。用一个数组表示:3 2 2 2 0 2 2,其中第5个是周备份,这里设成0也就是跳过不处理。以下是实验数据。

$ddd = @"
2018-03-03 21:21:00
2018-03-03 21:21:05
2018-03-03 21:21:10
2018-03-03 21:21:15
2018-03-03 21:21:20
2018-03-03 21:21:25

2018-03-03 21:11:20
2018-03-03 21:11:25
2018-03-03 21:11:30

2018-03-03 21:05:20
2018-03-03 21:05:25
2018-03-03 21:05:30

2018-03-03 20:11:20
2018-03-03 20:11:25
2018-03-03 20:11:30

2018-03-03 19:21:20
2018-03-03 19:21:25
2018-03-03 19:21:30

2018-03-02 20:11:20
2018-03-02 20:11:25
2018-03-02 20:11:30

2018-03-01 21:21:20
2018-03-01 21:21:25
2018-03-01 21:21:30

2018-02-02 20:11:20
2018-02-02 20:11:25
2018-02-02 20:11:30

2018-01-03 21:21:20
2018-01-03 21:21:25
2018-01-03 21:21:30

2017-02-02 20:11:20
2017-02-02 20:11:25
2017-02-02 20:11:30

2016-03-03 21:21:20
2016-03-03 21:21:25
2016-03-03 21:21:30
"@

单元测试代码:

function getfixture {
    $ddd -split "[\r\n]+" | Where-Object {$_} | ForEach-Object {@{CreationTime=(Get-Date $_)}} | Sort-Object -Property CreationTime
}

Describe "find backup files to delete" {
    it "should find yearly" {
        $v = getfixture
        "total $($v.Count)" | Out-Host
        $toDelete = Find-BackupFilesToDelete -FileOrFolders $v -Pattern '2 0 0 0 0 0 0'
        "todelete $($toDelete.Count)" | Out-Host
        $toDelete.Count | Should -Be 5 # all 3 of 2016, and 2 out of 3 in 2017.
    }
    it "should find monthly" {
        $v = getfixture
        "total $($v.Count)" | Out-Host
        $toDelete = Find-BackupFilesToDelete -FileOrFolders $v -Pattern '2 2 0 0 0 0 0'
        "todelete $($toDelete.Count)" | Out-Host
        $toDelete.Count | Should -Be 10 # 5 + 5
    }

    it "should find weekly" {
        $v = getfixture
        "total $($v.Count)" | Out-Host
        $toDelete = Find-BackupFilesToDelete -FileOrFolders $v -Pattern '2 0 2 0 0 0 0'
        "todelete $($toDelete.Count)" | Out-Host
        $toDelete.Count | Should -Be 10 # 5 + 5
    }

    it "should find dayly" {
        $v = getfixture
        "total $($v.Count)" | Out-Host
        $toDelete = Find-BackupFilesToDelete -FileOrFolders $v -Pattern '2 2 0 2 0 0 0'
        "todelete $($toDelete.Count)" | Out-Host
        $toDelete.Count | Should -Be 15 # 5 + 5 + 5
    }

    it "should find hourly" {
        $v = getfixture
        "total $($v.Count)" | Out-Host
        $toDelete = Find-BackupFilesToDelete -FileOrFolders $v -Pattern '2 2 0 2 2 0 0'
        "todelete $($toDelete.Count)" | Out-Host
        $toDelete.Count | Should -Be 20 # 5 + 5 + 5 + 5
    }

    it "should find minutely" {
        $v = getfixture
        "total $($v.Count)" | Out-Host
        $toDelete = Find-BackupFilesToDelete -FileOrFolders $v -Pattern '2 2 0 2 2 2 0'
        "todelete $($toDelete.Count)" | Out-Host
        $toDelete.Count | Should -Be 25 # 5 + 5 + 5 + 5 + 5
    }

    it "should find secondly" {
        $v = getfixture
        "total $($v.Count)" | Out-Host
        $toDelete = Find-BackupFilesToDelete -FileOrFolders $v -Pattern '2 2 0 2 2 2 2'
        "todelete $($toDelete.Count)" | Out-Host
        # only the items in last minute participate find action. If group secondly, there will be one item per group.
        # But it does'nt matter, it still do right.
        # 2018-03-03 21:21:00
        # 2018-03-03 21:21:05
        # 2018-03-03 21:21:10
        # 2018-03-03 21:21:15
        # 2018-03-03 21:21:20
        # 2018-03-03 21:21:25
        $toDelete.Count | Should -Be 29 # 5 + 5 + 5 + 5 + 5 + 4
    }
}
function Find-BackupFilesToDelete {
    param (
        [Parameter(Mandatory = $true, Position = 0)][array]$FileOrFolders,
        [Parameter(Mandatory = $false, Position = 1)][string]$Pattern
    )
    [array]$pts = $Pattern.Trim() -split '\s+'
    $pts = $pts | ForEach-Object {[int]$_}

    if ($pts.Count -ne 7) {
        throw 'wrong prune pattern, must have 7 fields.'
    }
    if (($pts[1] -gt 0) -and ($pts[2] -gt 0)) {
        throw 'one of week and month field must be 0.'
    }
    $ga = @(
        '{0:yyyy}',
        '{0:yyyyMM}',
        '{0:yyyy}',
        '{0:yyyyMMdd}',
        '{0:yyyyMMddHH}',
        '{0:yyyyMMddHHmm}',
        '{0:yyyyMMddHHmmss}'
    )

    $ToIterator = $FileOrFolders
    for ($i = 0; $i -lt $pts.Count; $i++) {
        $pt = $pts[$i]
        $mftstr = $ga[$i]
        if ($pt -gt 0) {
            if ($i -ne 2) {
                $grps = $ToIterator |
                    Sort-Object -Property CreationTime | 
                    Group-Object -Property {$mftstr -f $_.CreationTime} |
                    Sort-Object -Property Name
            }
            else {
                $grps = $ToIterator |
                    Sort-Object -Property CreationTime |
                    Group-Object -Property {($mftstr -f $_.CreationTime) + [int]($_.CreationTime.DayOfYear / 7)} |
                    Sort-Object -Property Name
            }
            $toDeleteGrps = $grps | Select-Object -SkipLast $pt
            $remainGrpsButLast = $grps | Select-Object -Last $pt | Select-Object -SkipLast 1
            $lastGrp = $grps | Select-Object -Last 1

            $toDeleteGrps | ForEach-Object {
                $PSItem.Group | ForEach-Object {$_}
            }
            $remainGrpsButLast | ForEach-Object {
                $PSItem.Group | Select-Object -SkipLast 1
            }
            $ToIterator = $lastGrp.Group
        }
    }
}

代码逻辑:

首先按年份分组,实验数据分组后有2016,2017,2018三个分组,除去需要保留的分组数,比如2,那么2016分组全部删除;最后一个不去动它(2018),除此之外的保留分组内只保留最新的一个版本(2017年分组内只保留最新的版本)。然后将没有动过的分组的内容传递给月分类,按同样的逻辑处理即可。

Powershell persistent environment variables永久性环境变量的设置

以下代码改变当前session进程的环境变量值,但是退出powershell窗口再次进入,这些修改就会消失。

$Env:abc = 1
Set-Item Env:abc 1

要保存变量,可以使用windows的SETX命令,但Linux下没有setx命令,因此采用.NET的方式更具有通用性。

[Environment]::SetEnvironmentVariable("TV", "a permanent variable in user space.", "User")
[Environment]::SetEnvironmentVariable("TV", "a permanent variable in machine space.", "Machine")

如何删除保存的变量?把值设置成$null即可。

[Environment]::SetEnvironmentVariable("TV", $null, "Machine")

改变Path变量的一些例子

首先在powershel的交互窗口中确定结果是期望的结果之后,再将值设置成永久改变。

$OriginPath = $env:path

# 删除最后两个路径
($OriginPath -split ';' | Select-Object -SkipLast 2) -join ';'

# 如果结果正确
[Environment]::SetEnvironmentVariable("path", (($OriginPath -split ';' | Select-Object -SkipLast 2) -join ';'), "User")