分类 技术 下的文章

在这个指南中,我将用示例来阐明如何使用 FFmpeg 媒体框架来做各种各样的音频、视频转码和转换的操作。我已经为初学者汇集了最常用的 20 多个 FFmpeg 命令,我将不时地添加更多的示例来保持更新这个指南。请给这个指南加书签,以后回来检查更新。让我们开始吧,如果你还没有在你的 Linux 系统中安装 FFmpeg,参考下面的指南。

针对初学者的 20 多个 FFmpeg 命令

FFmpeg 命令的典型语法是:

ffmpeg [全局选项] {[输入文件选项] -i 输入_url_地址} ...
 {[输出文件选项] 输出_url_地址} ...

现在我们将查看一些重要的和有用的 FFmpeg 命令。

1、获取音频/视频文件信息

为显示你的媒体文件细节,运行:

$ ffmpeg -i video.mp4

样本输出:

ffmpeg version n4.1.3 Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 8.2.1 (GCC) 20181127
configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
libavutil 56. 22.100 / 56. 22.100
libavcodec 58. 35.100 / 58. 35.100
libavformat 58. 20.100 / 58. 20.100
libavdevice 58. 5.100 / 58. 5.100
libavfilter 7. 40.101 / 7. 40.101
libswscale 5. 3.100 / 5. 3.100
libswresample 3. 3.100 / 3. 3.100
libpostproc 55. 3.100 / 55. 3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'video.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.20.100
Duration: 00:00:28.79, start: 0.000000, bitrate: 454 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, smpte170m/bt470bg/smpte170m), 1920x1080 [SAR 1:1 DAR 16:9], 318 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 04/08/2019.
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)
Metadata:
handler_name : ISO Media file produced by Google Inc. Created on: 04/08/2019.
At least one output file must be specified

如你在上面的输出中看到的,FFmpeg 显示该媒体文件信息,以及 FFmpeg 细节,例如版本、配置细节、版权标记、构建参数和库选项等等。

如果你不想看 FFmpeg 标语和其它细节,而仅仅想看媒体文件信息,使用 -hide_banner 标志,像下面。

$ ffmpeg -i video.mp4 -hide_banner

样本输出:

使用 FFMpeg 查看音频、视频文件信息。

看见了吗?现在,它仅显示媒体文件细节。

2、转换视频文件到不同的格式

FFmpeg 是强有力的音频和视频转换器,因此,它能在不同格式之间转换媒体文件。举个例子,要转换 mp4 文件到 avi 文件,运行:

$ ffmpeg -i video.mp4 video.avi

类似地,你可以转换媒体文件到你选择的任何格式。

例如,为转换 YouTube flv 格式视频为 mpeg 格式,运行:

$ ffmpeg -i video.flv video.mpeg

如果你想维持你的源视频文件的质量,使用 -qscale 0 参数:

$ ffmpeg -i input.webm -qscale 0 output.mp4

为检查 FFmpeg 的支持格式的列表,运行:

$ ffmpeg -formats

3、转换视频文件到音频文件

我转换一个视频文件到音频文件,只需具体指明输出格式,像 .mp3,或 .ogg,或其它任意音频格式。

上面的命令将转换 input.mp4 视频文件到 output.mp3 音频文件。

$ ffmpeg -i input.mp4 -vn output.mp3

此外,你也可以对输出文件使用各种各样的音频转换编码选项,像下面演示。

$ ffmpeg -i input.mp4 -vn -ar 44100 -ac 2 -ab 320 -f mp3 output.mp3

在这里,

  • -vn – 表明我们已经在输出文件中禁用视频录制。
  • -ar – 设置输出文件的音频频率。通常使用的值是22050 Hz、44100 Hz、48000 Hz。
  • -ac – 设置音频通道的数目。
  • -ab – 表明音频比特率。
  • -f – 输出文件格式。在我们的实例中,它是 mp3 格式。

4、更改视频文件的分辨率

如果你想设置一个视频文件为指定的分辨率,你可以使用下面的命令:

$ ffmpeg -i input.mp4 -filter:v scale=1280:720 -c:a copy output.mp4

或,

$ ffmpeg -i input.mp4 -s 1280x720 -c:a copy output.mp4

上面的命令将设置所给定视频文件的分辨率到 1280×720。

类似地,为转换上面的文件到 640×480 大小,运行:

$ ffmpeg -i input.mp4 -filter:v scale=640:480 -c:a copy output.mp4

或者,

$ ffmpeg -i input.mp4 -s 640x480 -c:a copy output.mp4

这个技巧将帮助你缩放你的视频文件到较小的显示设备上,例如平板电脑和手机。

5、压缩视频文件

减小媒体文件的大小到较小来节省硬件的空间总是一个好主意.

下面的命令将压缩并减少输出文件的大小。

$ ffmpeg -i input.mp4 -vf scale=1280:-1 -c:v libx264 -preset veryslow -crf 24 output.mp4

请注意,如果你尝试减小视频文件的大小,你将损失视频质量。如果 24 太有侵略性,你可以降低 -crf 值到或更低值。

你也可以通过下面的选项来转换编码音频降低比特率,使其有立体声感,从而减小大小。

-ac 2 -c:a aac -strict -2 -b:a 128k

6、压缩音频文件

正像压缩视频文件一样,为节省一些磁盘空间,你也可以使用 -ab 标志压缩音频文件。

例如,你有一个 320 kbps 比特率的音频文件。你想通过更改比特率到任意较低的值来压缩它,像下面。

$ ffmpeg -i input.mp3 -ab 128 output.mp3

各种各样可用的音频比特率列表是:

  1. 96kbps
  2. 112kbps
  3. 128kbps
  4. 160kbps
  5. 192kbps
  6. 256kbps
  7. 320kbps

7、从一个视频文件移除音频流

如果你不想要一个视频文件中的音频,使用 -an 标志。

$ ffmpeg -i input.mp4 -an output.mp4

在这里,-an 表示没有音频录制。

上面的命令会撤销所有音频相关的标志,因为我们不要来自 input.mp4 的音频。

8、从一个媒体文件移除视频流

类似地,如果你不想要视频流,你可以使用 -vn 标志从媒体文件中简单地移除它。-vn 代表没有视频录制。换句话说,这个命令转换所给定媒体文件为音频文件。

下面的命令将从所给定媒体文件中移除视频。

$ ffmpeg -i input.mp4 -vn output.mp3

你也可以使用 -ab 标志来指出输出文件的比特率,如下面的示例所示。

$ ffmpeg -i input.mp4 -vn -ab 320 output.mp3

9、从视频中提取图像

FFmpeg 的另一个有用的特色是我们可以从一个视频文件中轻松地提取图像。如果你想从一个视频文件中创建一个相册,这可能是非常有用的。

为从一个视频文件中提取图像,使用下面的命令:

$ ffmpeg -i input.mp4 -r 1 -f image2 image-%2d.png

在这里,

  • -r – 设置帧速度。即,每秒提取帧到图像的数字。默认值是 25。
  • -f – 表示输出格式,即,在我们的实例中是图像。
  • image-%2d.png – 表明我们如何想命名提取的图像。在这个实例中,命名应该像这样image-01.png、image-02.png、image-03.png 等等开始。如果你使用 %3d,那么图像的命名像 image-001.png、image-002.png 等等开始。

10、裁剪视频

FFMpeg 允许以我们选择的任何范围裁剪一个给定的媒体文件。

裁剪一个视频文件的语法如下给定:

ffmpeg -i input.mp4 -filter:v "crop=w:h:x:y" output.mp4

在这里,

  • input.mp4 – 源视频文件。
  • -filter:v – 表示视频过滤器。
  • crop – 表示裁剪过滤器。
  • w – 我们想自源视频中裁剪的矩形的宽度。
  • h – 矩形的高度。
  • x – 我们想自源视频中裁剪的矩形的 x 坐标 。
  • y – 矩形的 y 坐标。

比如说你想要一个来自视频的位置 (200,150),且具有 640 像素宽度和 480 像素高度的视频,命令应该是:

$ ffmpeg -i input.mp4 -filter:v "crop=640:480:200:150" output.mp4

请注意,剪切视频将影响质量。除非必要,请勿剪切。

11、转换一个视频的具体的部分

有时,你可能想仅转换视频文件的一个具体的部分到不同的格式。以示例说明,下面的命令将转换所给定视频input.mp4 文件的开始 10 秒到视频 .avi 格式。

$ ffmpeg -i input.mp4 -t 10 output.avi

在这里,我们以秒具体说明时间。此外,以 hh.mm.ss 格式具体说明时间也是可以的。

12、设置视频的屏幕高宽比

你可以使用 -aspect 标志设置一个视频文件的屏幕高宽比,像下面。

$ ffmpeg -i input.mp4 -aspect 16:9 output.mp4

通常使用的高宽比是:

  • 16:9
  • 4:3
  • 16:10
  • 5:4
  • 2:21:1
  • 2:35:1
  • 2:39:1

13、添加海报图像到音频文件

你可以添加海报图像到你的文件,以便图像将在播放音频文件时显示。这对托管在视频托管主机或共享网站中的音频文件是有用的。

$ ffmpeg -loop 1 -i inputimage.jpg -i inputaudio.mp3 -c:v libx264 -c:a aac -strict experimental -b:a 192k -shortest output.mp4

14、使用开始和停止时间剪下一段媒体文件

可以使用开始和停止时间来剪下一段视频为小段剪辑,我们可以使用下面的命令。

$ ffmpeg -i input.mp4 -ss 00:00:50 -codec copy -t 50 output.mp4

在这里,

  • –ss – 表示视频剪辑的开始时间。在我们的示例中,开始时间是第 50 秒。
  • -t – 表示总的持续时间。

当你想使用开始和结束时间从一个音频或视频文件剪切一部分时,它是非常有用的。

类似地,我们可以像下面剪下音频。

$ ffmpeg -i audio.mp3 -ss 00:01:54 -to 00:06:53 -c copy output.mp3

15、切分视频文件为多个部分

一些网站将仅允许你上传具体指定大小的视频。在这样的情况下,你可以切分大的视频文件到多个较小的部分,像下面。

$ ffmpeg -i input.mp4 -t 00:00:30 -c copy part1.mp4 -ss 00:00:30 -codec copy part2.mp4

在这里,

  • -t 00:00:30 表示从视频的开始到视频的第 30 秒创建一部分视频。
  • -ss 00:00:30 为视频的下一部分显示开始时间戳。它意味着第 2 部分将从第 30 秒开始,并将持续到原始视频文件的结尾。

16、接合或合并多个视频部分到一个

FFmpeg 也可以接合多个视频部分,并创建一个单个视频文件。

创建包含你想接合文件的准确的路径的 join.txt。所有的文件都应该是相同的格式(相同的编码格式)。所有文件的路径应该逐个列出,像下面。

file /home/sk/myvideos/part1.mp4
file /home/sk/myvideos/part2.mp4
file /home/sk/myvideos/part3.mp4
file /home/sk/myvideos/part4.mp4

现在,接合所有文件,使用命令:

$ ffmpeg -f concat -i join.txt -c copy output.mp4

如果你得到一些像下面的错误;

[concat @ 0x555fed174cc0] Unsafe file name '/path/to/mp4'
join.txt: Operation not permitted

添加 -safe 0 :

$ ffmpeg -f concat -safe 0 -i join.txt -c copy output.mp4

上面的命令将接合 part1.mp4、part2.mp4、part3.mp4 和 part4.mp4 文件到一个称为 output.mp4 的单个文件中。

17、添加字幕到一个视频文件

我们可以使用 FFmpeg 来添加字幕到视频文件。为你的视频下载正确的字幕,并如下所示添加它到你的视频。

$ fmpeg -i input.mp4 -i subtitle.srt -map 0 -map 1 -c copy -c:v libx264 -crf 23 -preset veryfast output.mp4

18、预览或测试视频或音频文件

你可能希望通过预览来验证或测试输出的文件是否已经被恰当地转码编码。为完成预览,你可以从你的终端播放它,用命令:

$ ffplay video.mp4

类似地,你可以测试音频文件,像下面所示。

$ ffplay audio.mp3

19、增加/减少视频播放速度

FFmpeg 允许你调整视频播放速度。

为增加视频播放速度,运行:

$ ffmpeg -i input.mp4 -vf "setpts=0.5*PTS" output.mp4

该命令将双倍视频的速度。

为降低你的视频速度,你需要使用一个大于 1 的倍数。为减少播放速度,运行:

$ ffmpeg -i input.mp4 -vf "setpts=4.0*PTS" output.mp4

20、创建动画的 GIF

出于各种目的,我们在几乎所有的社交和专业网络上使用 GIF 图像。使用 FFmpeg,我们可以简单地和快速地创建动画的视频文件。下面的指南阐释了如何在类 Unix 系统中使用 FFmpeg 和 ImageMagick 创建一个动画的 GIF 文件。

21、从 PDF 文件中创建视频

我长年累月的收集了很多 PDF 文件,大多数是 Linux 教程,保存在我的平板电脑中。有时我懒得从平板电脑中阅读它们。因此,我决定从 PDF 文件中创建一个视频,在一个大屏幕设备(像一台电视机或一台电脑)中观看它们。如果你想知道如何从一批 PDF 文件中制作一个电影,下面的指南将帮助你。

22、获取帮助

在这个指南中,我已经覆盖大多数常常使用的 FFmpeg 命令。它有很多不同的选项来做各种各样的高级功能。要学习更多用法,请参考手册页。

$ man ffmpeg

这就是全部了。我希望这个指南将帮助你入门 FFmpeg。如果你发现这个指南有用,请在你的社交和专业网络上分享它。更多好东西将要来。敬请期待!

谢谢!


via: https://www.ostechnix.com/20-ffmpeg-commands-beginners/

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

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

学习在 Linux 中进程是如何与其他进程进行同步的。

本篇是 Linux 下进程间通信(IPC)系列的第三篇同时也是最后一篇文章。第一篇文章聚焦在通过共享存储(文件和共享内存段)来进行 IPC,第二篇文章则通过管道(无名的或者命名的)及消息队列来达到相同的目的。这篇文章将目光从高处(套接字)然后到低处(信号)来关注 IPC。代码示例将用力地充实下面的解释细节。

套接字

正如管道有两种类型(命名和无名)一样,套接字也有两种类型。IPC 套接字(即 Unix 套接字)给予进程在相同设备(主机)上基于通道的通信能力;而网络套接字给予进程运行在不同主机的能力,因此也带来了网络通信的能力。网络套接字需要底层协议的支持,例如 TCP(传输控制协议)或 UDP(用户数据报协议)。

与之相反,IPC 套接字依赖于本地系统内核的支持来进行通信;特别的,IPC 通信使用一个本地的文件作为套接字地址。尽管这两种套接字的实现有所不同,但在本质上,IPC 套接字和网络套接字的 API 是一致的。接下来的例子将包含网络套接字的内容,但示例服务器和客户端程序可以在相同的机器上运行,因为服务器使用了 localhost(127.0.0.1)这个网络地址,该地址表示的是本地机器上的本地机器地址。

套接字以流的形式(下面将会讨论到)被配置为双向的,并且其控制遵循 C/S(客户端/服务器端)模式:客户端通过尝试连接一个服务器来初始化对话,而服务器端将尝试接受该连接。假如万事顺利,来自客户端的请求和来自服务器端的响应将通过管道进行传输,直到其中任意一方关闭该通道,从而断开这个连接。

一个迭代服务器(只适用于开发)将一直和连接它的客户端打交道:从最开始服务第一个客户端,然后到这个连接关闭,然后服务第二个客户端,循环往复。这种方式的一个缺点是处理一个特定的客户端可能会挂起,使得其他的客户端一直在后面等待。生产级别的服务器将是并发的,通常使用了多进程或者多线程的混合。例如,我台式机上的 Nginx 网络服务器有一个 4 个 工人 worker 的进程池,它们可以并发地处理客户端的请求。在下面的代码示例中,我们将使用迭代服务器,使得我们将要处理的问题保持在一个很小的规模,只关注基本的 API,而不去关心并发的问题。

最后,随着各种 POSIX 改进的出现,套接字 API 随着时间的推移而发生了显著的变化。当前针对服务器端和客户端的示例代码特意写的比较简单,但是它着重强调了基于流的套接字中连接的双方。下面是关于流控制的一个总结,其中服务器端在一个终端中开启,而客户端在另一个不同的终端中开启:

  • 服务器端等待客户端的连接,对于给定的一个成功连接,它就读取来自客户端的数据。
  • 为了强调是双方的会话,服务器端会对接收自客户端的数据做回应。这些数据都是 ASCII 字符代码,它们组成了一些书的标题。
  • 客户端将书的标题写给服务器端的进程,并从服务器端的回应中读取到相同的标题。然后客户端和服务器端都在屏幕上打印出标题。下面是服务器端的输出,客户端的输出也和它完全一样:
Listening on port 9876 for clients...
War and Peace
Pride and Prejudice
The Sound and the Fury

示例 1. 使用套接字的客户端程序

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include "sock.h"

void report(const char* msg, int terminate) {
  perror(msg);
  if (terminate) exit(-1); /* failure */
}

int main() {
  int fd = socket(AF_INET,     /* network versus AF_LOCAL */
          SOCK_STREAM, /* reliable, bidirectional: TCP */
          0);          /* system picks underlying protocol */
  if (fd < 0) report("socket", 1); /* terminate */
    
  /* bind the server's local address in memory */
  struct sockaddr_in saddr;
  memset(&saddr, 0, sizeof(saddr));          /* clear the bytes */
  saddr.sin_family = AF_INET;                /* versus AF_LOCAL */
  saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* host-to-network endian */
  saddr.sin_port = htons(PortNumber);        /* for listening */
  
  if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
    report("bind", 1); /* terminate */
    
  /* listen to the socket */
  if (listen(fd, MaxConnects) < 0) /* listen for clients, up to MaxConnects */
    report("listen", 1); /* terminate */

  fprintf(stderr, "Listening on port %i for clients...\n", PortNumber);
  /* a server traditionally listens indefinitely */
  while (1) {
    struct sockaddr_in caddr; /* client address */
    int len = sizeof(caddr);  /* address length could change */
    
    int client_fd = accept(fd, (struct sockaddr*) &caddr, &len);  /* accept blocks */
    if (client_fd < 0) {
      report("accept", 0); /* don't terminated, though there's a problem */
      continue;
    }

    /* read from client */
    int i;
    for (i = 0; i < ConversationLen; i++) {
      char buffer[BuffSize + 1];
      memset(buffer, '\0', sizeof(buffer)); 
      int count = read(client_fd, buffer, sizeof(buffer));
      if (count > 0) {
    puts(buffer);
    write(client_fd, buffer, sizeof(buffer)); /* echo as confirmation */
      }
    }
    close(client_fd); /* break connection */
  }  /* while(1) */
  return 0;
}

上面的服务器端程序执行典型的 4 个步骤来准备回应客户端的请求,然后接受其他的独立请求。这里每一个步骤都以服务器端程序调用的系统函数来命名。

  1. socket(…):为套接字连接获取一个文件描述符
  2. bind(…):将套接字和服务器主机上的一个地址进行绑定
  3. listen(…):监听客户端请求
  4. accept(…):接受一个特定的客户端请求

上面的 socket 调用的完整形式为:

int sockfd = socket(AF_INET,      /* versus AF_LOCAL */
                    SOCK_STREAM,  /* reliable, bidirectional */
                    0);           /* system picks protocol (TCP) */

第一个参数特别指定了使用的是一个网络套接字,而不是 IPC 套接字。对于第二个参数有多种选项,但 SOCK_STREAMSOCK_DGRAM(数据报)是最为常用的。基于流的套接字支持可信通道,在这种通道中如果发生了信息的丢失或者更改,都将会被报告。这种通道是双向的,并且从一端到另外一端的有效载荷在大小上可以是任意的。相反的,基于数据报的套接字大多是不可信的,没有方向性,并且需要固定大小的载荷。socket 的第三个参数特别指定了协议。对于这里展示的基于流的套接字,只有一种协议选择:TCP,在这里表示的 0。因为对 socket 的一次成功调用将返回相似的文件描述符,套接字可以被读写,对应的语法和读写一个本地文件是类似的。

bind 的调用是最为复杂的,因为它反映出了在套接字 API 方面上的各种改进。我们感兴趣的点是这个调用将一个套接字和服务器端所在机器中的一个内存地址进行绑定。但对 listen 的调用就非常直接了:

if (listen(fd, MaxConnects) < 0)

第一个参数是套接字的文件描述符,第二个参数则指定了在服务器端处理一个拒绝连接错误之前,有多少个客户端连接被允许连接。(在头文件 sock.hMaxConnects 的值被设置为 8。)

accept 调用默认将是一个阻塞等待:服务器端将不做任何事情直到一个客户端尝试连接它,然后进行处理。accept 函数返回的值如果是 -1 则暗示有错误发生。假如这个调用是成功的,则它将返回另一个文件描述符,这个文件描述符被用来指代另一个可读可写的套接字,它与 accept 调用中的第一个参数对应的接收套接字有所不同。服务器端使用这个可读可写的套接字来从客户端读取请求然后写回它的回应。接收套接字只被用于接受客户端的连接。

在设计上,服务器端可以一直运行下去。当然服务器端可以通过在命令行中使用 Ctrl+C 来终止它。

示例 2. 使用套接字的客户端

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include "sock.h"

const char* books[] = {"War and Peace",
               "Pride and Prejudice",
               "The Sound and the Fury"};

void report(const char* msg, int terminate) {
  perror(msg);
  if (terminate) exit(-1); /* failure */
}

int main() {
  /* fd for the socket */
  int sockfd = socket(AF_INET,      /* versus AF_LOCAL */
              SOCK_STREAM,  /* reliable, bidirectional */
              0);           /* system picks protocol (TCP) */
  if (sockfd < 0) report("socket", 1); /* terminate */

  /* get the address of the host */
  struct hostent* hptr = gethostbyname(Host); /* localhost: 127.0.0.1 */ 
  if (!hptr) report("gethostbyname", 1); /* is hptr NULL? */
  if (hptr->h_addrtype != AF_INET)       /* versus AF_LOCAL */
    report("bad address family", 1);
  
  /* connect to the server: configure server's address 1st */
  struct sockaddr_in saddr;
  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = 
     ((struct in_addr*) hptr->h_addr_list[0])->s_addr;
  saddr.sin_port = htons(PortNumber); /* port number in big-endian */
  
  if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)
    report("connect", 1);
  
  /* Write some stuff and read the echoes. */
  puts("Connect to server, about to write some stuff...");
  int i;
  for (i = 0; i < ConversationLen; i++) {
    if (write(sockfd, books[i], strlen(books[i])) > 0) {
      /* get confirmation echoed from server and print */
      char buffer[BuffSize + 1];
      memset(buffer, '\0', sizeof(buffer));
      if (read(sockfd, buffer, sizeof(buffer)) > 0)
    puts(buffer);
    }
  }
  puts("Client done, about to exit...");
  close(sockfd); /* close the connection */
  return 0;
}

客户端程序的设置代码和服务器端类似。两者主要的区别既不是在于监听也不在于接收,而是连接:

if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)

connect 的调用可能因为多种原因而导致失败,例如客户端拥有错误的服务器端地址或者已经有太多的客户端连接上了服务器端。假如 connect 操作成功,客户端将在一个 for 循环中,写入它的请求然后读取返回的响应。在会话后,服务器端和客户端都将调用 close 去关闭这个可读可写套接字,尽管任何一边的关闭操作就足以关闭它们之间的连接。此后客户端可以退出了,但正如前面提到的那样,服务器端可以一直保持开放以处理其他事务。

从上面的套接字示例中,我们看到了请求信息被回显给客户端,这使得客户端和服务器端之间拥有进行丰富对话的可能性。也许这就是套接字的主要魅力。在现代系统中,客户端应用(例如一个数据库客户端)和服务器端通过套接字进行通信非常常见。正如先前提及的那样,本地 IPC 套接字和网络套接字只在某些实现细节上面有所不同,一般来说,IPC 套接字有着更低的消耗和更好的性能。它们的通信 API 基本是一样的。

信号

信号会中断一个正在执行的程序,在这种意义下,就是用信号与这个程序进行通信。大多数的信号要么可以被忽略(阻塞)或者被处理(通过特别设计的代码)。SIGSTOP (暂停)和 SIGKILL(立即停止)是最应该提及的两种信号。这种符号常量有整数类型的值,例如 SIGKILL 对应的值为 9

信号可以在与用户交互的情况下发生。例如,一个用户从命令行中敲了 Ctrl+C 来终止一个从命令行中启动的程序;Ctrl+C 将产生一个 SIGTERM 信号。SIGTERM 意即终止,它可以被阻塞或者被处理,而不像 SIGKILL 信号那样。一个进程也可以通过信号和另一个进程通信,这样使得信号也可以作为一种 IPC 机制。

考虑一下一个多进程应用,例如 Nginx 网络服务器是如何被另一个进程优雅地关闭的。kill 函数:

int kill(pid_t pid, int signum); /* declaration */

可以被一个进程用来终止另一个进程或者一组进程。假如 kill 函数的第一个参数是大于 0 的,那么这个参数将会被认为是目标进程的 pid(进程 ID),假如这个参数是 0,则这个参数将会被视作信号发送者所属的那组进程。

kill 的第二个参数要么是一个标准的信号数字(例如 SIGTERMSIGKILL),要么是 0 ,这将会对信号做一次询问,确认第一个参数中的 pid 是否是有效的。这样优雅地关闭一个多进程应用就可以通过向组成该应用的一组进程发送一个终止信号来完成,具体来说就是调用一个 kill 函数,使得这个调用的第二个参数是 SIGTERM 。(Nginx 主进程可以通过调用 kill 函数来终止其他工人进程,然后再停止自己。)就像许多库函数一样,kill 函数通过一个简单的可变语法拥有更多的能力和灵活性。

示例 3. 一个多进程系统的优雅停止

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

void graceful(int signum) {
  printf("\tChild confirming received signal: %i\n", signum);
  puts("\tChild about to terminate gracefully...");
  sleep(1);
  puts("\tChild terminating now...");
  _exit(0); /* fast-track notification of parent */
}

void set_handler() {
  struct sigaction current;
  sigemptyset(&current.sa_mask);         /* clear the signal set */
  current.sa_flags = 0;                  /* enables setting sa_handler, not sa_action */
  current.sa_handler = graceful;         /* specify a handler */
  sigaction(SIGTERM, &current, NULL);    /* register the handler */
}

void child_code() {
  set_handler();

  while (1) {   /` loop until interrupted `/
    sleep(1);
    puts("\tChild just woke up, but going back to sleep.");
  }
}

void parent_code(pid_t cpid) {
  puts("Parent sleeping for a time...");
  sleep(5);

  /* Try to terminate child. */
  if (-1 == kill(cpid, SIGTERM)) {
    perror("kill");
    exit(-1);
  }
  wait(NULL); /` wait for child to terminate `/
  puts("My child terminated, about to exit myself...");
}

int main() {
  pid_t pid = fork();
  if (pid < 0) {
    perror("fork");
    return -1; /* error */
  }
  if (0 == pid)
    child_code();
  else
    parent_code(pid);
  return 0;  /* normal */
}

上面的停止程序模拟了一个多进程系统的优雅退出,在这个例子中,这个系统由一个父进程和一个子进程组成。这次模拟的工作流程如下:

  • 父进程尝试去 fork 一个子进程。假如这个 fork 操作成功了,每个进程就执行它自己的代码:子进程就执行函数 child_code,而父进程就执行函数 parent_code
  • 子进程将会进入一个潜在的无限循环,在这个循环中子进程将睡眠一秒,然后打印一个信息,接着再次进入睡眠状态,以此循环往复。来自父进程的一个 SIGTERM 信号将引起子进程去执行一个信号处理回调函数 graceful。这样这个信号就使得子进程可以跳出循环,然后进行子进程和父进程之间的优雅终止。在终止之前,进程将打印一个信息。
  • fork 一个子进程后,父进程将睡眠 5 秒,使得子进程可以执行一会儿;当然在这个模拟中,子进程大多数时间都在睡眠。然后父进程调用 SIGTERM 作为第二个参数的 kill 函数,等待子进程的终止,然后自己再终止。

下面是一次运行的输出:

% ./shutdown
Parent sleeping for a time...
        Child just woke up, but going back to sleep.
        Child just woke up, but going back to sleep.
        Child just woke up, but going back to sleep.
        Child just woke up, but going back to sleep.
        Child confirming received signal: 15  ## SIGTERM is 15
        Child about to terminate gracefully...
        Child terminating now...
My child terminated, about to exit myself...

对于信号的处理,上面的示例使用了 sigaction 库函数(POSIX 推荐的用法)而不是传统的 signal 函数,signal 函数有移植性问题。下面是我们主要关心的代码片段:

  • 假如对 fork 的调用成功了,父进程将执行 parent_code 函数,而子进程将执行 child_code 函数。在给子进程发送信号之前,父进程将会等待 5 秒:
puts("Parent sleeping for a time...");
sleep(5);
if (-1 == kill(cpid, SIGTERM)) {
...sleepkillcpidSIGTERM...

假如 kill 调用成功了,父进程将在子进程终止时做等待,使得子进程不会变成一个僵尸进程。在等待完成后,父进程再退出。

  • child_code 函数首先调用 set_handler 然后进入它的可能永久睡眠的循环。下面是我们将要查看的 set_handler 函数:
void set_handler() {
  struct sigaction current;            /* current setup */
  sigemptyset(&current.sa_mask);       /* clear the signal set */
  current.sa_flags = 0;                /* for setting sa_handler, not sa_action */
  current.sa_handler = graceful;       /* specify a handler */
  sigaction(SIGTERM, &current, NULL);  /* register the handler */
}

上面代码的前三行在做相关的准备。第四个语句将为 graceful 设定为句柄,它将在调用 _exit 来停止之前打印一些信息。第 5 行和最后一行的语句将通过调用 sigaction 来向系统注册上面的句柄。sigaction 的第一个参数是 SIGTERM ,用作终止;第二个参数是当前的 sigaction 设定,而最后的参数(在这个例子中是 NULL )可被用来保存前面的 sigaction 设定,以备后面的可能使用。

使用信号来作为 IPC 的确是一个很轻量的方法,但确实值得尝试。通过信号来做 IPC 显然可以被归入 IPC 工具箱中。

这个系列的总结

在这个系列中,我们通过三篇有关 IPC 的文章,用示例代码介绍了如下机制:

  • 共享文件
  • 共享内存(通过信号量)
  • 管道(命名和无名)
  • 消息队列
  • 套接字
  • 信号

甚至在今天,在以线程为中心的语言,例如 Java、C# 和 Go 等变得越来越流行的情况下,IPC 仍然很受欢迎,因为相比于使用多线程,通过多进程来实现并发有着一个明显的优势:默认情况下,每个进程都有它自己的地址空间,除非使用了基于共享内存的 IPC 机制(为了达到安全的并发,竞争条件在多线程和多进程的时候必须被加上锁),在多进程中可以排除掉基于内存的竞争条件。对于任何一个写过即使是基本的通过共享变量来通信的多线程程序的人来说,他都会知道想要写一个清晰、高效、线程安全的代码是多么具有挑战性。使用单线程的多进程的确是很有吸引力的,这是一个切实可行的方式,使用它可以利用好今天多处理器的机器,而不需要面临基于内存的竞争条件的风险。

当然,没有一个简单的答案能够回答上述 IPC 机制中的哪一个更好。在编程中每一种 IPC 机制都会涉及到一个取舍问题:是追求简洁,还是追求功能强大。以信号来举例,它是一个相对简单的 IPC 机制,但并不支持多个进程之间的丰富对话。假如确实需要这样的对话,另外的选择可能会更合适一些。带有锁的共享文件则相对直接,但是当要处理大量共享的数据流时,共享文件并不能很高效地工作。管道,甚至是套接字,有着更复杂的 API,可能是更好的选择。让具体的问题去指导我们的选择吧。

尽管所有的示例代码(可以在我的网站上获取到)都是使用 C 写的,其他的编程语言也经常提供这些 IPC 机制的轻量包装。这些代码示例都足够短小简单,希望这样能够鼓励你去进行实验。


via: https://opensource.com/article/19/4/interprocess-communication-linux-networking

作者:Marty Kalin 选题:lujun9972 译者:FSSlc 校对:wxy

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

本篇文章讨论了在 Linux 命令行中监控 CPU 和 GPU 温度的两种简单方式。

由于 Steam(包括 Steam Play,即 Proton)和一些其他的发展,GNU/Linux 正在成为越来越多计算机用户的日常游戏平台的选择。也有相当一部分用户在遇到像视频编辑或图形设计等(Kdenlive 和 Blender 是这类应用程序中很好的例子)资源消耗型计算任务时,也会使用 GNU/Linux。

不管你是否是这些用户中的一员或其他用户,你也一定想知道你的电脑 CPU 和 GPU 能有多热(如果你想要超频的话更会如此)。如果是这样,那么继续读下去。我们会介绍两个非常简单的命令来监控 CPU 和 GPU 温度。

我的装置包括一台 Slimbook Kymera 和两台显示器(一台 TV 和一台 PC 监视器),使得我可以用一台来玩游戏,另一台来留意监控温度。另外,因为我使用 Zorin OS,我会将关注点放在 Ubuntu 和 Ubuntu 的衍生发行版上。

为了监控 CPU 和 GPU 的行为,我们将利用实用的 watch 命令在每几秒钟之后动态地得到读数。

在 Linux 中监控 CPU 温度

对于 CPU 温度,我们将结合使用 watchsensors 命令。一篇关于此工具的图形用户界面版本的有趣文章已经在 It’s FOSS 中介绍过了。然而,我们将在此处使用命令行版本:

watch -n 2 sensors

watch 保证了读数会在每 2 秒钟更新一次(当然,这个周期值能够根据你的需要去更改):

Every 2,0s: sensors

iwlwifi-virtual-0
Adapter: Virtual device
temp1:        +39.0°C

acpitz-virtual-0
Adapter: Virtual device
temp1:        +27.8°C  (crit = +119.0°C)
temp2:        +29.8°C  (crit = +119.0°C)

coretemp-isa-0000
Adapter: ISA adapter
Package id 0:  +37.0°C  (high = +82.0°C, crit = +100.0°C)
Core 0:        +35.0°C  (high = +82.0°C, crit = +100.0°C)
Core 1:        +35.0°C  (high = +82.0°C, crit = +100.0°C)
Core 2:        +33.0°C  (high = +82.0°C, crit = +100.0°C)
Core 3:        +36.0°C  (high = +82.0°C, crit = +100.0°C)
Core 4:        +37.0°C  (high = +82.0°C, crit = +100.0°C)
Core 5:        +35.0°C  (high = +82.0°C, crit = +100.0°C)

除此之外,我们还能得到如下信息:

  • 我们有 5 个核心正在被使用(并且当前的最高温度为 37.0℃)。
  • 温度超过 82.0℃ 会被认为是过热。
  • 超过 100.0℃ 的温度会被认为是超过临界值。

根据以上的温度值我们可以得出结论,我的电脑目前的工作负载非常小。

在 Linux 中监控 GPU 温度

现在让我们来看看显卡。我从来没使用过 AMD 的显卡,因此我会将重点放在 Nvidia 的显卡上。我们需要做的第一件事是从 Ubuntu 的附加驱动 中下载合适的最新驱动。

在 Ubuntu(Zorin 或 Linux Mint 也是相同的)中,进入“软件和更新 > 附加驱动”选项,选择最新的可用驱动。另外,你可以添加或启用显示卡的官方 ppa(通过命令行或通过“软件和更新 > 其他软件”来实现)。安装驱动程序后,你将可以使用 “Nvidia X Server” 的 GUI 程序以及命令行工具 nvidia-smi(Nvidia 系统管理界面)。因此我们将使用 watchnvidia-smi

watch -n 2 nvidia-smi

与 CPU 的情况一样,我们会在每两秒得到一次更新的读数:

Every 2,0s: nvidia-smi

Fri Apr 19 20:45:30 2019
+-----------------------------------------------------------------------------+
| Nvidia-SMI 418.56       Driver Version: 418.56       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:01:00.0  On |                  N/A |
|  0%   54C    P8    10W / 120W |    433MiB /  6077MiB |      4%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1557      G   /usr/lib/xorg/Xorg                           190MiB |
|    0      1820      G   /usr/bin/gnome-shell                         174MiB |
|    0      7820      G   ...equest-channel-token=303407235874180773    65MiB |
+-----------------------------------------------------------------------------+

从这个表格中我们得到了关于显示卡的如下信息:

  • 它正在使用版本号为 418.56 的开源驱动。
  • 显示卡的当前温度为 54.0℃,并且风扇的使用量为 0%。
  • 电量的消耗非常低:仅仅 10W。
  • 总量为 6GB 的 vram(视频随机存取存储器),只使用了 433MB。
  • vram 正在被 3 个进程使用,他们的 ID 分别为 1557、1820 和 7820。

大部分这些事实或数值都清晰地表明,我们没有在玩任何消耗系统资源的游戏或处理大负载的任务。当我们开始玩游戏、处理视频或其他类似任务时,这些值就会开始上升。

结论

即便我们有 GUI 工具,但我还是发现这两个命令对于实时监控硬件非常的顺手。

你将如何去使用它们呢?你可以通过阅读他们的 man 手册来学习更多关于这些工具的使用技巧。

你有其他偏爱的工具吗?在评论里分享给我们吧 ;)。

玩得开心!


via: https://itsfoss.com/monitor-cpu-gpu-temp-linux/

作者:Alejandro Egea-Abellán 选题:lujun9972 译者:cycoe 校对:wxy

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

几天前,我们发布了一篇指南,其中涵盖了开始使用 Docker 时需要了解的几乎所有细节。在该指南中,我们向你展示了如何详细创建和管理 Docker 容器。还有一些可用于管理 Docker 容器的非官方工具。如果你看过我们以前的文章,你可能会看到两个基于 Web 的工具,PortainerPiCluster。它们都使得 Docker 管理任务在 Web 浏览器中变得更加容易和简单。今天,我遇到了另一个名为 Dockly 的 Docker 管理工具。

与上面的工具不同,Dockly 是一个 TUI(文本界面)程序,用于在类 Unix 系统中从终端管理 Docker 容器和服务。它是使用 NodeJS 编写的自由开源工具。在本简要指南中,我们将了解如何安装 Dockly 以及如何从命令行管理 Docker 容器。

安装 Dockly

确保已在 Linux 上安装了 NodeJS。如果尚未安装,请参阅以下指南。

安装 NodeJS 后,运行以下命令安装 Dockly:

# npm install -g dockly

使用 Dockly 在终端管理 Docker 容器

使用 Dockly 管理 Docker 容器非常简单!你所要做的就是打开终端并运行以下命令:

# dockly

Dockly 将通过 unix 套接字自动连接到你的本机 docker 守护进程,并在终端中显示正在运行的容器列表,如下所示。

使用 Dockly 管理 Docker 容器

正如你在上面的截图中看到的,Dockly 在顶部显示了运行容器的以下信息:

  • 容器 ID,
  • 容器名称,
  • Docker 镜像,
  • 命令,
  • 运行中容器的状态,
  • 状态。

在右上角,你将看到容器的 CPU 和内存利用率。使用向上/向下箭头键在容器之间移动。

在底部,有少量的键盘快捷键来执行各种 Docker 管理任务。以下是目前可用的键盘快捷键列表:

  • = - 刷新 Dockly 界面,
  • / - 搜索容器列表视图,
  • i - 显示有关当前所选容器或服务的信息,
  • 回车 - 显示当前容器或服务的日志,
  • v - 在容器和服务视图之间切换,
  • l - 在选定的容器上启动 /bin/bash 会话,
  • r - 重启选定的容器,
  • s - 停止选定的容器,
  • h - 显示帮助窗口,
  • q - 退出 Dockly。

查看容器的信息

使用向上/向下箭头选择一个容器,然后按 i 以显示所选容器的信息。

查看容器的信息

重启容器

如果你想随时重启容器,只需选择它并按 r 即可重新启动。

重启 Docker 容器

停止/删除容器和镜像

如果不再需要容器,我们可以立即停止和/或删除一个或所有容器。为此,请按 m 打开菜单。

停止,删除 Docker 容器和镜像

在这里,你可以执行以下操作。

  • 停止所有 Docker 容器,
  • 删除选定的容器,
  • 删除所有容器,
  • 删除所有 Docker 镜像等。

显示 Dockly 帮助部分

如果你有任何疑问,只需按 h 即可打开帮助部分。

Dockly 帮助

有关更多详细信息,请参考最后给出的官方 GitHub 页面。

就是这些了。希望这篇文章有用。如果你一直在使用 Docker 容器,请试试 Dockly,看它是否有帮助。

建议阅读:

资源:


via: https://www.ostechnix.com/dockly-manage-docker-containers-from-terminal/

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

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

在某些情况下,你可能想要将一个服务器上的软件包列表安装到另一个服务器上。例如,你已经在服务器 A 上安装了 15 个软件包并且这些软件包也需要被安装到服务器 B、服务器 C 上等等。

我们可以手动去安装这些软件但是这将花费大量的时间。你可以手动安装一俩个服务器,但是试想如果你有大概十个服务器呢。在这种情况下你无法手动完成工作,那么怎样才能解决问题呢?

不要担心我们可以帮你摆脱这样的情况和场景。我们在这篇文章中增加了四种方法来克服困难。

我希望这可以帮你解决问题。我已经在 Centos7 和 Ubuntu 18.04 上测试了这些命令。

我也希望这可以在其他发行版上工作。这仅仅需要使用该发行版的官方包管理器命令替代本文中的包管理器命令就行了。

如果想要 检查 Linux 系统上已安装的软件包列表,请点击链接。

例如,如果你想要在基于 RHEL 系统上创建软件包列表请使用以下步骤。其他发行版也一样。

# rpm -qa --last | head -15 | awk '{print $1}' > /tmp/pack1.txt

# cat /tmp/pack1.txt
mariadb-server-5.5.60-1.el7_5.x86_64
perl-DBI-1.627-4.el7.x86_64
perl-DBD-MySQL-4.023-6.el7.x86_64
perl-PlRPC-0.2020-14.el7.noarch
perl-Net-Daemon-0.48-5.el7.noarch
perl-IO-Compress-2.061-2.el7.noarch
perl-Compress-Raw-Zlib-2.061-4.el7.x86_64
mariadb-5.5.60-1.el7_5.x86_64
perl-Data-Dumper-2.145-3.el7.x86_64
perl-Compress-Raw-Bzip2-2.061-3.el7.x86_64
httpd-2.4.6-88.el7.centos.x86_64
mailcap-2.1.41-2.el7.noarch
httpd-tools-2.4.6-88.el7.centos.x86_64
apr-util-1.5.2-6.el7.x86_64
apr-1.4.8-3.el7_4.1.x86_64

方法一:如何在 Linux 上使用 cat 命令安装文件中列出的包?

为实现这个目标,我将使用简单明了的第一种方法。为此,创建一个文件并添加上你想要安装的包列表。

出于测试的目的,我们将只添加以下的三个软件包名到文件中。

# cat /tmp/pack1.txt

apache2
mariadb-server
nano

只要简单的运行 apt 命令 就能在 Ubuntu/Debian 系统上一次性安装所有的软件包。

# apt -y install $(cat /tmp/pack1.txt)

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  libopts25 sntp
Use 'sudo apt autoremove' to remove them.
Suggested packages:
  apache2-doc apache2-suexec-pristine | apache2-suexec-custom spell
The following NEW packages will be installed:
  apache2 mariadb-server nano
0 upgraded, 3 newly installed, 0 to remove and 24 not upgraded.
Need to get 339 kB of archives.
After this operation, 1,377 kB of additional disk space will be used.
Get:1 http://in.archive.ubuntu.com/ubuntu bionic-updates/main amd64 apache2 amd64 2.4.29-1ubuntu4.6 [95.1 kB]
Get:2 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 nano amd64 2.9.3-2 [231 kB]
Get:3 http://in.archive.ubuntu.com/ubuntu bionic-updates/universe amd64 mariadb-server all 1:10.1.38-0ubuntu0.18.04.1 [12.9 kB]
Fetched 339 kB in 19s (18.0 kB/s)
Selecting previously unselected package apache2.
(Reading database ... 290926 files and directories currently installed.)
Preparing to unpack .../apache2_2.4.29-1ubuntu4.6_amd64.deb ...
Unpacking apache2 (2.4.29-1ubuntu4.6) ...
Selecting previously unselected package nano.
Preparing to unpack .../nano_2.9.3-2_amd64.deb ...
Unpacking nano (2.9.3-2) ...
Selecting previously unselected package mariadb-server.
Preparing to unpack .../mariadb-server_1%3a10.1.38-0ubuntu0.18.04.1_all.deb ...
Unpacking mariadb-server (1:10.1.38-0ubuntu0.18.04.1) ...
Processing triggers for ufw (0.36-0ubuntu0.18.04.1) ...
Setting up apache2 (2.4.29-1ubuntu4.6) ...
Processing triggers for ureadahead (0.100.0-20) ...
Processing triggers for install-info (6.5.0.dfsg.1-2) ...
Setting up nano (2.9.3-2) ...
update-alternatives: using /bin/nano to provide /usr/bin/editor (editor) in auto mode
update-alternatives: using /bin/nano to provide /usr/bin/pico (pico) in auto mode
Processing triggers for systemd (237-3ubuntu10.20) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Setting up mariadb-server (1:10.1.38-0ubuntu0.18.04.1) ...

至于删除,需要使用相同的命令格式和适当的选项。

# apt -y remove $(cat /tmp/pack1.txt)
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  apache2-bin apache2-data apache2-utils galera-3 libaio1 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libconfig-inifiles-perl libdbd-mysql-perl libdbi-perl libjemalloc1 liblua5.2-0
  libmysqlclient20 libopts25 libterm-readkey-perl mariadb-client-10.1 mariadb-client-core-10.1 mariadb-common mariadb-server-10.1 mariadb-server-core-10.1 mysql-common sntp socat
Use 'apt autoremove' to remove them.
The following packages will be REMOVED:
  apache2 mariadb-server nano
0 upgraded, 0 newly installed, 3 to remove and 24 not upgraded.
After this operation, 1,377 kB disk space will be freed.
(Reading database ... 291046 files and directories currently installed.)
Removing apache2 (2.4.29-1ubuntu4.6) ...
Removing mariadb-server (1:10.1.38-0ubuntu0.18.04.1) ...
Removing nano (2.9.3-2) ...
update-alternatives: using /usr/bin/vim.tiny to provide /usr/bin/editor (editor) in auto mode
Processing triggers for ufw (0.36-0ubuntu0.18.04.1) ...
Processing triggers for install-info (6.5.0.dfsg.1-2) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...

使用 yum 命令 在基于 RHEL (如 Centos、RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上安装文件中列出的软件包。

# yum -y install $(cat /tmp/pack1.txt)

使用以命令在基于 RHEL (如 Centos、RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上卸载文件中列出的软件包。

# yum -y remove $(cat /tmp/pack1.txt)

使用以下 dnf 命令 在 Fedora 系统上安装文件中列出的软件包。

# dnf -y install $(cat /tmp/pack1.txt)

使用以下命令在 Fedora 系统上卸载文件中列出的软件包。

# dnf -y remove $(cat /tmp/pack1.txt)

使用以下 zypper 命令 在 openSUSE 系统上安装文件中列出的软件包。

# zypper -y install $(cat /tmp/pack1.txt)

使用以下命令从 openSUSE 系统上卸载文件中列出的软件包。

# zypper -y remove $(cat /tmp/pack1.txt)

使用以下 pacman 命令 在基于 Arch Linux (如 Manjaro 和 Antergos) 的系统上安装文件中列出的软件包。

# pacman -S $(cat /tmp/pack1.txt)

使用以下命令从基于 Arch Linux (如 Manjaro 和 Antergos) 的系统中卸载文件中列出的软件包。

# pacman -Rs $(cat /tmp/pack1.txt)

方法二:如何使用 cat 和 xargs 命令在 Linux 中安装文件中列出的软件包。

甚至,我更喜欢使用这种方法,因为这是一种非常简单直接的方法。

使用以下 apt 命令在基于 Debian 的系统 (如 Debian、Ubuntu 和 Linux Mint) 上安装文件中列出的软件包。

# cat /tmp/pack1.txt | xargs apt -y install

使用以下 apt 命令 从基于 Debian 的系统 (如 Debian、Ubuntu 和 Linux Mint) 上卸载文件中列出的软件包。

# cat /tmp/pack1.txt | xargs apt -y remove

使用以下 yum 命令在基于 RHEL (如 Centos,RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上安装文件中列出的软件包。

# cat /tmp/pack1.txt | xargs yum -y install

使用以命令从基于 RHEL (如 Centos、RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上卸载文件中列出的软件包。

# cat /tmp/pack1.txt | xargs yum -y remove

使用以下 dnf 命令在 Fedora 系统上安装文件中列出的软件包。

# cat /tmp/pack1.txt | xargs dnf -y install

使用以下命令从 Fedora 系统上卸载文件中列出的软件包。

# cat /tmp/pack1.txt | xargs dnf -y remove

使用以下 zypper 命令在 openSUSE 系统上安装文件中列出的软件包。

# cat /tmp/pack1.txt | xargs zypper -y install

使用以下命令从 openSUSE 系统上卸载文件中列出的软件包。

# cat /tmp/pack1.txt | xargs zypper -y remove

使用以下 pacman 命令在基于 Arch Linux (如 Manjaro 和 Antergos) 的系统上安装文件中列出的软件包。

# cat /tmp/pack1.txt | xargs pacman -S

使用下以命令从基于 Arch Linux (如 Manjaro 和 Antergos) 的系统上卸载文件中列出的软件包。

# cat /tmp/pack1.txt | xargs pacman -Rs

方法三 : 如何使用 For 循环在 Linux 上安装文件中列出的软件包

我们也可以使用 for 循环命令来实现此目的。

安装批量包可以使用以下一条 for 循环的命令。

# for pack in `cat /tmp/pack1.txt` ; do apt -y install $i; done

要使用 shell 脚本安装批量包,请使用以下 for 循环。

# vi /opt/scripts/bulk-package-install.sh

#!/bin/bash
for pack in `cat /tmp/pack1.txt`
do apt -y remove $pack
done

bulk-package-install.sh 设置可执行权限。

# chmod + bulk-package-install.sh

最后运行这个脚本。

# sh bulk-package-install.sh

方法四:如何使用 While 循环在 Linux 上安装文件中列出的软件包

我们也可以使用 while 循环命令来实现目的。

安装批量包可以使用以下一条 while 循环的命令。

# file="/tmp/pack1.txt"; while read -r pack; do apt -y install $pack; done < "$file"

要使用 shell 脚本安装批量包,请使用以下 while 循环。

# vi /opt/scripts/bulk-package-install.sh

#!/bin/bash
file="/tmp/pack1.txt"
while read -r pack
do apt -y remove $pack
done < "$file"

bulk-package-install.sh 设置可执行权限。

# chmod + bulk-package-install.sh

最后运行这个脚本。

# sh bulk-package-install.sh

via: https://www.2daygeek.com/how-to-install-uninstall-listed-packages-from-a-file-in-linux/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:way-ww 校对:wxy

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

为你的微服务架构或者集成测试创建一个简单的内部 CA。

传输层安全(TLS)模型(有时也称它的旧名称 SSL)基于 证书颁发机构 certificate authoritie (CA)的概念。这些机构受到浏览器和操作系统的信任,从而签名服务器的的证书以用于验证其所有权。

但是,对于内部网络,微服务架构或集成测试,有时候本地 CA更有用:一个只在内部受信任的 CA,然后签名本地服务器的证书。

这对集成测试特别有意义。获取证书可能会带来负担,因为这会占用服务器几分钟。但是在代码中使用“忽略证书”可能会被引入到生产环境,从而导致安全灾难。

CA 证书与常规服务器证书没有太大区别。重要的是它被本地代码信任。例如,在 Python requests 库中,可以通过将 REQUESTS_CA_BUNDLE 变量设置为包含此证书的目录来完成。

在为集成测试创建证书的例子中,不需要长期的证书:如果你的集成测试需要超过一天,那么你应该已经测试失败了。

因此,计算昨天明天作为有效期间隔:

>>> import datetime
>>> one_day = datetime.timedelta(days=1)
>>> today = datetime.date.today()
>>> yesterday = today - one_day
>>> tomorrow = today - one_day

现在你已准备好创建一个简单的 CA 证书。你需要生成私钥,创建公钥,设置 CA 的“参数”,然后自签名证书:CA 证书总是自签名的。最后,导出证书文件以及私钥文件。

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes, serialization
from cryptography import x509
from cryptography.x509.oid import NameOID


private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
public_key = private_key.public_key()
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
    x509.NameAttribute(NameOID.COMMON_NAME, 'Simple Test CA'),
]))
builder = builder.issuer_name(x509.Name([
    x509.NameAttribute(NameOID.COMMON_NAME, 'Simple Test CA'),
]))
builder = builder.not_valid_before(yesterday)
builder = builder.not_valid_after(tomorrow)
builder = builder.serial_number(x509.random_serial_number())
builder = builder.public_key(public_key)
builder = builder.add_extension(
    x509.BasicConstraints(ca=True, path_length=None),
    critical=True)
certificate = builder.sign(
    private_key=private_key, algorithm=hashes.SHA256(),
    backend=default_backend()
)
private_bytes = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncrption())
public_bytes = certificate.public_bytes(
    encoding=serialization.Encoding.PEM)
with open("ca.pem", "wb") as fout:
    fout.write(private_bytes + public_bytes)
with open("ca.crt", "wb") as fout:
    fout.write(public_bytes)

通常,真正的 CA 会需要证书签名请求(CSR)来签名证书。但是,当你是自己的 CA 时,你可以制定自己的规则!可以径直签名你想要的内容。

继续集成测试的例子,你可以创建私钥并立即签名相应的公钥。注意 COMMON_NAME 需要是 https URL 中的“服务器名称”。如果你已配置名称查询,你需要服务器能响应对 service.test.local 的请求。

service_private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
service_public_key = service_private_key.public_key()
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
   x509.NameAttribute(NameOID.COMMON_NAME, 'service.test.local')
]))
builder = builder.not_valid_before(yesterday)
builder = builder.not_valid_after(tomorrow)
builder = builder.public_key(public_key)
certificate = builder.sign(
    private_key=private_key, algorithm=hashes.SHA256(),
    backend=default_backend()
)
private_bytes = service_private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncrption())
public_bytes = certificate.public_bytes(
    encoding=serialization.Encoding.PEM)
with open("service.pem", "wb") as fout:
    fout.write(private_bytes + public_bytes)

现在 service.pem 文件有一个私钥和一个“有效”的证书:它已由本地的 CA 签名。该文件的格式可以给 Nginx、HAProxy 或大多数其他 HTTPS 服务器使用。

通过将此逻辑用在测试脚本中,只要客户端配置信任该 CA,那么就可以轻松创建看起来真实的 HTTPS 服务器。


via: https://opensource.com/article/19/4/certificate-authority

作者:Moshe Zadka 选题:lujun9972 译者:geekpi 校对:wxy

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