分类 技术 下的文章

如何在 Linux 中校验 ISO 镜像

你从喜爱的 Linux 发行版的官方网站或第三方网站下载了它的 ISO 镜像之后,接下来要做什么呢?是创建可启动介质并开始安装系统吗?并不是,请稍等一下。在开始使用它之前,强烈建议你检查一下你刚下载到本地系统中的 ISO 文件是否是下载镜像站点中 ISO 文件的一个精确拷贝。因为在前几年 Linux Mint 的网站被攻破了,并且攻击者创建了一个包含后门的经过修改的 Linux Mint ISO 文件。 所以验证下载的 Linux ISO 镜像的可靠性和完整性是非常重要的一件事儿。假如你不知道如何在 Linux 中验证 ISO 镜像,本次的简要介绍将给予你帮助,请接着往下看!

在 Linux 中验证 ISO 镜像

我们可以使用 ISO 镜像的“校验和”来验证 ISO 镜像。校验和是一系列字母和数字的组合,用来检验下载文件的数据是否有错以及验证其可靠性和完整性。当前存在不同类型的校验和,例如 SHA-0、SHA-1、SHA-2(224、256、384、512)和 MD5。MD5 校验和最为常用,但对于现代的 Linux 发行版,SHA-256 最常被使用。

我们将使用名为 gpgsha256 的两个工具来验证 ISO 镜像的可靠性和完整性。

下载校验和及签名

针对本篇指南的目的,我将使用 Ubuntu 18.04 LTS 服务器 ISO 镜像来做验证,但对于其他的 Linux 发行版应该也是适用的。

在靠近 Ubuntu 下载页的最上端,你将看到一些额外的文件(校验和及签名),正如下面展示的图片那样:

Ubuntu 18.04 的校验和及签名

其中名为 SHA256SUMS 的文件包含了这里所有可获取镜像的校验和,而 SHA256SUMS.gpg 文件则是这个文件的 GnuPG 签名。在下面的步骤中,我们将使用这个签名文件来 验证 校验和文件。

下载 Ubuntu 的 ISO 镜像文件以及刚才提到的那两个文件,然后将它们放到同一目录下,例如这里的 ISO 目录:

$ ls ISO/
SHA256SUMS SHA256SUMS.gpg ubuntu-18.04.2-live-server-amd64.iso

如你所见,我已经下载了 Ubuntu 18.04.2 LTS 服务器版本的镜像,以及对应的校验和文件和签名文件。

下载有效的签名秘钥

现在,使用下面的命令来下载正确的签名秘钥:

$ gpg --keyid-format long --keyserver hkp://keyserver.ubuntu.com --recv-keys 0x46181433FBB75451 0xD94AA3F0EFE21092

示例输出如下:

gpg: key D94AA3F0EFE21092: 57 signatures not checked due to missing keys
gpg: key D94AA3F0EFE21092: public key "Ubuntu CD Image Automatic Signing Key (2012) <[email protected]>" imported
gpg: key 46181433FBB75451: 105 signatures not checked due to missing keys
gpg: key 46181433FBB75451: public key "Ubuntu CD Image Automatic Signing Key <[email protected]>" imported
gpg: no ultimately trusted keys found
gpg: Total number processed: 2
gpg: imported: 2

验证 SHA-256 校验和

接下来我们将使用签名来验证校验和文件:

$ gpg --keyid-format long --verify SHA256SUMS.gpg SHA256SUMS

下面是示例输出:

gpg: Signature made Friday 15 February 2019 04:23:33 AM IST
gpg: using DSA key 46181433FBB75451
gpg: Good signature from "Ubuntu CD Image Automatic Signing Key <[email protected]>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: C598 6B4F 1257 FFA8 6632 CBA7 4618 1433 FBB7 5451
gpg: Signature made Friday 15 February 2019 04:23:33 AM IST
gpg: using RSA key D94AA3F0EFE21092
gpg: Good signature from "Ubuntu CD Image Automatic Signing Key (2012) <[email protected]>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 8439 38DF 228D 22F7 B374 2BC0 D94A A3F0 EFE2 1092

假如你在输出中看到 Good signature 字样,那么该校验和文件便是由 Ubuntu 开发者制作的,并且由秘钥文件的所属者签名认证。

检验下载的 ISO 文件

下面让我们继续检查下载的 ISO 文件是否和所给的校验和相匹配。为了达到该目的,只需要运行:

$ sha256sum -c SHA256SUMS 2>&1 | grep OK
ubuntu-18.04.2-live-server-amd64.iso: OK

假如校验和是匹配的,你将看到 OK 字样,这意味着下载的文件是合法的,没有被改变或篡改过。

假如你没有获得类似的输出,或者看到不同的输出,则该 ISO 文件可能已经被修改过或者没有被正确地下载。你必须从一个更好的下载源重新下载该文件。

某些 Linux 发行版已经在它的下载页面中包含了校验和。例如 Pop!\_os 的开发者在他们的下载页面中提供了所有 ISO 镜像的 SHA-256 校验和,这样你就可以快速地验证这些 ISO 镜像。

Pop os 位于其下载页面中的 SHA256 校验和

在下载完 ISO 镜像文件后,可以使用下面的命令来验证它们:

$ sha256sum Soft_backup/ISOs/pop-os_18.04_amd64_intel_54.iso

示例输出如下:

680e1aa5a76c86843750e8120e2e50c2787973343430956b5cbe275d3ec228a6 Soft_backup/ISOs/pop-os_18.04_amd64_intel_54.iso

Pop os 的 SHA256 校验和的值

在上面的输出中,以 680elaa 开头的部分为 SHA-256 校验和的值。请将该值与位于下载页面中提供的 SHA-256 校验和的值进行比较,如果这两个值相同,那说明这个下载的 ISO 文件是合法的,与它的原有状态相比没有经过更改或者篡改。万事俱备,你可以进行下一步了!

上面的内容便是我们如何在 Linux 中验证一个 ISO 文件的可靠性和完整性的方法。无论你是从官方站点或者第三方站点下载 ISO 文件,我们总是推荐你在使用它们之前做一次简单的快速验证。希望本篇的内容对你有所帮助。

参考文献:


via: https://www.ostechnix.com/how-to-verify-iso-images-in-linux/

作者:sk 选题:lujun9972 译者:FSSlc 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

设计模式可以帮助消除冗余代码。学习如何利用 Java 使用单例模式、工厂模式和观察者模式。

如果你是一名正在致力于计算机科学或者相关学科的程序员或者学生,很快,你将会遇到一条术语 “ 软件设计模式 software design pattern ”。根据维基百科,“软件设计模式是在平常的软件设计工作中所遭遇的问题的一种通用的、可重复使用的解决方案”。我对该定义的理解是:当在从事于一个编码项目时,你经常会思考,“嗯,这里貌似是冗余代码,我觉得是否能改变这些代码使之更灵活和便于修改?”因此,你会开始考虑怎样分割那些保持不变的内容和需要经常改变的内容。

设计模式是一种通过分割那些保持不变的部分和经常变化的部分,让你的代码更容易修改的方法。

不出意外的话,每个从事编程项目的人都可能会有同样的思考。特别是那些工业级别的项目,在那里通常工作着数十甚至数百名开发者;协作过程表明必须有一些标准和规则来使代码更加优雅并适应变化。这就是为什么我们有了 面向对象编程(OOP)和 软件框架工具。设计模式有点类似于 OOP,但它通过将变化视为自然开发过程的一部分而进一步发展。基本上,设计模式利用了一些 OOP 的思想,比如抽象和接口,但是专注于改变的过程。

当你开始开发项目时,你经常会听到这样一个术语重构,它意味着通过改变代码使它变得更优雅和可复用;这就是设计模式耀眼的地方。当你处理现有代码时(无论是由其他人构建还是你自己过去构建的),了解设计模式可以帮助你以不同的方式看待事物,你将发现问题以及改进代码的方法。

有很多种设计模式,其中单例模式、工厂模式和观察者模式三种最受欢迎,在这篇文章中我将会一一介绍它们。

如何遵循本指南

无论你是一位有经验的编程工作者还是一名刚刚接触的新手,我想让这篇教程让每个人都很容易理解。设计模式概念并不容易理解,减少开始旅程时的学习曲线始终是首要任务。因此,除了这篇带有图表和代码片段的文章外,我还创建了一个 GitHub 仓库,你可以克隆仓库并在你的电脑上运行这些代码来实现这三种设计模式。你也可以观看我创建的 YouTube视频

必要条件

如果你只是想了解一般的设计模式思想,则无需克隆示例项目或安装任何工具。但是,如果要运行示例代码,你需要安装以下工具:

  • Java 开发套件(JDK):我强烈建议使用 OpenJDK
  • Apache Maven:这个简单的项目使用 Apache Maven 构建;好的是许多 IDE 自带了Maven。
  • 交互式开发编辑器(IDE):我使用 社区版 IntelliJ,但是你也可以使用 Eclipse IDE 或者其他你喜欢的 Java IDE。
  • Git:如果你想克隆这个工程,你需要 Git 客户端。

安装好 Git 后运行下列命令克隆这个工程:

git clone https://github.com/bryantson/OpensourceDotComDemos.git

然后在你喜欢的 IDE 中,你可以将 TopDesignPatterns 仓库中的代码作为 Apache Maven 项目导入。

我使用的是 Java,但你也可以使用支持抽象原则)的任何编程语言来实现设计模式。

单例模式:避免每次创建一个对象

单例模式 singleton pattern 是非常流行的设计模式,它的实现相对来说很简单,因为你只需要一个类。然而,许多开发人员争论单例设计模式的是否利大于弊,因为它缺乏明显的好处并且容易被滥用。很少有开发人员直接实现单例;相反,像 Spring Framework 和 Google Guice 等编程框架内置了单例设计模式的特性。

但是了解单例模式仍然有巨大的用处。单例模式确保一个类仅创建一次且提供了一个对它的全局访问点。

单例模式:确保仅创建一个实例且避免在同一个项目中创建多个实例。

下面这幅图展示了典型的类对象创建过程。当客户端请求创建一个对象时,构造函数会创建或者实例化一个对象并调用方法返回这个类给调用者。但是每次请求一个对象都会发生这样的情况:构造函数被调用,一个新的对象被创建并且它返回了一个独一无二的对象。我猜面向对象语言的创建者有每次都创建一个新对象的理由,但是单例过程的支持者说这是冗余的且浪费资源。

 title=

下面这幅图使用单例模式创建对象。这里,构造函数仅当对象首次通过调用预先设计好的 getInstance() 方法时才会被调用。这通常通过检查该值是否为 null 来完成,并且这个对象被作为私有变量保存在单例类的内部。下次 getInstance() 被调用时,这个类会返回第一次被创建的对象。而没有新的对象产生;它只是返回旧的那一个。

 title=

下面这段代码展示了创建单例模式最简单的方法:

package org.opensource.demo.singleton;

public class OpensourceSingleton {

    private static OpensourceSingleton uniqueInstance;

    private OpensourceSingleton() {
    }

    public static OpensourceSingleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new OpensourceSingleton();
        }
        return uniqueInstance;
    }

}

在调用方,这里展示了如何调用单例类来获取对象:

Opensource newObject = Opensource.getInstance();

这段代码很好的验证了单例模式的思想:

  1. getInstance() 被调用时,它通过检查 null 值来检查对象是否已经被创建。
  2. 如果值为 null,它会创建一个新对象并把它保存到私有域,返回这个对象给调用者。否则直接返回之前被创建的对象。

单例模式实现的主要问题是它忽略了并行进程。当多个进程使用线程同时访问资源时,这个问题就产生了。对于这种情况有对应的解决方案,它被称为双重检查锁,用于多线程安全,如下所示:

package org.opensource.demo.singleton;

public class ImprovedOpensourceSingleton {

    private volatile static ImprovedOpensourceSingleton uniqueInstance;

    private ImprovedOpensourceSingleton() {}

    public static ImprovedOpensourceSingleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (ImprovedOpensourceSingleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new ImprovedOpensourceSingleton();
                }
            }
        }
        return uniqueInstance;
    }

}

再强调一下前面的观点,确保只有在你认为这是一个安全的选择时才直接实现你的单例模式。最好的方法是通过使用一个制作精良的编程框架来利用单例功能。

工厂模式:将对象创建委派给工厂类以隐藏创建逻辑

工厂模式 factory pattern 是另一种众所周知的设计模式,但是有一小点复杂。实现工厂模式的方法有很多,而下列的代码示例为最简单的实现方式。为了创建对象,工厂模式定义了一个接口,让它的子类去决定实例化哪一个类。

工厂模式:将对象创建委派给工厂类,因此它能隐藏创建逻辑。

下列的图片展示了最简单的工厂模式是如何实现的。

 title=

客户端请求工厂类创建类型为 x 的某个对象,而不是客户端直接调用对象创建。根据其类型,工厂模式决定要创建和返回的对象。

在下列代码示例中,OpensourceFactory 是工厂类实现,它从调用者那里获取类型并根据该输入值决定要创建和返回的对象:

package org.opensource.demo.factory;

public class OpensourceFactory {

    public OpensourceJVMServers getServerByVendor(String name) {
        if(name.equals("Apache")) {
            return new Tomcat();
        }
        else if(name.equals("Eclipse")) {
            return new Jetty();
        }
        else if (name.equals("RedHat")) {
            return new WildFly();
        }
        else {
            return null;
        }
    }
}

OpenSourceJVMServer 是一个 100% 的抽象类(即接口类),它指示要实现的是什么,而不是怎样实现:

package org.opensource.demo.factory;

public interface OpensourceJVMServers {
    public void startServer();
    public void stopServer();
    public String getName();
}

这是一个 OpensourceJVMServers 类的实现示例。当 RedHat 被作为类型传递给工厂类,WildFly 服务器将被创建:

package org.opensource.demo.factory;

public class WildFly implements OpensourceJVMServers {
    public void startServer() {
        System.out.println("Starting WildFly Server...");
    }

    public void stopServer() {
        System.out.println("Shutting Down WildFly Server...");
    }

    public String getName() {
        return "WildFly";
    }
}

观察者模式:订阅主题并获取相关更新的通知

最后是 观察者模式 observer pattern 。像单例模式那样,很少有专业的程序员直接实现观察者模式。但是,许多消息队列和数据服务实现都借用了观察者模式的概念。观察者模式在对象之间定义了一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将被自动地通知和更新。

观察者模式:如果有更新,那么订阅了该话题/主题的客户端将被通知。

理解观察者模式的最简单方法是想象一个邮件列表,你可以在其中订阅任何主题,无论是开源、技术、名人、烹饪还是您感兴趣的任何其他内容。每个主题维护者一个它的订阅者列表,在观察者模式中它们相当于观察者。当某一个主题更新时,它所有的订阅者(观察者)都将被通知这次改变。并且订阅者总是能取消某一个主题的订阅。

如下图所示,客户端可以订阅不同的主题并添加观察者以获得最新信息的通知。因为观察者不断的监听着这个主题,这个观察者会通知客户端任何发生的改变。

 title=

让我们来看看观察者模式的代码示例,从主题/话题类开始:

package org.opensource.demo.observer;

public interface Topic {

    public void addObserver(Observer observer);
    public void deleteObserver(Observer observer);
    public void notifyObservers();
}

这段代码描述了一个为不同的主题去实现已定义方法的接口。注意一个观察者如何被添加、移除和通知的。

这是一个主题的实现示例:

package org.opensource.demo.observer;

import java.util.List;
import java.util.ArrayList;

public class Conference implements Topic {
    private List<Observer> listObservers;
    private int totalAttendees;
    private int totalSpeakers;
    private String nameEvent;

    public Conference() {
        listObservers = new ArrayList<Observer>();
    }

    public void addObserver(Observer observer) {
        listObservers.add(observer);
    }

    public void deleteObserver(Observer observer) {
        int i = listObservers.indexOf(observer);
        if (i >= 0) {
            listObservers.remove(i);
        }
    }

    public void notifyObservers() {
        for (int i=0, nObservers = listObservers.size(); i < nObservers; ++ i) {
            Observer observer = listObservers.get(i);
            observer.update(totalAttendees,totalSpeakers,nameEvent);
        }
    }

    public void setConferenceDetails(int totalAttendees, int totalSpeakers, String nameEvent) {
        this.totalAttendees = totalAttendees;
        this.totalSpeakers = totalSpeakers;
        this.nameEvent = nameEvent;
        notifyObservers();
    }
}

这段代码定义了一个特定主题的实现。当发生改变时,这个实现调用它自己的方法。注意这将获取观察者的数量,它以列表方式存储,并且可以通知和维护观察者。

这是一个观察者类:

package org.opensource.demo.observer;

public interface Observer {
    public void update(int totalAttendees, int totalSpeakers, String nameEvent);
}

这个类定义了一个接口,不同的观察者可以实现该接口以执行特定的操作。

例如,实现了该接口的观察者可以在会议上打印出与会者和发言人的数量:

package org.opensource.demo.observer;

public class MonitorConferenceAttendees implements Observer {
    private int totalAttendees;
    private int totalSpeakers;
    private String nameEvent;
    private Topic topic;

    public MonitorConferenceAttendees(Topic topic) {
        this.topic = topic;
        topic.addObserver(this);
    }

    public void update(int totalAttendees, int totalSpeakers, String nameEvent) {
        this.totalAttendees = totalAttendees;
        this.totalSpeakers = totalSpeakers;
        this.nameEvent = nameEvent;
        printConferenceInfo();
    }

    public void printConferenceInfo() {
        System.out.println(this.nameEvent + " has " + totalSpeakers + " speakers and " + totalAttendees + " attendees");
    }
}

接下来

现在你已经阅读了这篇对于设计模式的介绍引导,你还可以去寻求了解其他设计模式,例如外观模式,模版模式和装饰器模式。也有一些并发和分布式系统的设计模式如断路器模式和锚定模式。

可是,我相信最好的磨砺你的技能的方式首先是通过在你的业余项目或者练习中实现这些设计模式。你甚至可以开始考虑如何在实际项目中应用这些设计模式。接下来,我强烈建议你查看 OOP 的 SOLID 原则。之后,你就准备好了解其他设计模式。


via: https://opensource.com/article/19/7/understanding-software-design-patterns

作者:Bryant Son 选题:lujun9972 译者:arrowfeng 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

这篇文章是关于如何在使用 cp 命令进行备份以及同步时提高效率。

去年七月,我写了一篇关于 cp 命令的两种绝佳用法的文章:备份一个文件,以及同步一个文件夹的备份。

虽然这些工具确实很好用,但同时,输入这些命令太过于累赘了。为了解决这个问题,我在我的 Bash 启动文件里创建了一些 Bash 快捷方式。现在,我想把这些捷径分享给你们,以便于你们在需要的时候可以拿来用,或者是给那些还不知道怎么使用 Bash 的别名以及函数的用户提供一些思路。

使用 Bash 别名来更新一个文件夹的副本

如果要使用 cp 来更新一个文件夹的副本,通常会使用到的命令是:

cp -r -u -v SOURCE-FOLDER DESTINATION-DIRECTORY

其中 -r 代表“向下递归访问文件夹中的所有文件”,-u 代表“更新目标”,-v 代表“详细模式”,SOURCE-FOLDER 是包含最新文件的文件夹的名称,DESTINATION-DIRECTORY 是包含必须同步的SOURCE-FOLDER 副本的目录。

因为我经常使用 cp 命令来复制文件夹,我会很自然地想起使用 -r 选项。也许再想地更深入一些,我还可以想起用 -v 选项,如果再想得再深一层,我会想起用选项 -u(不知道这个选项是代表“更新”还是“同步”还是一些什么其它的)。

或者,还可以使用Bash 的别名功能来将 cp 命令以及其后的选项转换成一个更容易记忆的单词,就像这样:

alias sync='cp -r -u -v'

如果我将其保存在我的主目录中的 .bash_aliases 文件中,然后启动一个新的终端会话,我可以使用该别名了,例如:

sync Pictures /media/me/4388-E5FE

可以将我的主目录中的图片文件夹与我的 USB 驱动器中的相同版本同步。

不清楚 sync 是否已经定义了?你可以在终端里输入 alias 这个单词来列出所有正在使用的命令别名。

喜欢吗?想要现在就立即使用吗?那就现在打开终端,输入:

echo "alias sync='cp -r -u -v'" >> ~/.bash_aliases

然后启动一个新的终端窗口并在命令提示符下键入 alias。你应该看到这样的东西:

me@mymachine~$ alias

alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias gvm='sdk'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
alias sync='cp -r -u -v'
me@mymachine:~$

这里你能看到 sync 已经定义了。

使用 Bash 函数来为备份编号

若要使用 cp 来备份一个文件,通常使用的命令是:

cp --force --backup=numbered WORKING-FILE BACKED-UP-FILE

其中 --force 代表“强制制作副本”,--backup= numbered 代表“使用数字表示备份的生成”,WORKING-FILE 是我们希望保留的当前文件,BACKED-UP-FILEWORKING-FILE 的名称相同,并附加生成信息。

我们不仅需要记得所有 cp 的选项,我们还需要记得去重复输入 WORKING-FILE 的名字。但当Bash 的函数功能已经可以帮我们做这一切,为什么我们还要不断地重复这个过程呢?就像这样:

再一次提醒,你可将下列内容保存入你在家目录下的 .bash_aliases 文件里:

function backup {
    if [ $# -ne 1 ]; then
        echo "Usage: $0 filename"
    elif [ -f $1 ] ; then
        echo "cp --force --backup=numbered $1 $1"
        cp --force --backup=numbered $1 $1
    else
        echo "$0: $1 is not a file"
    fi
}

我将此函数称之为 backup,因为我的系统上没有任何其他名为 backup 的命令,但你可以选择适合的任何名称。

第一个 if 语句是用于检查是否提供有且只有一个参数,否则,它会用 echo 命令来打印出正确的用法。

elif 语句是用于检查提供的参数所指向的是一个文件,如果是的话,它会用第二个 echo 命令来打印所需的 cp 的命令(所有的选项都是用全称来表示)并且执行它。

如果所提供的参数不是一个文件,文件中的第三个 echo 用于打印错误信息。

在我的家目录下,如果我执行 backup 这个命令,我可以发现目录下多了一个文件名为checkCounts.sql.~1~ 的文件,如果我再执行一次,便又多了另一个名为 checkCounts.sql.~2~ 的文件。

成功了!就像所想的一样,我可以继续编辑 checkCounts.sql,但如果我可以经常地用这个命令来为文件制作快照的话,我可以在我遇到问题的时候回退到最近的版本。

也许在未来的某个时间,使用 git 作为版本控制系统会是一个好主意。但像上文所介绍的 backup 这个简单而又好用的工具,是你在需要使用快照的功能时却还未准备好使用 git 的最好工具。

结论

在我的上一篇文章里,我保证我会通过使用脚本,shell 里的函数以及别名功能来简化一些机械性的动作来提高生产效率。

在这篇文章里,我已经展示了如何在使用 cp 命令同步或者备份文件时运用 shell 函数以及别名功能来简化操作。如果你想要了解更多,可以读一下这两篇文章:怎样通过使用命令别名功能来减少敲击键盘的次数 以及由我的同事 Greg 和 Seth 写的 Shell 编程:shift 方法和自定义函数介绍


via: https://opensource.com/article/18/1/two-great-uses-cp-command-update

作者:Chris Hermansen 译者:zyk2290 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

当你用电子邮件系统发送自动回复时,你需要注意不要向自动生成的电子邮件发送回复。最好的情况下,你将获得无用的投递失败消息。更可能的是,你会得到一个无限的电子邮件循环和一个混乱的世界。

事实证明,可靠地检测自动生成的电子邮件并不总是那么容易。以下是基于为此编写的检测器并使用它扫描大约 100,000 封电子邮件(大量的个人存档和公司存档)的观察结果。

Auto-submitted 信头

RFC 3834 定义。

这是表示你的邮件是自动回复的“官方”标准。如果存在 Auto-Submitted 信头,并且其值不是 no,你应该发送回复。

X-Auto-Response-Suppress 信头

由微软.aspx)定义。

此信头由微软 Exchange、Outlook 和其他一些产品使用。许多新闻订阅等都设定了这个。如果 X-Auto-Response-Suppress 包含 DR(“抑制投递报告”)、AutoReply(“禁止 OOF 通知以外的自动回复消息”)或 All,你应该发送回复。

List-Id 和 List-Unsubscribe 信头

RFC 2919) 定义。

你通常不希望给邮件列表或新闻订阅发送自动回复。几乎所有的邮件列表和大多数新闻订阅都至少设置了其中一个信头。如果存在这些信头中的任何一个,你应该发送回复。这个信头的值不重要。

Feedback-ID 信头

由谷歌定义。

Gmail 使用此信头识别邮件是否是新闻订阅,并使用它为这些新闻订阅的所有者生成统计信息或报告。如果此信头存在,你应该发送回复。这个信头的值不重要。

非标准方式

上述方法定义明确(即使有些是非标准的)。不幸的是,有些电子邮件系统不使用它们中的任何一个 :-( 这里有一些额外的措施。

Precedence 信头

RFC 2076 中没有真正定义,不鼓励使用它(但通常会遇到此信头)。

请注意,不建议检查是否存在此信头,因为某些邮件使用 normal 和其他一些(少见的)值(尽管这不常见)。

我的建议是如果其值不区分大小写地匹配 bulkauto_replylist,则发送回复。

其他不常见的信头

这是我遇到的另外的一些(不常见的)信头。如果设置了其中一个,我建议发送自动回复。大多数邮件也设置了上述信头之一,但有些没有(这并不常见)。

  • X-MSFBL:无法真正找到定义(Microsoft 信头?),但我只有自动生成的邮件带有此信头。
  • X-Loop:在任何地方都没有真正定义过,有点罕见,但有时有。它通常设置为不应该收到电子邮件的地址,但也会遇到 X-Loop: yes
  • X-Autoreply:相当罕见,并且似乎总是具有 yes 的值。

Email 地址

检查 FromReply-To 信头是否包含 noreplyno-replyno_reply(正则表达式:^no.?reply@)。

只有 HTML 部分

如果电子邮件只有 HTML 部分,而没有文本部分,则表明这是一个自动生成的邮件或新闻订阅。几乎所有邮件客户端都设置了文本部分。

投递失败消息

许多传递失败消息并不能真正表明它们是失败的。一些检查方法:

  • From 包含 mailer-daemonMail Delivery Subsystem

特定的邮件库特征

许多邮件类库留下了某种痕迹,大多数常规邮件客户端使用自己的数据覆盖它。检查这个似乎工作得相当可靠。

  • X-Mailer: Microsoft CDO for Windows 2000:由某些微软软件设置;我只能在自动生成的邮件中找到它。是的,在 2015 年它仍然在使用。
  • Message-ID 信头包含 .JavaMail.:我发现了一些(5 个 50k 大小的)常规消息,但不是很多;绝大多数(数千封)邮件是新闻订阅、订单确认等。
  • ^X-MailerPHP 开头。这应该会同时看到 X-Mailer: PHP/5.5.0X-Mailer: PHPmailer XXX XXX。与 “JavaMail” 相同。
  • 出现了 X-Library;似乎只有 Indy 设定了这个。
  • X-Mailerwdcollect 开头。由一些 Plesk 邮件设置。
  • X-MailerMIME-tools 开头。

最后的预防措施:限制回复的数量

即使遵循上述所有建议,你仍可能会遇到一个避开所有这些检测的电子邮件程序。这可能非常危险,因为电子邮件系统只是“如果有电子邮件那么发送”,就有可能导致无限的电子邮件循环。

出于这个原因,我建议你记录你自动发送的电子邮件,并将此速率限制为在几分钟内最多几封电子邮件。这将打破循环链条。

我们使用每五分钟一封电子邮件的设置,但没这么严格的设置可能也会运作良好。

你需要为自动回复设置什么信头

具体细节取决于你发送的邮件类型。这是我们用于自动回复邮件的内容:

Auto-Submitted: auto-replied
X-Auto-Response-Suppress: All
Precedence: auto_reply

反馈

你可以发送电子邮件至 [email protected]创建 GitHub 议题以提交反馈、问题等。


via: https://arp242.net/weblog/autoreply.html

作者:Martin Tournoij 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

目前市场上有许多开源监测工具可用于监控 Linux 系统的性能。当系统到达指定的阈值时,它将发送邮件提醒。

它会监控 CPU 利用率、内存利用率、交换内存利用率、磁盘空间利用率等所有内容。但我不认为它们可以选择监控新用户创建活动,并发送提醒。

如果没有,这并不重要,因为我们可以编写自己的 bash 脚本来实现这一点。

我们过去写了许多有用的 shell 脚本。如果要查看它们,请点击以下链接。

这个脚本做了什么?它监测 /var/log/secure 文件,并在系统创建新帐户时提醒管理员。

我们不会经常运行此脚本,因为创建用户不经常发生。但是,我打算一天运行一次这个脚本。因此,我们可以获得有关用户创建的综合报告。

如果在昨天的 /var/log/secure 中找到了 “useradd” 字符串,那么该脚本将向指定的邮箱发送邮件提醒,其中包含了新用户的详细信息。

注意:你需要更改邮箱而不是使用我们的邮箱。

# vi /opt/scripts/new-user.sh
#!/bin/bash

#Set the variable which equal to zero
prev_count=0
count=$(grep -i "`date --date='yesterday' '+%b %e'`" /var/log/secure | egrep -wi 'useradd' | wc -l)

if [ "$prev_count" -lt "$count" ] ; then
  # Send a mail to given email id when errors found in log
  SUBJECT="ATTENTION: New User Account is created on server : `date --date='yesterday' '+%b %e'`"
  # This is a temp file, which is created to store the email message.
  MESSAGE="/tmp/new-user-logs.txt"
  TO="[email protected]"
  echo  "Hostname: `hostname`" >> $MESSAGE
  echo -e "\n" >> $MESSAGE
  echo "The New User Details are below." >> $MESSAGE
  echo "+------------------------------+" >> $MESSAGE
  grep -i "`date --date='yesterday' '+%b %e'`" /var/log/secure | egrep -wi 'useradd' | grep -v 'failed adding'| awk '{print $4,$8}' | uniq | sed 's/,/ /' >>  $MESSAGE
  echo "+------------------------------+" >> $MESSAGE
  mail -s "$SUBJECT" "$TO" < $MESSAGE
  rm $MESSAGE
fi

new-user.sh 添加可执行权限。

$ chmod +x /opt/scripts/new-user.sh

最后添加一个 cron 任务来自动化执行它。它会在每天 7 点运行。

# crontab -e

0 7 * * * /bin/bash /opt/scripts/new-user.sh

注意:你将在每天 7 点收到一封邮件提醒,但这是昨天的日志。

你将会看到类似下面的邮件提醒。

# cat /tmp/logs.txt

Hostname: 2g.server10.com

The New User Details are below.
+------------------------------+
2g.server10.com name=magesh
2g.server10.com name=daygeek
+------------------------------+

via: https://www.2daygeek.com/linux-bash-script-to-monitor-user-creation-send-email/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

JSON 是一种轻量级且与语言无关的数据存储格式,易于与大多数编程语言集成,也易于人类理解 —— 当然,如果格式正确的话。JSON 这个词代表 Java Script Object Notation,虽然它以 JavaScript 开头,而且主要用于在服务器和浏览器之间交换数据,但现在正在用于许多领域,包括嵌入式系统。在这里,我们将使用 Linux 上的命令行工具解析并格式化打印 JSON。它对于在 shell 脚本中处理大型 JSON 数据或在 shell 脚本中处理 JSON 数据非常有用。

什么是格式化输出?

JSON 数据的结构更具人性化。但是在大多数情况下,JSON 数据会存储在一行中,甚至没有行结束字符。

显然,这对于手动阅读和编辑不太方便。

这是 格式化输出 pretty print 就很有用。这个该名称不言自明:重新格式化 JSON 文本,使人们读起来更清晰。这被称为 JSON 格式化输出

用 Linux 命令行工具解析和格式化输出 JSON

可以使用命令行文本处理器解析 JSON 数据,例如 awksedgerp。实际上 JSON.awk 是一个来做这个的 awk 脚本。但是,也有一些专用工具可用于同一目的。

  1. jqjshon,shell 下的 JSON 解析器,它们都非常有用。
  2. Shell 脚本,如 JSON.shjsonv.sh,用于在 bash、zsh 或 dash shell 中解析JSON。
  3. JSON.awk,JSON 解析器 awk 脚本。
  4. json.tool 这样的 Python 模块。
  5. undercore-cli,基于 Node.js 和 javascript。

在本教程中,我只关注 jq,这是一个 shell 下的非常强大的 JSON 解析器,具有高级过滤和脚本编程功能。

JSON 格式化输出

JSON 数据可能放在一行上使人难以解读,因此为了使其具有一定的可读性,JSON 格式化输出就可用于此目的的。

示例:来自 jsonip.com 的数据,使用 curlwget 工具获得 JSON 格式的外部 IP 地址,如下所示。

$ wget -cq http://jsonip.com/ -O -

实际数据看起来类似这样:

{"ip":"111.222.333.444","about":"/about","Pro!":"http://getjsonip.com"}

现在使用 jq 格式化输出它:

$ wget -cq http://jsonip.com/ -O - | jq '.'

通过 jq 过滤了该结果之后,它应该看起来类似这样:

{
   "ip": "111.222.333.444",
   "about": "/about",
   "Pro!": "http://getjsonip.com"
}

同样也可以通过 Python json.tool 模块做到。示例如下:

$ cat anything.json | python -m json.tool

这种基于 Python 的解决方案对于大多数用户来说应该没问题,但是如果没有预安装或无法安装 Python 则不行,比如在嵌入式系统上。

然而,json.tool Python 模块具有明显的优势,它是跨平台的。因此,你可以在 Windows、Linux 或 Mac OS 上无缝使用它。

如何用 jq 解析 JSON

首先,你需要安装 jq,它已被大多数 GNU/Linux 发行版选中,并使用各自的软件包安装程序命令进行安装。

在 Arch Linux 上:

$ sudo pacman -S jq

在 Debian、Ubuntu、Linux Mint 上:

$ sudo apt-get install jq

在 Fedora 上:

$ sudo dnf install jq

在 openSUSE 上:

$ sudo zypper install jq

对于其它操作系统或平台参见官方的安装指导

jq 的基本过滤和标识符功能

jq 可以从 STDIN 或文件中读取 JSON 数据。你可以根据情况使用。

单个符号 . 是最基本的过滤器。这些过滤器也称为对象标识符-索引jq 使用单个 . 过滤器基本上相当将输入的 JSON 文件格式化输出。

  • 单引号:不必始终使用单引号。但是如果你在一行中组合几个过滤器,那么你必须使用它们。
  • 双引号:你必须用两个双引号括起任何特殊字符,如 @$,例如 jq .foo.”@bar”
  • 原始数据打印:不管出于任何原因,如果你只需要最终解析的数据(不包含在双引号内),请使用带有 -r 标志的 jq 命令,如下所示:jq -r .foo.bar

解析特定数据

要过滤出 JSON 的特定部分,你需要了解格式化输出的 JSON 文件的数据层次结构。

来自维基百科的 JSON 数据示例:

{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021"
},
  "phoneNumber": [
{
  "type": "home",
  "number": "212 555-1234"
},
{
  "type": "fax",
  "number": "646 555-4567"
}
],
  "gender": {
  "type": "male"
  }
}

我将在本教程中将此 JSON 数据用作示例,将其保存为 sample.json

假设我想从 sample.json 文件中过滤出地址。所以命令应该是这样的:

$ jq .address sample.json

示例输出:

{
  "streetAddress": "21 2nd Street",
  "city": "New York",
  "state": "NY",
  "postalCode": "10021"
}

再次,我想要邮政编码,然后我要添加另一个对象标识符-索引,即另一个过滤器。

$ cat sample.json | jq .address.postalCode

另请注意,过滤器区分大小写,并且你必须使用完全相同的字符串来获取有意义的输出,否则就是 null。

从 JSON 数组中解析元素

JSON 数组的元素包含在方括号内,这无疑是非常通用的。

要解析数组中的元素,你必须使用 [] 标识符以及其他对象标识符索引。

在此示例 JSON 数据中,电话号码存储在数组中,要从此数组中获取所有内容,你只需使用括号,像这个示例:

$ jq .phoneNumber[] sample.json

假设你只想要数组的第一个元素,然后使用从 0 开始的数组对象编号,对于第一个项目,使用 [0],对于下一个项目,它应该每步增加 1。

$ jq .phoneNumber[0] sample.json

脚本编程示例

假设我只想要家庭电话,而不是整个 JSON 数组数据。这就是用 jq 命令脚本编写的方便之处。

$ cat sample.json | jq -r '.phoneNumber[] | select(.type == "home") | .number'

首先,我将一个过滤器的结果传递给另一个,然后使用 select 属性选择特定类型的数据,再次将结果传递给另一个过滤器。

解释每种类型的 jq 过滤器和脚本编程超出了本教程的范围和目的。强烈建议你阅读 jq 手册,以便更好地理解下面的内容。

资源:


via: https://www.ostechnix.com/how-to-parse-and-pretty-print-json-with-linux-commandline-tools/

作者:ostechnix 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出