月度归档: 2022 年 7 月

  • Linux tar.gz、tar、bz2、zip 等解压缩、压缩命令详解

    Linux tar.gz、tar、bz2、zip 等解压缩、压缩命令详解

    分类 编程技术

    Linux 常用的压缩与解压缩命令有:tar、gzip、gunzip、bzip2、bunzip2、compress 、uncompress、 zip、 unzip、rar、unrar 等。


    tar

    参数:

    • -z 用gzip指令处理备份文件
    • -v 显示执行过程
    • -f 指定备份文件
    • -c 建立备份文件
    • -t 列出备份文件的内容
    • -x 从备份文件中还原文件
    • -p 保留权限
    • –exclude 排除指定文件

    最常用的打包命令是 tar,使用 tar 程序打出来的包我们常称为 tar 包,tar 包文件的命令通常都是以 .tar 结尾的。生成 tar 包后,就可以用其它的程序来进行压缩了,所以首先就来讲讲 tar 命令的基本用法。

    tar 命令的选项有很多(用 man tar 可以查看到),但常用的就那么几个选项,下面来举例说明一下:

    # tar -cf all.tar *.jpg

    这条命令是将所有 .jpg 的文件打成一个名为 all.tar 的包。-c 是表示产生新的包,-f 指定包的文件名。

    # tar -rf all.tar *.gif

    这条命令是将所有 .gif 的文件增加到 all.tar 的包里面去,-r 是表示增加文件的意思。

    # tar -uf all.tar logo.gif

    这条命令是更新原来 tar 包 all.tar 中 logo.gif 文件,-u 是表示更新文件的意思。

    # tar -tf all.tar

    这条命令是列出 all.tar 包中所有文件,-t 是列出文件的意思。

    # tar -xf all.tar

    这条命令是解出 all.tar 包中所有文件,-x 是解开的意思。

    以上就是 tar 的最基本的用法。为了方便用户在打包解包的同时可以压缩或解压文件,tar 提供了一种特殊的功能。这就是 tar 可以在打包或解包的同时调用其它的压缩程序,比如调用 gzip、bzip2 等。

    1) tar调用

    gzip 是 GNU 组织开发的一个压缩程序,.gz 结尾的文件就是 gzip 压缩的结果。与 gzip 相对的解压程序是 gunzip。tar 中使用 -z 这个参数来调用gzip。下面来举例说明一下:

    # tar -czf all.tar.gz *.jpg

    这条命令是将所有 .jpg 的文件打成一个 tar 包,并且将其用 gzip 压缩,生成一个 gzip 压缩过的包,包名为 all.tar.gz。

    # tar -xzf all.tar.gz

    这条命令是将上面产生的包解开。

    2) tar 调用 bzip2

    bzip2 是一个压缩能力更强的压缩程序,.bz2 结尾的文件就是 bzip2 压缩的结果。

    与 bzip2 相对的解压程序是 bunzip2。tar 中使用 -j 这个参数来调用 gzip。下面来举例说明一下:

    # tar -cjf all.tar.bz2 *.jpg

    这条命令是将所有 .jpg 的文件打成一个 tar 包,并且将其用 bzip2 压缩,生成一个 bzip2 压缩过的包,包名为 all.tar.bz2

    # tar -xjf all.tar.bz2

    这条命令是将上面产生的包解开。

    3)tar 调用 compress

    compress 也是一个压缩程序,但是好象使用 compress 的人不如 gzip 和 bzip2 的人多。.Z 结尾的文件就是 bzip2 压缩的结果。与 compress 相对的解压程序是 uncompress。tar 中使用 -Z 这个参数来调用 compress。下面来举例说明一下:

    # tar -cZf all.tar.Z *.jpg

    这条命令是将所有 .jpg 的文件打成一个 tar 包,并且将其用 compress 压缩,生成一个 uncompress 压缩过的包,包名为 all.tar.Z。

    # tar -xZf all.tar.Z

    这条命令是将上面产生的包解开。

    有了上面的知识,你应该可以解开多种压缩文件了,下面对于 tar 系列的压缩文件作一个小结:

    1) 对于.tar结尾的文件

    tar -xf all.tar

    2) 对于 .gz 结尾的文件

    gzip -d all.gz
    gunzip all.gz

    3)对于 .tgz 或 .tar.gz 结尾的文件

    tar -xzf all.tar.gz
    tar -xzf all.tgz

    4) 对于 .bz2 结尾的文件

    bzip2 -d all.bz2
    bunzip2 all.bz2

    5) 对于 tar.bz2 结尾的文件

    tar -xjf all.tar.bz2

    6) 对于 .Z 结尾的文件

    uncompress all.Z

    7) 对于 .tar.Z 结尾的文件

    tar -xZf all.tar.z

    另外对于 Windows 下的常见压缩文件 .zip 和 .rar,Linux 也有相应的方法来解压它们:

    1) 对于 .zip

    linux 下提供了 zip 和 unzip 程序,zip 是压缩程序,unzip 是解压程序。它们的参数选项很多,这里只做简单介绍,依旧举例说明一下其用法:

    # zip all.zip *.jpg

    这条命令是将所有 .jpg 的文件压缩成一个 zip 包:

    # unzip all.zip

    这条命令是将 all.zip 中的所有文件解压出来。

    2) 对于 .rar

    要在 linux 下处理 .rar 文件,需要安装 RAR for Linux。下载地址:http://www.rarsoft.com/download.htm,下载后安装即可。

    # tar -xzpvf rarlinux-x64-5.6.b5.tar.gz
    # cd rar 
    # make

    这样就安装好了,安装后就有了 rar 和 unrar 这两个程序,rar 是压缩程序,unrar 是解压程序。它们的参数选项很多,这里只做简单介绍,依旧举例说明一下其用法:

    # rar a all *.jpg

    这条命令是将所有 .jpg 的文件压缩成一个 rar 包,名为 all.rar,该程序会将 .rar 扩展名将自动附加到包名后。

    # unrar e all.rar

    这条命令是将 all.rar 中的所有文件解压出来:


    扩展内容

    tar

    -c: 建立压缩档案 
    -x:解压 
    -t:查看内容 
    -r:向压缩归档文件末尾追加文件 
    -u:更新原压缩包中的文件

    这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个。下面的参数是根据需要在压缩或解压档案时可选的。

    -z:有gzip属性的 
    -j:有bz2属性的 
    -Z:有compress属性的 
    -v:显示所有过程 
    -O:将文件解开到标准输出 

    下面的参数 -f 是必须的:

    -f: 使用档案名字,切记,这个参数是最后一个参数,后面只能接档案名。 

    # tar -cf all.tar *.jpg

    这条命令是将所有 .jpg 的文件打成一个名为 all.tar 的包。-c 是表示产生新的包,-f 指定包的文件名。

    # tar -rf all.tar *.gif 

    这条命令是将所有 .gif 的文件增加到 all.tar 的包里面去。-r 是表示增加文件的意思。

    # tar -uf all.tar logo.gif 

    这条命令是更新原来 tar 包 all.tar 中 logo.gif 文件,-u 是表示更新文件的意思。

    # tar -tf all.tar 

    这条命令是列出 all.tar 包中所有文件,-t 是列出文件的意思。

    # tar -xf all.tar 

    这条命令是解出 all.tar 包中所有文件,-x 是解开的意思。

    压缩

    tar –cvf jpg.tar *.jpg       // 将目录里所有jpg文件打包成 tar.jpg 
    tar –czf jpg.tar.gz *.jpg    // 将目录里所有jpg文件打包成 jpg.tar 后,并且将其用 gzip 压缩,生成一个 gzip 压缩过的包,命名为 jpg.tar.gz 
    tar –cjf jpg.tar.bz2 *.jpg   // 将目录里所有jpg文件打包成 jpg.tar 后,并且将其用 bzip2 压缩,生成一个 bzip2 压缩过的包,命名为jpg.tar.bz2 
    tar –cZf jpg.tar.Z *.jpg     // 将目录里所有 jpg 文件打包成 jpg.tar 后,并且将其用 compress 压缩,生成一个 umcompress 压缩过的包,命名为jpg.tar.Z 
    rar a jpg.rar *.jpg          // rar格式的压缩,需要先下载 rar for linux 
    zip jpg.zip *.jpg            // zip格式的压缩,需要先下载 zip for linux

    解压

    tar –xvf file.tar         // 解压 tar 包 
    tar -xzvf file.tar.gz     // 解压 tar.gz 
    tar -xjvf file.tar.bz2    // 解压 tar.bz2 
    tar –xZvf file.tar.Z      // 解压 tar.Z 
    unrar e file.rar          // 解压 rar 
    unzip file.zip            // 解压 zip 

    总结

    1、*.tar 用 tar –xvf 解压 
    2、*.gz 用 gzip -d或者gunzip 解压 
    3、*.tar.gz和*.tgz 用 tar –xzf 解压 
    4、*.bz2 用 bzip2 -d或者用bunzip2 解压 
    5、*.tar.bz2用tar –xjf 解压 
    6、*.Z 用 uncompress 解压 
    7、*.tar.Z 用tar –xZf 解压 
    8、*.rar 用 unrar e解压 
    9、*.zip 用 unzip 解压
  • 进程管理工具Supervisor使用教程

    Supervisor 是 Linux/Unix 系统下的一个进程管理工具。可以很方便的监听、启动、停止和重启一个或多个进程。通过 Supervisor 管理的进程,当进程意外被 Kill 时,Supervisor 会自动将它重启,可以很方便地做到进程自动恢复的目的,而无需自己编写 shell 脚本来管理进程。

    安装 Supervisor

    这里仅举例 CentOS 系统下的安装方式:

    # 安装 epel 源,如果此前安装过,此步骤跳过
    yum install -y epel-release
    yum install -y supervisor  

    创建一个配置文件

    cp /etc/supervisord.conf /etc/supervisord.d/supervisord.conf

    编辑新复制出来的配置文件 /etc/supervisord.d/supervisord.conf,并在文件结尾处添加以下内容后保存文件:

    # 新建一个应用并设置一个名称,这里设置为 hyperf
    [program:hyperf]
    # 设置命令在指定的目录内执行 
    directory=/var/www/hyperf/ 
    # 这里为您要管理的项目的启动命令 
    command=php ./bin/hyperf.php start 
    # 以哪个用户来运行该进程 
    user=root 
    # supervisor 启动时自动该应用 
    autostart=true 
    # 进程退出后自动重启进程 
    autorestart=true 
    # 进程持续运行多久才认为是启动成功 
    startsecs=1 
    # 重试次数 startretries=3 
    # stderr 日志输出位置 
    stderr_logfile=/var/www/hyperf/runtime/stderr.log 
    # stdout 日志输出位置 
    stdout_logfile=/var/www/hyperf/runtime/stdout.log

    建议同时增大配置文件中的 minfds 配置项,默认为 1024。同时也应该修改系统的 unlimit,防止出现 Failed to open stream: Too many open files 的问题。

    启动 Supervisor

    运行下面的命令基于配置文件启动 Supervisor 程序:

    supervisord -c /etc/supervisord.d/supervisord.conf

    使用 supervisorctl 管理项目

    # 启动 hyperf 应用
    supervisorctl start hyperf
    # 重启 hyperf 应用
    supervisorctl restart hyperf
    # 停止 hyperf 应用
    supervisorctl stop hyperf  
    # 查看所有被管理项目运行状态
    supervisorctl status
    # 重新加载配置文件
    supervisorctl update
    # 重新启动所有程序
    supervisorctl reload
  • 道设计模式面试题总结(含答案解析和思维导图)

    Q1:设计模式有哪些原则?

    开闭原则:OOP 中最基础的原则,指一个软件实体(类、模块、方法等)应该对扩展开放,对修改关闭。强调用抽象构建框架,用实现扩展细节,提高代码的可复用性和可维护性。

    单一职责原则:一个类、接口或方法只负责一个职责,降低代码复杂度以及变更引起的风险。

    依赖倒置原则:程序应该依赖于抽象类或接口,而不是具体的实现类。

    接口隔离原则:将不同功能定义在不同接口中实现接口隔离,避免了类依赖它不需要的接口,减少了接口之间依赖的冗余性和复杂性。

    里氏替换原则:开闭原则的补充,规定了任何父类可以出现的地方子类都一定可以出现,可以约束继承泛滥,加强程序健壮性。

    迪米特原则:也叫最少知道原则,每个模块对其他模块都要尽可能少地了解和依赖,降低代码耦合度。

    合成/聚合原则:尽量使用组合(has-a)/聚合(contains-a)而不是继承(is-a)达到软件复用的目的,避免滥用继承带来的方法污染和方法爆炸,方法污染指父类的行为通过继承传递给子类,但子类并不具备执行此行为的能力;方法爆炸指继承树不断扩大,底层类拥有的方法过于繁杂,导致很容易选择错误。


    Q2:设计模式的分类,你知道哪些设计模式?

    创建型: 在创建对象的同时隐藏创建逻辑,不使用 new 直接实例化对象,程序在判断需要创建哪些对象时更灵活。包括工厂/抽象工厂/单例/建造者/原型模式。

    结构型: 通过类和接口间的继承和引用实现创建复杂结构的对象。包括适配器/桥接模式/过滤器/组合/装饰器/外观/享元/代理模式。

    行为型: 通过类之间不同通信方式实现不同行为。包括责任链/命名/解释器/迭代器/中介者/备忘录/观察者/状态/策略/模板/访问者模式。


    Q3:说一说简单工厂模式

    简单工厂模式指由一个工厂对象来创建实例,客户端不需要关注创建逻辑,只需提供传入工厂的参数。

    适用于工厂类负责创建对象较少的情况,缺点是如果要增加新产品,就需要修改工厂类的判断逻辑,违背开闭原则,且产品多的话会使工厂类比较复杂。

    Calendar 抽象类的 getInstance 方法,调用 createCalendar 方法根据不同的地区参数创建不同的日历对象。

    Spring 中的 BeanFactory 使用简单工厂模式,根据传入一个唯一的标识来获得 Bean 对象。


    Q4:说一说工厂方法模式

    工厂方法模式指定义一个创建对象的接口,让接口的实现类决定创建哪种对象,让类的实例化推迟到子类中进行。

    客户端只需关心对应工厂而无需关心创建细节,主要解决了产品扩展的问题,在简单工厂模式中如果产品种类变多,工厂的职责会越来越多,不便于维护。

    Collection 接口这个抽象工厂中定义了一个抽象的 iterator 工厂方法,返回一个 Iterator 类的抽象产品。该方法通过 ArrayList 、HashMap 等具体工厂实现,返回 Itr、KeyIterator 等具体产品。

    Spring 的 FactoryBean 接口的 getObject 方法也是工厂方法。


    Q5:抽象工厂模式了解吗?

    抽象工厂模式指提供一个创建一系列相关或相互依赖对象的接口,无需指定它们的具体类。

    客户端不依赖于产品类实例如何被创建和实现的细节,主要用于系统的产品有多于一个的产品族,而系统只消费其中某一个产品族产品的情况。抽象工厂模式的缺点是不方便扩展产品族,并且增加了系统的抽象性和理解难度。

    java.sql.Connection 接口就是一个抽象工厂,其中包括很多抽象产品如 Statement、Blob、Savepoint 等。


    Q6:单例模式的特点是什么?

    单例模式属于创建型模式,一个单例类在任何情况下都只存在一个实例,构造方法必须是私有的、由自己创建一个静态变量存储实例,对外提供一个静态公有方法获取实例。

    优点是内存中只有一个实例,减少了开销,尤其是频繁创建和销毁实例的情况下并且可以避免对资源的多重占用。缺点是没有抽象层,难以扩展,与单一职责原则冲突。

    Spring 的 ApplicationContext 创建的 Bean 实例都是单例对象,还有 ServletContext、数据库连接池等也都是单例模式。

    Q7:单例模式有哪些实现?

    饿汉式:在类加载时就初始化创建单例对象,线程安全,但不管是否使用都创建对象可能会浪费内存。

    public class HungrySingleton {
        private HungrySingleton(){}
    
        private static HungrySingleton instance = new HungrySingleton();
    
        public static HungrySingleton getInstance() {
            return instance;
        }
    }

    懒汉式:在外部调用时才会加载,线程不安全,可以加锁保证线程安全但效率低。

    public class LazySingleton {
        private LazySingleton(){}
    
        private static LazySingleton instance;
    
        public static LazySingleton getInstance() {
            if(instance == null) {
                instance = new LazySingleton();
            }
            return instance;
        }
    }

    双重检查锁:使用 volatile 以及多重检查来减小锁范围,提升效率。

    public class DoubleCheckSingleton {
        private DoubleCheckSingleton(){}
    
        private volatile static DoubleCheckSingleton instance;
    
        public static DoubleCheckSingleton getInstance() {
            if(instance == null) {
                synchronized (DoubleCheckSingleton.class) {
                    if (instance == null) {
                        instance = new DoubleCheckSingleton();
                    }
                }
            }
            return instance;
        }
    }

    静态内部类:同时解决饿汉式的内存浪费问题和懒汉式的线程安全问题。

    public class StaticSingleton {
        private StaticSingleton(){}
    
        public static StaticSingleton getInstance() {
            return StaticClass.instance;
        }
    
        private static class StaticClass {
            private static final StaticSingleton instance = new StaticSingleton();
        }
    }

    枚举:《Effective Java》提倡的方式,不仅能避免线程安全问题,还能防止反序列化重新创建新的对象,绝对防止多次实例化,也能防止反射破解单例的问题。

    public enum EnumSingleton {
        INSTANCE;
    }

    Q8:讲一讲代理模式

    代理模式属于结构型模式,为其他对象提供一种代理以控制对这个对象的访问。优点是可以增强目标对象的功能,降低代码耦合度,扩展性好。缺点是在客户端和目标对象之间增加代理对象会导致请求处理速度变慢,增加系统复杂度。

    Spring 利用动态代理实现 AOP,如果 Bean 实现了接口就使用 JDK 代理,否则使用 CGLib 代理。

    静态代理:代理对象持有被代理对象的引用,调用代理对象方法时也会调用被代理对象的方法,但是会在被代理对象方法的前后增加其他逻辑。需要手动完成,在程序运行前就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就已经确定了。 缺点是一个代理类只能为一个目标服务,如果要服务多种类型会增加工作量。

    动态代理:动态代理在程序运行时通过反射创建具体的代理类,代理类和被代理类的关系在运行前是不确定的。动态代理的适用性更强,主要分为 JDK 动态代理和 CGLib 动态代理。

    • JDK 动态代理:通过 Proxy 类的 newInstance 方法获取一个动态代理对象,需要传入三个参数,被代理对象的类加载器、被代理对象实现的接口,以及一个 InvocationHandler 调用处理器来指明具体的逻辑,相比静态代理的优势是接口中声明的所有方法都被转移到 InvocationHandler 的 invoke 方法集中处理。
    • CGLib 动态代理:JDK 动态代理要求实现被代理对象的接口,而 CGLib 要求继承被代理对象,如果一个类是 final 类则不能使用 CGLib 代理。两种代理都在运行期生成字节码,JDK 动态代理直接写字节码,而 CGLib 动态代理使用 ASM 框架写字节码,ASM 的目的是生成、转换和分析以字节数组表示的已编译 Java 类。 JDK 动态代理调用代理方法通过反射机制实现,而 GCLib 动态代理通过 FastClass 机制直接调用方法,它为代理类和被代理类各生成一个类,该类为代理类和被代理类的方法分配一个 int 参数,调用方法时可以直接定位,因此调用效率更高。

    Q9:讲一讲装饰器模式

    装饰器模式属于结构型模式,在不改变原有对象的基础上将功能附加到对象,相比继承可以更加灵活地扩展原有对象的功能。

    装饰器模式适合的场景:在不想增加很多子类的前提下扩展一个类的功能。

    java.io 包中,InputStream 字节输入流通过装饰器 BufferedInputStream 增强为缓冲字节输入流。


    Q10:装饰器模式和动态代理的区别?

    装饰器模式的关注点在于给对象动态添加方法,而动态代理更注重对象的访问控制。动态代理通常会在代理类中创建被代理对象的实例,而装饰器模式会将装饰者作为构造方法的参数。


    Q11:讲一讲适配器模式

    适配器模式属于结构型模式,它作为两个不兼容接口之间的桥梁,结合了两个独立接口的功能,将一个类的接口转换成另外一个接口使得原本由于接口不兼容而不能一起工作的类可以一起工作。

    缺点是过多使用适配器会让系统非常混乱,不易整体把握。

    java.io 包中,InputStream 字节输入流通过适配器 InputStreamReader 转换为 Reader 字符输入流。

    Spring MVC 中的 HandlerAdapter,由于 handler 有很多种形式,包括 Controller、HttpRequestHandler、Servlet 等,但调用方式又是确定的,因此需要适配器来进行处理,根据适配规则调用 handle 方法。

    Arrays.asList 方法,将数组转换为对应的集合(注意不能使用修改集合的方法,因为返回的 ArrayList 是 Arrays 的一个内部类)。


    Q12:适配器模式和和装饰器模式以及代理模式的区别?

    适配器模式没有层级关系,适配器和被适配者没有必然连续,满足 has-a 的关系,解决不兼容的问题,是一种后置考虑。

    装饰器模式具有层级关系,装饰器与被装饰者实现同一个接口,满足 is-a 的关系,注重覆盖和扩展,是一种前置考虑。

    适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。


    Q13:讲一讲策略模式

    策略模式属于行为型模式,定义了一系列算法并封装起来,之间可以互相替换。策略模式主要解决在有多种算法相似的情况下,使用 if/else 所带来的难以维护。

    优点是算法可以自由切换,可以避免使用多重条件判断并且扩展性良好,缺点是策略类会增多并且所有策略类都需要对外暴露。

    在集合框架中,经常需要通过构造方法传入一个比较器 Comparator 进行比较排序。Comparator 就是一个抽象策略,一个类通过实现该接口并重写 compare 方法成为具体策略类。

    创建线程池时,需要传入拒绝策略,当创建新线程使当前运行的线程数超过 maximumPoolSize 时会使用相应的拒绝策略处理。


    Q14:讲一讲模板模式

    模板模式属于行为型模式,使子类可以在不改变算法结构的情况下重新定义算法的某些步骤,适用于抽取子类重复代码到公共父类。

    优点是可以封装固定不变的部分,扩展可变的部分。缺点是每一个不同实现都需要一个子类维护,会增加类的数量。

    为防止恶意操作,一般模板方法都以 final 修饰。

    HttpServlet 定义了一套处理 HTTP 请求的模板,service 方法为模板方法,定义了处理HTTP请求的基本流程,doXXX 等方法为基本方法,根据请求方法的类型做相应的处理,子类可重写这些方法。


    Q15:讲一讲观察者模式

    观察者模式属于行为型模式,也叫发布订阅模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。主要解决一个对象状态改变给其他对象通知的问题,缺点是如果被观察者对象有很多的直接和间接观察者的话通知很耗时, 如果存在循环依赖的话可能导致系统崩溃,另外观察者无法知道目标对象具体是怎么发生变化的。

    ServletContextListener 能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用。当 Servlet 容器启动 Web 应用时调用 contextInitialized 方法,终止时调用 contextDestroyed 方法。