2019年12月

有没有想过你喜欢的开源项目或编程语言的名称来自何处?让我们按字母顺序了解一下流行的技术术语背后的起源故事。

GNOME、Java、Jupyter、Python……如果你的朋友或家人曾留意过你的工作对话,他们可能会认为你从事文艺复兴时期的民间文学艺术、咖啡烘焙、天文学或动物学工作。这些开源技术的名称从何而来?我们请我们的作者社区提供意见,并汇总了一些我们最喜欢的技术名称的起源故事。

Ansible

“Ansible”这个名称直接来自科幻小说。Ursula Le Guin 的《Rocannon's World》一书中能进行即时(比光速更快)通信的设备被称为 ansibles(显然来自 “answerable” 一词)。Ansibles 开始流行于科幻小说之中,Orson Scott Card 的《Ender's Game》(后来成为受欢迎的电影)中,该设备控制了许多远程太空飞船。对于控制分布式机器的软件来说,这似乎是一个很好的模型,因此 Michael DeHaan(Ansible 的创建者和创始人)借用了这个名称。

Apache

Apache 是最初于 1995 年发布的开源 Web 服务器。它的名称与著名的美国原住民部落无关;相反,它是指对原始软件代码的重复补丁。因此称之为,“ 一个修补的 A-patchy 服务器”。

awk

“awk(1) 代表着 Aho、Weinberger、Kernighan(作者)”—— Michael Greenberg

Bash

“最初的 Unix shell,即 Bourne shell,是以其创造者的名字命名的。在开发出来 Bash 时,csh(发音为 ‘seashell’)实际上更受交互登录用户的欢迎。Bash 项目旨在赋予 Bourne shell 新的生命,使其更适合于交互式使用,因此它被命名为 ‘Bourne again shell’,是‘ 重生 born again ’的双关语。”——Ken Gaillot

C

在早期,AT&T 的 Ken Thompson 和 Dennis Ritchie 发现可以使用更高级的编程语言(而不是低级的、可移植性更低的汇编编程)来编写操作系统和工具。早期有一个叫做 BCPL( 基本组合编程语言 Basic Combined programming Language )的编程系统,Thompson 创建了一个名为 B 的简化版 BCPL,但 B 的灵活性和速度都不高。然后,Ritchie 把 B 的思想扩展成一种叫做 C 的编译语言。”——Jim Hall

dd

“我想你发表这样一篇文章不能不提到 dd。我的外号叫 Didi。发音正确的话听起来像 ‘dd’。我开始学的是 Unix,然后是 Linux,那是在 1993 年,当时我还是个学生。然后我去了军队,来到了我的部队中少数几个使用 Unix(Ultrix)的部门之一(其它部门主要是 VMS),那里的一个人说:‘这么说,你是一个黑客,对吗?你以为你了解 Unix 吗?好的,那么 dd 这个名字的是怎么来的呢?’我不知道,试着猜道:‘ 数据复印机 Data duplicator ?’所以他说,‘我要告诉你 dd 的故事。dd 是 转换 convert 复制 copy 的缩写(如今人们仍然可以在手册页中看到),但由于 cc 这个缩写已经被 C 编译器占用,所以它被命名为 dd。’就在几年后,我听闻了关于 JCL 的数据定义和 Unix dd 命令不统一的、半开玩笑的语法的真实故事,某种程度是基于此的。”——Yedidyah Bar David

Emacs

经典的 反 vi anti-vi 编辑器,其名称的真正词源并不明显,因为它源自“ 编辑宏 Editing MACroS ”。但是,它作为一个伟大的宗教亵渎和崇拜的对象,吸引了许多恶作剧般的缩写,例如“Escape Meta Alt Control Shift”(以调侃其对键盘的大量依赖),“ 8MB 并经常发生内存交换 Eight Megabytes And Constantly Swapping ”(从那时起就很吃内存了),“ 最终分配了所有的计算机存储空间 Eventually malloc()s All Computer Storage ”和 “ EMACS 使一台计算机慢 EMACS Makes A Computer Slow ”——改编自 Jargon File/Hacker's Dictionary

Enarx

Enarx 是机密计算领域的一个新项目。该项目的设计原则之一是它应该是“可替代的”。因此最初的名字是“psilocybin”(著名的魔术蘑菇)。一般情况下,经理级别的人可能会对这个名称有所抵触,因此考虑使用新名称。该项目的两位创始人 Mike Bursell 和 Nathaniel McCallum 都是古老语言极客,因此他们考虑了许多不同的想法,包括 тайна(Tayna——俄语中代表秘密或神秘——虽然俄语并不是一门古老的语言,但你就不要在乎这些细节了),crypticon(希腊语的意思是完全私生的),cryptidion(希腊中表示小密室),arconus(拉丁语中表示秘密的褒义形容词),arcanum(拉丁语中表示秘密的中性形容词)和 ærn(盎格鲁撒克逊人表示地方、秘密的地方、壁橱、住所、房子,或小屋的词汇)。最后,由于各种原因,包括域名和 GitHub 项目名称的可用性,他们选择了 enarx,这是两个拉丁词根的组合:en-(表示内部)和 -arx(表示城堡、要塞或堡垒)。

GIMP

没有 GIMP 我们会怎么样? GNU 图像处理项目 GNU Image Manipulation Project 多年来一直是开源的重要基础。维基百科指出,“1995 年,Spencer Kimball) 和 Peter Mattis 在加州大学伯克利分校开始为 实验计算设施 eXperimental Computing Facility 开发 GIMP,这是一个为期一个学期的项目。”

GNOME

你有没有想过为什么 GNOME 被称为 GNOME?根据维基百科,GNOME 最初是一个表示“ GNU 网络对象模型环境 GNU Network Object Model Environment ”的缩写词。现在,该名称不再表示该项目,并且该项目已被放弃,但这个名称仍然保留了下来。GNOME 3 是 Fedora、红帽企业版、Ubuntu、Debian、SUSE Linux 企业版等发行版的默认桌面环境。

Java

你能想象这种编程语言还有其它名称吗?Java 最初被称为 Oak,但是遗憾的是,Sun Microsystems 的法律团队由于已有该商标而否决了它。所以开发团队又重新给它命名。据说该语言的工作组在 1995 年 1 月举行了一次大规模的头脑风暴。许多其它名称也被扔掉了,包括 Silk、DNA、WebDancer 等。该团队不希望新名称与过度使用的术语“web”或“net”有任何关系。取而代之的是,他们在寻找更有活力、更有趣、更容易记住的东西。Java 满足了这些要求,并且奇迹般地,团队同意通过了!

Jupyter

现在许多数据科学家和学生在工作中使用 Jupyter 笔记本。“Jupyter”这个名字是三种开源计算机语言的融合,这三种语言在这个笔记本中都有使用,在数据科学中也很突出:JuliaPythonR

Kubernetes

Kubernetes 源自希腊语中的舵手。Kubernetes 项目创始人 Craig McLuckie 在 2015 Hacker News 回应中证实了这种词源。他坚持航海主题,解释说,这项技术可以驱动集装箱,就像舵手或驾驶员驾驶集装箱船一样,因此,他选择了 Kubernetes 这个名字。我们中的许多人仍然在尝试正确的发音(koo-bur-NET-eez),因此 替代使用 K8s 也是可以接受的。有趣的是,它与英语单词“ 行政长官 governor ”具有相同的词源,也与蒸汽机上的机械负反馈装置相同。

KDE

那 K 桌面呢?KDE 最初代表“ 酷桌面环境 Kool Desktop Environment ”。 它由 Matthias Ettrich 于 1996 年创立。根据维基百科上的说法,该名称是对 Unix 上 通用桌面环境 Common Desktop Environment (CDE)一词的调侃。

Linux

Linux 因其发明者 Linus Torvalds 的名字命名的。Linus 最初想将他的作品命名为“Freax”,因为他认为以他自己的名字命名太自负了。根据维基百科的说法,“赫尔辛基科技大学 Torvalds 的同事 Ari Lemmke 当时是 FTP 服务器的志愿管理员之一,他并不认为‘Freax’是个好名字。因此,他没有征询 Torvalds 就将服务器上的这个项目命名为‘Linux’。”

以下是一些最受欢迎的 Linux 发行版。

CentOS

CentOS 社区企业操作系统 Community Enterprise Operating System 的缩写。它包含来自 Red Hat Enterprise Linux 的上游软件包。

Debian

Debian Linux 创建于 1993 年 9 月,是其创始人 Ian Murdock 和他当时的女友 Debra Lynn 的名字的混成词。

RHEL

Red Hat Linux 得名于它的创始人 Marc Ewing,他戴着一顶祖父送给他的康奈尔大学红色 软呢帽 fedora 。红帽公司成立于 1993 年 3 月 26 日。Fedora Linux 最初是一个志愿者项目,旨在为红帽发行版提供额外的软件,它的名字来自红帽的“Shadowman”徽标。

Ubuntu

Ubuntu 旨在广泛分享开源软件,它以非洲哲学“ 人的本质 ubuntu ”命名,可以翻译为“对他人的人道主义”或“我之所以是我,是因为我们都是这样的人”。

Moodle

开源学习平台 Moodle 是“ 模块化面向对象动态学习环境 modular object-oriented dynamic learning environment ”的首字母缩写。Moodle 仍然是领先的线上学习平台。全球有近 10.4 万个注册的 Moodle 网站。

另外两个流行的开源内容管理系统是 Drupal 和 Joomla。Drupal 的名字来自荷兰语 “druppel”,意思是“掉落”。根据维基百科,Joomla 是斯瓦希里语单词“jumla”的英式拼写,在阿拉伯语、乌尔都语和其他语言中是“在一起”的意思。

Mozilla

Mozilla 是一个成立于 1998 年的开源软件社区。根据其网站,“Mozilla 项目创建于 1998 年,发布了 Netscape 浏览器套件源代码。其旨在利用互联网上成千上万的程序员的创造力,并推动浏览器市场上前所未有的创新水平。” 这个名字是 Mosaic) 和 Godzilla 的混成词。

Nginx

“许多技术人员都试图装酷,并将它念成‘n’‘g’‘n’‘x’。实际上,很少的一些人做点基本的调查工作,就可以很快发现该名称实际上应该被念成是“EngineX”,指的是功能强大的 web 服务器,像个引擎。”——Jean Sebastien Tougne

Perl

Perl 的创始人 Larry Wall 最初将他的项目命名为“Pearl”。根据维基百科,Wall 想给这种语言起一个有积极含义的简短名字。在 Perl 正式发布之前,Wall 发现了已有 PEARL) 编程语言,于是更改了名称的拼写。

Piet 和 Mondrian

“有两种编程语言以艺术家 Piet Mondrian 命名。一种叫做‘Piet’,另一种叫做‘Mondrian’。(David Morgan-Mar 写道):‘Piet 是一种编程语言,其中的程序看起来像抽象绘画。该语言以几何抽象艺术的开创者 Piet Mondrian 的名字命名。我曾想将这种语言命名为 Mondrian,但是有人告诉我这会让它看起来像一种很普通的脚本语言。哦,好吧,我想我们不能都是深奥的语言作家。’”——Yuval Lifshitz

Python

Python 编程语言的独特名称来自其创建者 Guido Van Rossum,他是英国六人喜剧团体 Monty Python 的粉丝。

Raspberry Pi

Raspberry Pi 以其微小但强大的功能和对低廉的价格而闻名,在开源社区中是最受欢迎的。但是它可爱(和好吃)的名字是从哪里来的呢?在 70 年代和 80 年代,以水果命名的计算机是一种流行的趋势。苹果、橘子、杏……有人饿了吗?根据创始人 Eben Upton 的 2012 采访,“ 树莓派 Raspberry Pi ”这个名称是对这种趋势的致敬。树莓也很小,但却很有味道。名称中的“Pi”暗示着这样的事实:最初,该计算机只能运行 Python。

Samba

Server Message Block 用于在 Linux 上共享 Windows 文件。

ScummVM

ScummVM(《疯狂大楼》虚拟机的脚本创建实用程序)是一个程序,可以在现代计算机上运行一些经典的计算机冒险游戏。最初,它旨在玩用 SCUMM 构建的 LucasArts 的冒险游戏,该游戏最初用于开发《疯狂大楼》,后来又被用来开发 LucasArts 的其它大多数冒险游戏。目前,ScummVM 支持大量游戏引擎,包括 Sierra Online 的 AGI 和 SCI,但仍保留着名称 ScummVM。

有一个相关的项目 ResidualVM 之所以得名,是因为它涵盖了 ScummVM 未涵盖的“ 剩余的 residual ” LucasArts 冒险游戏。 ResidualVM 涵盖的 LucasArts 游戏是使用 GrimE(Grim Engine)开发的,该引擎最初用于开发 Grim Fandango,因此 ResidualVM 的名称是双关语。

SQL

“你可能知道 SQL 代表 结构化查询语言 Structured Query Language ,但你知道为什么它经常被读作‘sequel’吗?它是作为原本的‘QUEL’( 查询语言 QUEry Language )的后续(如 结局 sequel )而创建的。”——Ken Gaillot

XFCE

XFCE 是由 Olivier Fourdan 创建的一个流行的桌面。它在 1996 年作为 CDE 的替代品出现,最初是 XForms 公共环境 XForms Common Environment 的缩写。

Zsh

Zsh 是一个交互式登录 shell。1990 年,普林斯顿大学的学生 Paul Falstad 写了该 shell 的第一个版本。他在看到当时在普林斯顿大学担任助教的 Zhong Sha 的登录 ID(zsh)后,觉得这个名字听起来像 shell 的好名字,给它起了这个名字。

还有更多的项目和名称还没有包括在这个列表中。请一定要在评论中分享你的收藏。


via: https://opensource.com/article/19/10/open-source-name-origins

作者:Joshua Allen Holm 选题:lujun9972 译者:laingke 校对:wxy

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

学习逻辑操作符和 shell 扩展,本文是三篇 Bash 编程系列的第二篇。

Bash 是一种强大的编程语言,完美契合命令行和 shell 脚本。本系列(三篇文章,基于我的 三集 Linux 自学课程)讲解如何在 CLI 使用 Bash 编程。

第一篇文章 讲解了 Bash 的一些简单命令行操作,包括如何使用变量和控制操作符。第二篇文章探讨文件、字符串、数字等类型和各种各样在执行流中提供控制逻辑的的逻辑运算符,还有 Bash 中的各类 shell 扩展。本系列第三篇也是最后一篇文章,将会探索能重复执行操作的 forwhileuntil 循环。

逻辑操作符是程序中进行判断的根本要素,也是执行不同的语句组合的依据。有时这也被称为流控制。

逻辑操作符

Bash 中有大量的用于不同条件表达式的逻辑操作符。最基本的是 if 控制结构,它判断一个条件,如果条件为真,就执行一些程序语句。操作符共有三类:文件、数字和非数字操作符。如果条件为真,所有的操作符返回真值(0),如果条件为假,返回假值(1)。

这些比较操作符的函数语法是,一个操作符加一个或两个参数放在中括号内,后面跟一系列程序语句,如果条件为真,程序语句执行,可能会有另一个程序语句列表,该列表在条件为假时执行:

if [ arg1 operator arg2 ] ; then list
或
if [ arg1 operator arg2 ] ; then list ; else list ; fi

像例子中那样,在比较表达式中,空格不能省略。中括号的每部分,[],是跟 test 命令一样的传统的 Bash 符号:

if test arg1 operator arg2 ; then list

还有一个更新的语法能提供一点点便利,一些系统管理员比较喜欢用。这种格式对于不同版本的 Bash 和一些 shell 如 ksh(Korn shell)兼容性稍差。格式如下:

if [[ arg1 operator arg2 ]] ; then list

文件操作符

文件操作符是 Bash 中一系列强大的逻辑操作符。图表 1 列出了 20 多种不同的 Bash 处理文件的操作符。在我的脚本中使用频率很高。

操作符描述
-a filename如果文件存在,返回真值;文件可以为空也可以有内容,但是只要它存在,就返回真值
-b filename如果文件存在且是一个块设备,如 /dev/sda/dev/sda1,则返回真值
-c filename如果文件存在且是一个字符设备,如 /dev/TTY1,则返回真值
-d filename如果文件存在且是一个目录,返回真值
-e filename如果文件存在,返回真值;与上面的 -a 相同
-f filename如果文件存在且是一个一般文件,不是目录、设备文件或链接等的其他的文件,则返回 真值
-g filename如果文件存在且 SETGID 标记被设置在其上,返回真值
-h filename如果文件存在且是一个符号链接,则返回真值
-k filename如果文件存在且粘滞位已设置,则返回真值
-p filename如果文件存在且是一个命名的管道(FIFO),返回真值
-r filename如果文件存在且有可读权限(它的可读位被设置),返回真值
-s filename如果文件存在且大小大于 0,返回真值;如果一个文件存在但大小为 0,则返回假值
-t fd如果文件描述符 fd 被打开且被关联到一个终端设备上,返回真值
-u filename如果文件存在且它的 SETUID 位被设置,返回真值
-w filename如果文件存在且有可写权限,返回真值
-x filename如果文件存在且有可执行权限,返回真值
-G filename如果文件存在且文件的组 ID 与当前用户相同,返回真值
-L filename如果文件存在且是一个符号链接,返回真值(同 -h
-N filename如果文件存在且从文件上一次被读取后文件被修改过,返回真值
-O filename如果文件存在且你是文件的拥有者,返回真值
-S filename如果文件存在且文件是套接字,返回真值
file1 -ef file2如果文件 file1 和文件 file2 指向同一设备的同一 INODE 号,返回真值(即硬链接)
file1 -nt file2如果文件 file1file2 新(根据修改日期),或 file1 存在而 file2 不存在,返回真值
file1 -ot file2如果文件 file1file2 旧(根据修改日期),或 file1 不存在而 file2 存在

图表 1:Bash 文件操作符

以测试一个文件存在与否来举例:

[student@studentvm1 testdir]$ File="TestFile1" ; if [ -e $File ] ; then echo "The file $File exists." ; else echo "The file $File does not exist." ; fi
The file TestFile1 does not exist.
[student@studentvm1 testdir]$

创建一个用来测试的文件,命名为 TestFile1。目前它不需要包含任何数据:

[student@studentvm1 testdir]$ touch TestFile1

在这个简短的 CLI 程序中,修改 $File 变量的值相比于在多个地方修改表示文件名的字符串的值要容易:

[student@studentvm1 testdir]$ File="TestFile1" ; if [ -e $File ] ; then echo "The file $File exists." ; else echo "The file $File does not exist." ; fi
The file TestFile1 exists.
[student@studentvm1 testdir]$

现在,运行一个测试来判断一个文件是否存在且长度不为 0(表示它包含数据)。假设你想判断三种情况:

  1. 文件不存在;
  2. 文件存在且为空;
  3. 文件存在且包含数据。

因此,你需要一组更复杂的测试代码 — 为了测试所有的情况,使用 if-elif-else 结构中的 elif 语句:

[student@studentvm1 testdir]$ File="TestFile1" ; if [ -s $File ] ; then echo "$File exists and contains data." ; fi
[student@studentvm1 testdir]$

在这个情况中,文件存在但不包含任何数据。向文件添加一些数据再运行一次:

[student@studentvm1 testdir]$ File="TestFile1" ; echo "This is file $File" > $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; fi
TestFile1 exists and contains data.
[student@studentvm1 testdir]$

这组语句能返回正常的结果,但是仅仅是在我们已知三种可能的情况下测试某种确切的条件。添加一段 else 语句,这样你就可以更精确地测试。把文件删掉,你就可以完整地测试这段新代码:

[student@studentvm1 testdir]$ File="TestFile1" ; rm $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; else echo "$File does not exist or is empty." ; fi
TestFile1 does not exist or is empty.

现在创建一个空文件用来测试:

[student@studentvm1 testdir]$ File="TestFile1" ; touch $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; else echo "$File does not exist or is empty." ; fi
TestFile1 does not exist or is empty.

向文件添加一些内容,然后再测试一次:

[student@studentvm1 testdir]$ File="TestFile1" ; echo "This is file $File" > $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; else echo "$File does not exist or is empty." ; fi
TestFile1 exists and contains data.

现在加入 elif 语句来辨别是文件不存在还是文件为空:

[student@studentvm1 testdir]$ File="TestFile1" ; touch $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; elif [ -e $File ] ; then echo "$File exists and is empty." ; else echo "$File does not exist." ; fi
TestFile1 exists and is empty.
[student@studentvm1 testdir]$ File="TestFile1" ; echo "This is $File" > $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; elif [ -e $File ] ; then echo "$File exists and is empty." ; else echo "$File does not exist." ; fi
TestFile1 exists and contains data.
[student@studentvm1 testdir]$

现在你有一个可以测试这三种情况的 Bash CLI 程序,但是可能的情况是无限的。

如果你能像保存在文件中的脚本那样组织程序语句,那么即使对于更复杂的命令组合也会很容易看出它们的逻辑结构。图表 2 就是一个示例。 if-elif-else 结构中每一部分的程序语句的缩进让逻辑更变得清晰。

File="TestFile1"
echo "This is $File" > $File
if [ -s $File ]
   then
   echo "$File exists and contains data."
elif [ -e $File ]
   then
   echo "$File exists and is empty."
else
   echo "$File does not exist."
fi

图表 2: 像在脚本里一样重写书写命令行程序

对于大多数 CLI 程序来说,让这些复杂的命令变得有逻辑需要写很长的代码。虽然 CLI 可能是用 Linux 或 Bash 内置的命令,但是当 CLI 程序很长或很复杂时,创建一个保存在文件中的脚本将更有效,保存到文件中后,可以随时运行。

字符串比较操作符

字符串比较操作符使我们可以对字符串中的字符按字母顺序进行比较。图表 3 列出了仅有的几个字符串比较操作符。

操作符描述
-z string如果字符串的长度为 0 ,返回真值
-n string如果字符串的长度不为 0 ,返回真值
string1 == string2string1 = string2如果两个字符串相等,返回真值。处于遵从 POSIX 一致性,在测试命令中应使用一个等号 =。与命令 [[ 一起使用时,会进行如上描述的模式匹配(混合命令)。
string1 != string2两个字符串不相等,返回真值
string1 < string2如果对 string1string2 按字母顺序进行排序,string1 排在 string2 前面(即基于地区设定的对所有字母和特殊字符的排列顺序)
string1 > string2如果对 string1string2 按字母顺序进行排序,string1 排在 string2 后面

图表 3: Bash 字符串逻辑操作符

首先,检查字符串长度。比较表达式中 $MyVar 两边的双引号不能省略(你仍应该在目录 ~/testdir 下 )。

[student@studentvm1 testdir]$ MyVar="" ; if [ -z "" ] ; then echo "MyVar is zero length." ; else echo "MyVar contains data" ; fi
MyVar is zero length.
[student@studentvm1 testdir]$ MyVar="Random text" ; if [ -z "" ] ; then echo "MyVar is zero length." ; else echo "MyVar contains data" ; fi
MyVar is zero length.

你也可以这样做:

[student@studentvm1 testdir]$ MyVar="Random text" ; if [ -n "$MyVar" ] ; then echo "MyVar contains data." ; else echo "MyVar is zero length" ; fi
MyVar contains data.
[student@studentvm1 testdir]$ MyVar="" ; if [ -n "$MyVar" ] ; then echo "MyVar contains data." ; else echo "MyVar is zero length" ; fi
MyVar is zero length

有时候你需要知道一个字符串确切的长度。这虽然不是比较,但是也与比较相关。不幸的是,计算字符串的长度没有简单的方法。有很多种方法可以计算,但是我认为使用 expr(求值表达式)命令是相对最简单的一种。阅读 expr 的手册页可以了解更多相关知识。注意表达式中你检测的字符串或变量两边的引号不要省略。

[student@studentvm1 testdir]$ MyVar="" ; expr length "$MyVar"
0
[student@studentvm1 testdir]$ MyVar="How long is this?" ; expr length "$MyVar"
17
[student@studentvm1 testdir]$ expr length "We can also find the length of a literal string as well as a variable."
70

关于比较操作符,在我们的脚本中使用了大量的检测两个字符串是否相等(例如,两个字符串是否实际上是同一个字符串)的操作。我使用的是非 POSIX 版本的比较表达式:

[student@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello World" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches Var2" ; else echo "Var1 and Var2 do not match." ; fi
Var1 matches Var2
[student@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello world" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches Var2" ; else echo "Var1 and Var2 do not match." ; fi
Var1 and Var2 do not match.

在你自己的脚本中去试一下这些操作符。

数字比较操作符

数字操作符用于两个数字参数之间的比较。像其他类操作符一样,大部分都很容易理解。

操作符描述
arg1 -eq arg2如果 arg1 等于 arg2,返回真值
arg1 -ne arg2如果 arg1 不等于 arg2,返回真值
arg1 -lt arg2如果 arg1 小于 arg2,返回真值
arg1 -le arg2如果 arg1 小于或等于 arg2,返回真值
arg1 -gt arg2如果 arg1 大于 arg2,返回真值
arg1 -ge arg2如果 arg1 大于或等于 arg2,返回真值

图表 4: Bash 数字比较逻辑操作符

来看几个简单的例子。第一个示例设置变量 $X 的值为 1,然后检测 $X 是否等于 1。第二个示例中,$X 被设置为 0,所以比较表达式返回结果不为真值。

[student@studentvm1 testdir]$ X=1 ; if [ $X -eq 1 ] ; then echo "X equals 1" ; else echo "X does not equal 1" ; fi
X equals 1
[student@studentvm1 testdir]$ X=0 ; if [ $X -eq 1 ] ; then echo "X equals 1" ; else echo "X does not equal 1" ; fi
X does not equal 1
[student@studentvm1 testdir]$

自己来多尝试一下其他的。

杂项操作符

这些杂项操作符展示一个 shell 选项是否被设置,或一个 shell 变量是否有值,但是它不显示变量的值,只显示它是否有值。

操作符描述
-o optname如果一个 shell 选项 optname 是启用的(查看内建在 Bash 手册页中的 set -o 选项描述下面的选项列表),则返回真值
-v varname如果 shell 变量 varname 被设置了值(被赋予了值),则返回真值
-R varname如果一个 shell 变量 varname 被设置了值且是一个名字引用,则返回真值

图表 5: 杂项 Bash 逻辑操作符

自己来使用这些操作符实践下。

扩展

Bash 支持非常有用的几种类型的扩展和命令替换。根据 Bash 手册页,Bash 有七种扩展格式。本文只介绍其中五种:~ 扩展、算术扩展、路径名称扩展、大括号扩展和命令替换。

大括号扩展

大括号扩展是生成任意字符串的一种方法。(下面的例子是用特定模式的字符创建大量的文件。)大括号扩展可以用于产生任意字符串的列表,并把它们插入一个用静态字符串包围的特定位置或静态字符串的两端。这可能不太好想象,所以还是来实践一下。

首先,看一下大括号扩展的作用:

[student@studentvm1 testdir]$ echo {string1,string2,string3}
string1 string2 string3

看起来不是很有用,对吧?但是用其他方式使用它,再来看看:

[student@studentvm1 testdir]$ echo "Hello "{David,Jen,Rikki,Jason}.
Hello David. Hello Jen. Hello Rikki. Hello Jason.

这看起来貌似有点用了 — 我们可以少打很多字。现在试一下这个:

[student@studentvm1 testdir]$ echo b{ed,olt,ar}s
beds bolts bars

我可以继续举例,但是你应该已经理解了它的用处。

~ 扩展

资料显示,使用最多的扩展是波浪字符(~)扩展。当你在命令中使用它(如 cd ~/Documents)时,Bash shell 把这个快捷方式展开成用户的完整的家目录。

使用这个 Bash 程序观察 ~ 扩展的作用:

[student@studentvm1 testdir]$ echo ~
/home/student
[student@studentvm1 testdir]$ echo ~/Documents
/home/student/Documents
[student@studentvm1 testdir]$ Var1=~/Documents ; echo $Var1 ; cd $Var1
/home/student/Documents
[student@studentvm1 Documents]$

路径名称扩展

路径名称扩展是展开文件通配模式为匹配该模式的完整路径名称的另一种说法,匹配字符使用 ?*。文件通配指的是在大量操作中匹配文件名、路径和其他字符串时用特定的模式字符产生极大的灵活性。这些特定的模式字符允许匹配字符串中的一个、多个或特定字符。

  • ? — 匹配字符串中特定位置的一个任意字符
  • * — 匹配字符串中特定位置的 0 个或多个任意字符

这个扩展用于匹配路径名称。为了弄清它的用法,请确保 testdir 是当前工作目录(PWD),先执行基本的列出清单命令 ls(我家目录下的内容跟你的不一样)。

[student@studentvm1 testdir]$ ls
chapter6  cpuHog.dos    dmesg1.txt  Documents  Music       softlink1  testdir6    Videos
chapter7  cpuHog.Linux  dmesg2.txt  Downloads  Pictures    Templates  testdir
testdir  cpuHog.mac    dmesg3.txt  file005    Public      testdir    tmp
cpuHog     Desktop       dmesg.txt   link3      random.txt  testdir1   umask.test
[student@studentvm1 testdir]$

现在列出以 Dotestdir/Documentstestdir/Downloads 开头的目录:

Documents:
Directory01  file07  file15        test02  test10  test20      testfile13  TextFiles
Directory02  file08  file16        test03  test11  testfile01  testfile14
file01       file09  file17        test04  test12  testfile04  testfile15
file02       file10  file18        test05  test13  testfile05  testfile16
file03       file11  file19        test06  test14  testfile09  testfile17
file04       file12  file20        test07  test15  testfile10  testfile18
file05       file13  Student1.txt  test08  test16  testfile11  testfile19
file06       file14  test01        test09  test18  testfile12  testfile20

Downloads:
[student@studentvm1 testdir]$

然而,并没有得到你期望的结果。它列出了以 Do 开头的目录下的内容。使用 -d 选项,仅列出目录而不列出它们的内容。

[student@studentvm1 testdir]$ ls -d Do*
Documents  Downloads
[student@studentvm1 testdir]$

在两个例子中,Bash shell 都把 Do* 模式展开成了匹配该模式的目录名称。但是如果有文件也匹配这个模式,会发生什么?

[student@studentvm1 testdir]$ touch Downtown ; ls -d Do*
Documents  Downloads  Downtown
[student@studentvm1 testdir]$

因此所有匹配这个模式的文件也被展开成了完整名字。

命令替换

命令替换是让一个命令的标准输出数据流被当做参数传给另一个命令的扩展形式,例如,在一个循环中作为一系列被处理的项目。Bash 手册页显示:“命令替换可以让你用一个命令的输出替换为命令的名字。”这可能不太好理解。

命令替换有两种格式:command$(command)。在更早的格式中使用反引号(`),在命令中使用反斜杠(\)来保持它转义之前的文本含义。然而,当用在新版本的括号格式中时,反斜杠被当做一个特殊字符处理。也请注意带括号的格式打开个关闭命令语句都是用一个括号。

我经常在命令行程序和脚本中使用这种能力,一个命令的结果能被用作另一个命令的参数。

来看一个非常简单的示例,这个示例使用了这个扩展的两种格式(再一次提醒,确保 testdir 是当前工作目录):

[student@studentvm1 testdir]$ echo "Todays date is `date`"
Todays date is Sun Apr  7 14:42:46 EDT 2019
[student@studentvm1 testdir]$ echo "Todays date is $(date)"
Todays date is Sun Apr  7 14:42:59 EDT 2019
[student@studentvm1 testdir]$

-seq 工具用于一个数字序列:

[student@studentvm1 testdir]$ seq 5
1
2
3
4
5
[student@studentvm1 testdir]$ echo `seq 5`
1 2 3 4 5
[student@studentvm1 testdir]$

现在你可以做一些更有用处的操作,比如创建大量用于测试的空文件。

[student@studentvm1 testdir]$ for I in $(seq -w 5000) ; do touch file-$I ; done

seq 工具加上 -w 选项后,在生成的数字前面会用 0 补全,这样所有的结果都等宽,例如,忽略数字的值,它们的位数一样。这样在对它们按数字顺序进行排列时很容易。

seq -w 5000 语句生成了 1 到 5000 的数字序列。通过把命令替换用于 for 语句,for 语句就可以使用该数字序列来生成文件名的数字部分。

算术扩展

Bash 可以进行整型的数学计算,但是比较繁琐(你一会儿将看到)。数字扩展的语法是 $((arithmetic-expression)) ,分别用两个括号来打开和关闭表达式。算术扩展在 shell 程序或脚本中类似命令替换;表达式结算后的结果替换了表达式,用于 shell 后续的计算。

我们再用一个简单的用法来开始:

[student@studentvm1 testdir]$ echo $((1+1))
2
[student@studentvm1 testdir]$ Var1=5 ; Var2=7 ; Var3=$((Var1*Var2)) ; echo "Var 3 = $Var3"
Var 3 = 35

下面的除法结果是 0,因为表达式的结果是一个小于 1 的整型数字:

[student@studentvm1 testdir]$ Var1=5 ; Var2=7 ; Var3=$((Var1/Var2)) ; echo "Var 3 = $Var3"
Var 3 = 0

这是一个我经常在脚本或 CLI 程序中使用的一个简单的计算,用来查看在 Linux 主机中使用了多少虚拟内存。 free 不提供我需要的数据:

[student@studentvm1 testdir]$ RAM=`free | grep ^Mem | awk '{print $2}'` ; Swap=`free | grep ^Swap | awk '{print $2}'` ; echo "RAM = $RAM and Swap = $Swap" ; echo "Total Virtual memory is $((RAM+Swap))" ;
RAM = 4037080 and Swap = 6291452
Total Virtual memory is 10328532

我使用 ` 字符来划定用作命令替换的界限。

我用 Bash 算术扩展的场景主要是用脚本检查系统资源用量后基于返回的结果选择一个程序运行的路径。

总结

本文是 Bash 编程语言系列的第二篇,探讨了 Bash 中文件、字符串、数字和各种提供流程控制逻辑的逻辑操作符还有不同种类的 shell 扩展。


via: https://opensource.com/article/19/10/programming-bash-logical-operators-shell-expansions

作者:David Both 选题:lujun9972 译者:lxbwolf 校对:wxy

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

似乎每一家互联网公司都有一种属于自己的气质,我接触过很多知名互联网公司的技术专家,其中有一家公司技术专家给了非常深的印象,那就是 UCloud。在和 UCloud 技术专家的沟通中,我深深感受到这是一支极为自信、动手能力很强的技术团队。

近期,我在UCloud上海办公室采访了 UCloud 虚拟网络负责人徐亮,就如同我采访过的很多一线技术领军人物一样,徐亮给我带来的直接印象就是执着、朴实和认真。

说到自己擅长的网络技术领域,徐亮神采飞扬

在和他的谈话中,我听到了不少 UCloud 有趣的研发故事,让我对 UCloud 技术团队的动手能力有了更深的认识。尤其是, 当UCloud 所倡行的“客户为先”、“客户的需求就是我们的下一个产品”等理念从一个专注于前沿技术的技术领军人物的谈吐中汩汩冒出时,印象更深刻了。

转化:实验室技术能力 ➡️➡️➡️ 生产能力

我为什么认为 UCloud 的技术团队是一支动手能力极强,这得从他们的 25G 智能网卡项目说起。

众所周知,如何将实验室形成的技术能力转化成生产能力,需要很好的工程能力,25G 智能网卡从实验室到生产环境恰恰体现了这样的工程能力。

“去年我们将 25G 智能网卡产品投入到生产环境,实际上,UCloud 从 2016 年就开始跟踪这项技术。这期间我们测试了很多厂商的智能网卡,有的智能网卡的性能相当不错。但是阻碍我们将其投入生产环境的一个重要原因是,它和公有云所要求的热迁移的能力不兼容。所以,虽然这些智能网卡的性能很好,但是没有办法应用到公有云环境。”徐亮介绍道。

其实,业界一些公有云厂商很早就在借助这些智能网卡做加速,但是在处理热迁移的时候做不到用户的无感迁移,必须要用户手动修改虚机里面网络的配置。这虽然能够达到目的,但是对用户并不友好。对此,UCloud 秉持一向的态度:“我们一定要做到用户没有额外的负担,这样对用户来说才是最佳的方案,才是成熟的、用户友好的方案。”

据徐亮回忆,情况在 2017 年底出现了一个转机。彼时,Linux 内核社区开始讨论智能网卡如何能够无感的支持热迁移,UCloud 技术团队第一时间进行了深入的技术追踪和研究。从社区开始讨论和开发,最后到 2018 年 5 月份时该功能趋于稳定,才被接受到 Linux 的内核主线里。

“在发现该功能已经基本成熟后,我们马上就把这个补丁应用回 UCloud 的定制内核当中,跟智能网卡厂商一起研究,很快就在实验室完成了该产品。”徐亮接着谈到,“然后我们就开始在线上做公测。这个时候就非常体现我们的工程能力。”

在徐亮的团队将智能网卡投入生产环境时,虽然也发生了一些问题,但是就算在连固件都升级过两次的情况下、对用户的业务并没有产生太大的影响,我想这就是 UCloud 技术团队的工程能力一个重要体现——“我的固件都升级了,而用户业务不受影响。”

驱动:客户为先 ➡️➡️➡️ 工程能力

在我采访的 UCloud 技术人员中,徐亮并不是第一个提到“客户为先”、“客户的需求就是我们下一个产品”等理念的,在与 UCloud 技术副总裁杨镭、私有云和容器产品线负责人叶理灯等人的采访沟通中他们都曾提及这一贯彻于 UCloud 所有技术、产品研发中的理念。他们对于“客户为先”以及在产品的研发、技术专研中的践行,让我深信他们从骨子里认可这样一个价值观。可以说,“用户为先”的理念驱动着其工程能力的形成。

谈到他们的经典网络升级至 VPC 2.0 项目,也许你会理解我说的。

以“客户为先”为出发点

据我了解,UCloud 应该是全球唯一一家把经典网络升级到 VPC 2.0 网络的公有云厂商。在我和多位 UCloud 技术人员的接触中,这个项目被多次提及,它的实施可谓是历经周折,遇到的困难很多。

我们的出发点是‘客户是不是有这个诉求?这件事情对客户来说是不是有好处?’如果是的话,那我们为什么不做呢?“。当问及项目出发点时,徐亮谈到。显然,如果能够透明的将经典网络升级至 VPC 网络,对于用户来说无疑是有诉求和好处,不需要自行迁移或是同时维护两个架构。但,UCloud 一开始低估了这个项目的难度。

徐亮说,“为什么这么难?因为一个默认的前提条件出现了变化——我们以前假设用户的 IP 是唯一的,这不光体现在网络产品上,还包括数据库产品、存储产品等,都会假设用户的 IP 是唯一的——在之前的经典网络时,该前提条件似乎是显而易见的。但在我们要升级到 VPC 2.0 时,我们突然发现这个 IP 变得不再唯一,由于不同的租户网络是完全隔离的,IP 完全是可以重复的。”

因此,这个项目不再是一个纯粹的网络项目,意味着 UCloud 平台上的所有产品都需要升级,都要支持这个重要变化,所以在工程上存在非常多的协调工作。这是一件非常、非常难的事,是一个涉及到全公司协调的能力。

“客户为先”驱动产品创新

VPC 2.0 项目过程中,徐亮团队还推出了许多创新产品,如 IPv6 地址转换。

公有云里面会有一些公共服务,比如说,像镜像服务、DNS 服务等,所有的客户都可能会访问这些公共服务,而有些客户的 IP 是彼此重叠的,只是 VPC 不同而已。传统上都是采用 NAT 的方式去做的,因为地址是相同的,肯定需要通过 NAT 翻译成不同的地址,然后再去访问公共服务。

但是,NAT 方式有两个问题,一个是公共服务在获取原地址时变得很复杂,它要用 TOA 或其它手段才能够提取出原地址。另外是这是一个有状态的网关,可扩展性会存在一定的问题,有状态的部分容易成为瓶颈,维护状态代价很大。

UCloud 在这方面做了一个创新——徐亮的团队把用户的地址和用户的 VPC 这两个部分信息组合起来,形成一个 128 位的 IPv6 地址,把用户的 IPv4 的请求无状态地转换成了 IPv6 请求,然后发送给这些公共服务。徐亮说,“我们这个无状态转换的思路,是非常创新的。对用户来说,得到的性能很好,同时不需要为此额外增加成本。

就这样在“用户为先”的驱动下,UCloud 技术团队一点一点的完成了经典网络到 VPC 网络的升级,历时三年。

“我们的初衷就是从客户为先的角度出发,用我们的技术给客户带去价值,在这个基础上我们就认为这件事情值得就去做。”

自省:关于工程能力的三句话

关于对工程能力的理解,徐亮谈到了几句话: “先于客户发现问题”、 “先扛住再优化”、“这件事情能不能做到24小时止血”、“一周之内能不能拿出一个中期解决方案?”… 让我印象深刻,这也是 UCloud 技术团队的实践路线。

“先于客户发现问题”

据徐亮介绍,其团队做可编程交换机的过程中遇到一个非常隐晦的交换机芯片编译器的 BUG,它会发生哈希冲突,从而导致行为不可预期,但是这个问题在实验室是没办法复现出来。历时一个月时间,UCloud 通过在工程上引入全量测试的环节,提前发现了问题。

这件事情之后,徐亮的团队开发了一个新系统,对所有用户虚机点对点通信的信息进行统计,在做变更时就会针对通信过的场景做全量验证。通过这种方式来发现一些因为软件架构变化导致的问题,能够“先于客户发现问题”,在对客户业务没有产生影响的情况下去解决它。

“先扛住再优化”

发现问题第一时间不是去分析根本原因是什么,而要考虑怎样降低对客户的影响,这就是 UCloud 团队常说的“先扛住再优化”。比如说,智能网卡出现故障了,不会先修复智能网卡,肯定是先把用户的业务切走,让用户的业务正常,然后再想办法解决智能网卡的问题。

“这件事情能不能做 24 小时止血”

一旦发生故障就会做复盘,这时候UCloud技术团队最常说的一句话就是“这件事情能不能做24小时止血”。故障对客户是有影响的,我们要在24小时之内先推出一个方案,这个的方案能够让客户降低损失。不光是出现问题的客户,甚至是其他没有出现问题的客户,我们也要在24小时之内拿出一个方案,让这些问题不会影响到客户。

然后,再问‘一周之内能不能拿出一个中期解决方案?’,最后再考虑长期解决方案,长期方案有的时候就真的很长,比如说体系架构设计的不合理、需要进行重构。

结语

在和包括徐亮在内的多位 UCloud 技术领头人在深入沟通后,深切感受到这是一支极为自信、工程能力极强的技术团队,他们敢于尝试新技术,同时其工程能力能在给用户提高价值的同时,保证不出现问题,我相信这是 UCloud 技术团队自信的底气。

“穿山甲专访”栏目是 Linux 中国社区推出的面向开源界、互联网技术圈的重要领军人物的系列采访,将为大家介绍中国开源领域中一些积极推动开源,谙熟开源思想的技术人,并辨析其思考、挖掘其动因,揭示其背后所发生的事情,为关注开源、有志于开源的企业和技术人标出一条路径。

取名为“穿山甲”寓意有二:取穿山甲挖掘、深入之意来象征技术进步和表征技术领袖的作用;穿山甲是珍稀保护动物,宣传公益。

Awk 是一个强大的工具,可以执行某些可能由其它常见实用程序(包括 sort)来完成的任务。

Awk 是个普遍存在的 Unix 命令,用于扫描和处理包含可预测模式的文本。但是,由于它具有函数功能,因此也可以合理地称之为编程语言。

令人困惑的是,有不止一个 awk。(或者,如果你认为只有一个,那么其它几个就是克隆。)有 awk(由Aho、Weinberger 和 Kernighan 编写的原始程序),然后有 nawkmawk 和 GNU 版本的 gawk。GNU 版本的 awk 是该实用程序的一个高度可移植的自由软件版本,具有几个独特的功能,因此本文是关于 GNU awk 的。

虽然它的正式名称是 gawk,但在 GNU+Linux 系统上,它的别名是 awk,并用作该命令的默认版本。 在其他没有带有 GNU awk 的系统上,你必须先安装它并将其称为 gawk,而不是 awk。本文互换使用术语 awkgawk

awk 既是命令语言又是编程语言,这使其成为一个强大的工具,可以处理原本留给 sortcutuniq 和其他常见实用程序的任务。幸运的是,开源中有很多冗余空间,因此,如果你面临是否使用 awk 的问题,答案可能是肯定的“随便”。

awk 的灵活之美在于,如果你已经确定使用 awk 来完成一项任务,那么无论接下来发生什么,你都可以继续使用 awk。这包括对数据排序而不是按交付给你的顺序的永恒需求。

样本数据集

在探索 awk 的排序方法之前,请生成要使用的样本数据集。保持简单,这样你就不会为极端情况和意想不到的复杂性所困扰。这是本文使用的样本集:

Aptenodytes;forsteri;Miller,JF;1778;Emperor
Pygoscelis;papua;Wagler;1832;Gentoo
Eudyptula;minor;Bonaparte;1867;Little Blue
Spheniscus;demersus;Brisson;1760;African
Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
Torvaldis;linux;Ewing,L;1996;Tux

这是一个很小的数据集,但它提供了多种数据类型:

  • 属名和种名,彼此相关但又是分开的
  • 姓,有时是以逗号开头的首字母缩写
  • 代表日期的整数
  • 任意术语
  • 所有字段均以分号分隔

根据你的教育背景,你可能会认为这是二维数组或表格,或者只是行分隔的数据集合。你如何看待它只是你的问题,而 awk 只认识文本。由你决定告诉 awk 你想如何解析它。

只想排序

如果你只想按特定的可定义字段(例如电子表格中的“单元格”)对文本数据集进行排序,则可以使用 sort 命令

字段和记录

无论输入的格式如何,都必须在其中找到模式才可以专注于对你重要的数据部分。在此示例中,数据由两个因素定界:行和字段。每行都代表一个新的记录,就如你在电子表格或数据库转储中看到的一样。在每一行中,都有用分号(;)分隔的不同的字段(将其视为电子表格中的单元格)。

awk 一次只处理一条记录,因此,当你在构造发给 awk 的这指令时,你可以只关注一行记录。写下你想对一行数据执行的操作,然后在下一行进行测试(无论是心理上还是用 awk 进行测试),然后再进行其它的一些测试。最后,你要对你的 awk 脚本要处理的数据做好假设,以便可以按你要的数据结构提供给你数据。

在这个例子中,很容易看到每个字段都用分号隔开。为简单起见,假设你要按每行的第一字段对列表进行排序。

在进行排序之前,你必须能够让 awk 只关注在每行的第一个字段上,因此这是第一步。终端中 awk 命令的语法为 awk,后跟相关选项,最后是要处理的数据文件。

$ awk --field-separator=";" '{print $1;}' penguins.list
Aptenodytes
Pygoscelis
Eudyptula
Spheniscus
Megadyptes
Eudyptes
Torvaldis

因为字段分隔符是对 Bash shell 具有特殊含义的字符,所以必须将分号括在引号中或在其前面加上反斜杠。此命令仅用于证明你可以专注于特定字段。你可以使用另一个字段的编号尝试相同的命令,以查看数据的另一个“列”的内容:

$ awk --field-separator=";" '{print $3;}' penguins.list
Miller,JF
Wagler
Bonaparte
Brisson
Milne-Edwards
Viellot
Ewing,L

我们尚未进行任何排序,但这是良好的基础。

脚本编程

awk 不仅仅是命令,它是一种具有索引、数组和函数的编程语言。这很重要,因为这意味着你可以获取要排序的字段列表,将列表存储在内存中,进行处理,然后打印结果数据。对于诸如此类的一系列复杂操作,在文本文件中进行操作会更容易,因此请创建一个名为 sort.awk 的新文件并输入以下文本:

#!/bin/gawk -f

BEGIN {
        FS=";";
}

这会将该文件建立为 awk 脚本,该脚本中包含执行的行。

BEGIN 语句是 awk 提供的特殊设置功能,用于只需要执行一次的任务。定义内置变量 FS,它代表 字段分隔符 field separator ,并且与你在 awk 命令中使用 --field-separator 设置的值相同,它只需执行一次,因此它包含在 BEGIN 语句中。

awk 中的数组

你已经知道如何通过使用 $ 符号和字段编号来收集特定字段的值,但是在这种情况下,你需要将其存储在数组中而不是将其打印到终端。这是通过 awk 数组完成的。awk 数组的重要之处在于它包含键和值。 想象一下有关本文的内容;它看起来像这样:author:"seth",title:"How to sort with awk",length:1200。诸如作者、标题和长度之类的元素是键,跟着的内容为值。

在排序的上下文中这样做的好处是,你可以将任何字段分配为键,将任何记录分配为值,然后使用内置的 awk 函数 asorti()(按索引排序)按键进行排序。现在,随便假设你想按第二个字段排序。

没有被特殊关键字 BEGINEND 引起来的 awk 语句是在每个记录都要执行的循环。这是脚本的一部分,该脚本扫描数据中的模式并进行相应的处理。每次 awk 将注意力转移到一条记录上时,都会执行 {} 中的语句(除非以 BEGINEND 开头)。

要将键和值添加到数组,请创建一个包含数组的变量(在本示例脚本中,我将其称为 ARRAY,虽然不是很原汁原味,但很清楚),然后在方括号中分配给它键,用等号(=)连接值。

{   # dump each field into an array
    ARRAY[$2] = $R;
}

在此语句中,第二个字段的内容($2)用作关键字,而当前记录($R)用作值。

asorti() 函数

除了数组之外,awk 还具有一些基本函数,你可以将它们用作常见任务的快速简便的解决方案。GNU awk中引入的函数之一 asorti() 提供了按键(索引)或值对数组进行排序的功能。

你只能在对数组进行填充后对其进行排序,这意味着此操作不能对每个新记录都触发,而只能在脚本的最后阶段进行。为此,awk 提供了特殊的 END 关键字。与 BEGIN 相反,END 语句仅在扫描了所有记录之后才触发一次。

将这些添加到你的脚本:

END {
    asorti(ARRAY,SARRAY);
    # get length
    j = length(SARRAY);
   
    for (i = 1; i &lt;= j; i++) {
        printf("%s %s\n", SARRAY[i],ARRAY[SARRAY[i]])
    }
}

asorti() 函数获取 ARRAY 的内容,按索引对其进行排序,然后将结果放入名为 SARRAY 的新数组(我在本文中发明的任意名称,表示“排序的 ARRAY”)。

接下来,将变量 j(另一个任意名称)分配给 length() 函数的结果,该函数计算 SARRAY 中的项数。

最后,使用 for 循环使用 printf() 函数遍历 SARRAY 中的每一项,以打印每个键,然后在 ARRAY 中打印该键的相应值。

运行该脚本

要运行你的 awk 脚本,先使其可执行:

$ chmod +x sorter.awk

然后针对 penguin.list 示例数据运行它:

$ ./sorter.awk penguins.list
antipodes Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
chrysocome Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
demersus Spheniscus;demersus;Brisson;1760;African
forsteri Aptenodytes;forsteri;Miller,JF;1778;Emperor
linux Torvaldis;linux;Ewing,L;1996;Tux
minor Eudyptula;minor;Bonaparte;1867;Little Blue
papua Pygoscelis;papua;Wagler;1832;Gentoo

如你所见,数据按第二个字段排序。

这有点限制。最好可以在运行时灵活选择要用作排序键的字段,以便可以在任何数据集上使用此脚本并获得有意义的结果。

添加命令选项

你可以通过在脚本中使用字面值 var 将命令变量添加到 awk 脚本中。更改脚本,以使迭代子句在创建数组时使用 var

{ # dump each field into an array
    ARRAY[$var] = $R;
}

尝试运行该脚本,以便在执行脚本时使用 -v var 选项将其按第三字段排序:

$ ./sorter.awk -v var=3 penguins.list
Bonaparte Eudyptula;minor;Bonaparte;1867;Little Blue
Brisson Spheniscus;demersus;Brisson;1760;African
Ewing,L Torvaldis;linux;Ewing,L;1996;Tux
Miller,JF Aptenodytes;forsteri;Miller,JF;1778;Emperor
Milne-Edwards Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyed
Viellot Eudyptes;chrysocome;Viellot;1816;Sothern Rockhopper
Wagler Pygoscelis;papua;Wagler;1832;Gentoo

修正

本文演示了如何在纯 GNU awk 中对数据进行排序。你可以对脚本进行改进,以便对你有用,花一些时间在gawk 的手册页上研究 awk 函数并自定义脚本以获得更好的输出。

这是到目前为止的完整脚本:

#!/usr/bin/awk -f
# GPLv3 appears here
# usage: ./sorter.awk -v var=NUM FILE

BEGIN { FS=";"; }

{ # dump each field into an array
    ARRAY[$var] = $R;
}

END {
    asorti(ARRAY,SARRAY);
    # get length
    j = length(SARRAY);
   
    for (i = 1; i &lt;= j; i++) {
        printf("%s %s\n", SARRAY[i],ARRAY[SARRAY[i]])
    }
}

via: https://opensource.com/article/19/11/how-sort-awk

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

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

2019 年 11 月初,数字天堂(北京)网络技术有限公司(下称:数字天堂)诉柚子(北京)科技有限公司、柚子(北京)移动技术有限公司(下称:两柚子)侵犯计算机软件著作权纠纷案,由北京高级人民法院二审作出终审判决。笔者曾密切关注该案,终审判决生效前,囿于关联代理关系的利益冲突,不便多谈。现将本案相关若干问题梳理成文,愿与各位探讨之。

本案之所以受关注,是因为本次计算机软件著作权侵权案涉及开源软件和 GPL 许可证,本案的判决对未来开源软件诉讼实践有重要意义。本案一审法院对 GPL 相关条款作了阐述,二审法院回避了 GPL 问题。本文,笔者基于本案事实和法院判决做些思考,分享给大家讨论。本文将仅对涉及开源软件及 GPL 许可证的内容进行论述,其他法律问题不作探讨。

案情简介

为节省篇幅,以下对案情进行摘要和总结,详细案情可见一审链接二审链接

经过一审和二审对事实的调查和确认,两柚子认可:

  1. 数字天堂是 HBuilder 软件的著作权人;
  2. 数字天堂拥有 HBuilder 软件中的代码输入法功能插件、真机运行功能插件、边改边看功能插件源代码著作权;
  3. 两柚子的 APICloud 软件中对应插件源代码部分与涉案的三个插件具有同一性。

基于此,针对本著作权侵权控诉,两柚子抗辩理由只有 GPL 开源许可协议这一个突破口。而二审中对涉案代码量、侵权产品数量的认定,以及基于此对赔偿额的认定,只是量的维度问题。

开源许可证是法律文件

一审法院虽未明确 GPL 许可证的法律效力,但在论述涉案三个插件是否受 GPL 协议限制时,默认了 GPL 许可证具有法律约束力。这一点虽然是意料之中,但毕竟开源理念和大部分开源协议来源于国外,且应用于开源社区特定人群,这一认定给未来涉及开源软件的诉讼消除了部分不确定性。为了强调协议内容,下文涉及 GPL 许可证的除特指许可证本身外,都以 GPL 协议指代。

法院虽然默认了 GPL 协议具有约束力,即类似于协议或合同的法律效果,但并未进一步将 GPL 协议条款基于我国著作权法进行解释。社区内关于 GPL 协议的解释,特别是关于 GPL 传染性的解释是基于美国版权法,其能否为国内法院认可,依然存在不确定性。

随着开源理念的深入以及开源软件在世界范围内的普及,本案作为中国 GPL 第一案,对未来开源软件相关的诉讼意义重大。稍有遗憾的是,两审法院并未就开源软件以及 GPL 协议的关键问题进行阐述。当然,也不可能期待 GPL 问题通过一次诉讼案件完全解决,未来还需要更多的法律、学术、技术等人士贡献智慧。

关于一审认定的思考

既然法院确认了 GPL 协议的法律约束力,那么对 GPL 协议的解释要么采取社区通说解释,要么基于我国著作权法作独立解释。否则很容易出现矛盾或模糊,以至于增加企业开源实践中的法律不确定性。

关于 GPL 协议约束力范围(传染性),一审法院以涉案的三个插件可以独立运行,分别存放在三个独立的文件夹中且三个独立文件夹中无 GPL 许可证,据此认为涉案三个插件不属于 GPL 协议中所指应被开源的衍生产品或修改版本

GPL 许可证中有关的原文如下:

The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".)——GPL 3.0

To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

A “covered work” means either the unmodified Program or a work based on the Program.——GPL 2.0

结合 GPL 协议条款和 GNU 官网对于 GPL 传染性的解释,一审法院这一认定是值得商榷的,就像你不能认为总部在西城的 A 公司与其在昌平拥有独立办公场所的分公司 B 是完全独立的,这需要从 A 和 B 之间的财务、人事、业务等是否独立为基础判断。

同理,GPL 模块 A 与模块 B 之间是否独立,绝对不能以A和B是否位于独立的文件夹中来判断,还是需要从 A 和 B 之间的功能关系、通信关系、调用关系、依赖关系等来判断。

插件相对于主程序是否独立,需要看:

  1. 插件的使命,是否为该主程序而存在;
  2. 插件与主程序的交互方式,如管道、队列、函数调用;
  3. 交换消息的密切性 intimate communication
  4. 是否有例外声明等。

一审法院在确定赔偿额的时候又一次指出三个涉案插件是三个独立软件作品。一审法院从审判认定到赔偿衡量是一致的。这里,两柚子并没有就涉案三个插件的独立性进行抗辩,而这一点对 GPL 是否构成传染非常关键,在一审中被告代理律师对 GPL 许可证条款的理解也存在局限。

关于二审认定的思考

首先,二审中两柚子再次提出司法鉴定来否定涉案三个插件独立性,可能是准备利用GPL传染性中关于独立作品的认定来抗辩。不过,法院认为二审诉讼中再次提出第三次鉴定申请,有违司法程序公正和司法程序效率,即二审法院基于程序公正的角度考量不予准许。同时,二审法院认为两柚子提出的新的司法鉴定申请内容与本案待证事实之间无直接关联性,这一点是值得商榷的,因为 GPL 中作品是否独立直接影响作品是否受GPL约束,进而直接影响本案关于侵权的认定。二审法院的否决理由,直接回避了可能对 GPL 传染性问题的讨论。

其次,二审法院认定数字天堂现有证据不足以证明涉案三个插件可以独立于 HBuilder 开发工具软件中的其他程序独立运行,但针对不独立的插件却没有进一步讨论这种不独立是否能够进行 GPL 开源抗辩。也就是说,一审法院基于作品的独立认定 GPL 抗辩无效,二审法院采取了一审对 GPL 抗辩的意见,但却否定了作品的独立性这一前提。

是否为独立作品的认定直接决定作品是否属于 衍生作品 derivative work 修改作品 modified version ,进而直接影响是否受 GPL 约束。

当然,我们可以这样解读二审法院对于作品独立的认定,即 GPL 许可证里所说的作品的独立性,和一审、二审法院判决赔偿额对插件独立的认定是不同维度/层次,这是说的通的。但仔细阅读一审判决可知,法院在否决 GPL 抗辩中和赔偿额判定中对独立的认定是一致的,二审认可了一审对 GPL 抗辩部分的认定,却否决了赔偿额中对独立作品的认定,本身前后有矛盾嫌疑

笔者认为,如果按照上述:GPL 传染性中独立性判定和对侵权作品数量中独立性判定是不同维度的独立性判定的假设,至少二审中需要重新认定 GPL 传染性的问题,从而将两个维度的独立性认定区分开来。

关于两柚子诉求理由的思考

两柚子在二审中再次申请司法鉴定:

  1. 涉案三个插件是否可以脱离 Eclipse 主体软件在 Windows 环境中独立运行;
  2. 将涉案三个插件源代码编译为插件以验证插件能否在 Eclipse 主体软件中独立运行;
  3. 任意删除 Hbuilder 软件目录下的一个或多个以“org.eclipse”、“org.apache”、“com.aptana”为前缀的文件或目录 JAR 文件以验证涉案三个插件能否正常运行……

关于这三个补充鉴定事项,首先,笔者认为两柚子或者其律师在开源上做足了工作,但其中依然存在问题。首先,插件独立于 Eclipse 主程序,并不一定需要插件可以脱离 Eclipse 主程序在 Windows 中独立运行。插件的独立性在于:插件有明确的功能,可用于特定的主程序,但不依赖于特定的主程序。最后,主程序脱离插件,应当且必须可以独立运行,并且不损失主程序本身的所有功能。

再看诉讼本身

基于以上认识,我们再回头看看案件本身。首先说明,因本案需要进行多处技术鉴定,笔者无法也没有精力一一取证,仅仅基于几个假设,再捋一下 GPL 相关的问题。笔者认为,关于本案 GPL 传染性的认定需要从 3 个方面来看:

  1. Eclipse 主程本身;
  2. 基于 Eclipse 主程序的 GPL 插件;
  3. 涉案插件与主程序,以及涉案插件与上述 GPL 插件的关系。

为方便读者理解,引用数字天堂代理律所对一审结果评述的一张图。

(1)从 Eclipse 主程序看

APICloud 和 HBuilder 都是基于主程序 Eclipse 平台,包含第三方开源插件+各自自研插件组成的集成开发环境 IDE。

首先,主程序 Eclipse 是采用 EPL(Eclipse Public License)许可证公开,EPL 与 GPL 不兼容。即便是 2017 年 8 月发布的 EPL-2.0 版本虽然添加了次级许可证选项,但其与 GPL 依然是不兼容的。因此,HBuilder 作为下游产品,其对 Eclipse 的包装分发不能变更 Eclipse 许可证

其次,针对插件来说,无非是拓展 Eclipse 某一特定的功能,任何非 Eclipse 本身的第三方插件,可以说对于 Eclipse 主程序来说都是非必须的。其第三方公司开发的 Eclipse 主程序的插件,按照 EPL 传染性的规定,一般不视为 EPL 的衍生作品,不受 EPL 约束。

最后,需要强调的是 EPL 虽然是弱 Copyleft 许可证,但其依然是类似于 GPL 的具有“传染性”的许可证,其在给予用户更大使用方便的同时,对自身软件的 Copyleft 保护依然很强。因此,下游软件开发者在处理 EPL 软件和 GPL 软件时,需要认真对待它们的兼容性问题。

(2)从 Aptana 插件看

Aptana 在 2006 年推出时,以 EPL 1.0 发布,并于 2017 年 9 月 21 日修改为 GPL3.0 和 APL(Aptana Public License )双许可。APL 不是开源/自由软件许可证,可以认为是商业许可,但对于非分发的内部使用是免费的。

Aptana 作为主程序 Eclipse 的插件,由于 EPL 和 GPL 不兼容,Aptana 中的 GPL 插件要和以 EPL 许可的 Eclipse 主程序连接,必须在 GPL 许可证里作例外声明。毫无疑问,笔者在 Aptana 官网找到了例外声明,即关于独立作品的 GPL 传染性例外声明,以及聚合程序的 GPL 传染性例外声明。部分内容如下:

GPL Section 7 Exception

……which are conveyed to you by Appcelerator, Inc. and licensed under one or more of the licenses identified in the Excepted License List below (each an "Excepted License"), as long as:

  1. you obey the GPL in all respects for the Program and the modified version, except for Excepted Works which are identifiable sections of the modified version, which are not derived from the Program, and which can reasonably be considered independent and separate works in themselves,
  2. all Excepted Works which are identifiable sections of the modified version, which are not derived from the Program, and which can reasonably be considered independent and separate works in themselves,
  3. are distributed subject to the Excepted License under which they were originally licensed, and
  4. are not themselves modified from the form in which they are conveyed to you by Appcelerator, and
  5. the object code or executable form of those sections are accompanied by the complete corresponding machine-readable source code for those sections, on the same medium as the corresponding object code or executable forms of those sections, and are licensed under the applicable Excepted License as the corresponding object code or executable forms of those sections, and
  6. any works which are aggregated with the Program, or with a modified version on a volume of a storage or distribution medium in accordance with the GPL, are aggregates (as defined in Section 5 of the GPL) which can reasonably be considered independent and separate works in themselves and which are not modified versions of either the Program, a modified version, or an Excepted Work.

If the above conditions are not met, then the Program may only be copied, modified, distributed or used under the terms and conditions of the GPL or another valid licensing option from Appcelerator, Inc. Terms used but not defined in the foregoing paragraph have the meanings given in the GPL

从以上 GPL 例外中可以看出,Aptana 只是部分限定了“衍生作品”解释,也就是运行采用 GPL 许可证的 Aptana 与像 Eclipse 这样独立的程序交互不会发生传染,仅此而已。而如果修改 Aptana,将其他程序并入 Aptana Studio,或者将 Aptana 与其他程序进行整合的作品依然受到 GPL 协议约束。简单的说,加入 GPL 例外的 GPL 程序依然是 GPL 程序,这一点必须强调。关于这一点,Aptana 官网还专门有问题解答

Can I add EPL'd plugins to Aptana Studio package and redistribute it?
No. You can only redistribute the unmodified binary versions of the EPL'd plugins that are part of Aptana Studio when distributing any of the GPL'd code. Adding any files to Aptana Studio package creates a derivative work, and since all derivative works need to be made GPL'd, you will not be able to add EPL'd (or any other license) plugins without contacting us for a commercial license.

What if I want to make changes to some of Aptana Studio's EPL'd plugins?
You are free to make changes to any of Aptana Studio EPL'd code under the terms of the EPL. To get those redistributed as part of Aptana Studio, we encourage you to contribute those back to Aptana so that we may evaluate your changes for inclusion back into the product.

Can I take unmodified Aptana Studio binaries and combine them with an Eclipse distribution?
No. Combining our GPL'd licensed code with any other product requires that the entire product be GPL'd, and therefore you cannot include any Eclipse distribution.

数字天堂认为,其 HBuilder 是包含 Eclipse 平台框架和众多插件的聚合体软件包,但其基于 Eclipse 开发且打包了 Aptana 中的 GPL 插件。从 GPL 协议对独立程序和聚合程序的规定来看,HBuilder 不被感染很难成立。一旦这种潜在传染可能性成立,数字天堂的 HBuilder 发行版就不满足合规性,其内部 EPL 和 GPL 软件不兼容。直白的说,就是整个发行版都可能受到 GPL 的约束。同时,这对于 Eclipse 是不可接受的,哪怕 EPL 是弱 Copyleft 许可证。这些问题,多是对 EPL 和 GPL 的 Copyleft 性质认识不到位导致。

(3)涉案插件与主程序及 Aptana 插件的关系

其实,以上两步分析一旦得出受 GPL 约束的结论,就不需要下面的分析了。为了完整,同时供未来类似案例参考,简单介绍。

进一步分析 Aptana 与数字天堂的涉案三个插件之间的关系,若涉案三个插件与 Aptana 有调用、通信、依赖关系,那么涉案三个插件必然会被 GPL 传染,也即是受 GPL 约束。

从以上三步的分析可见,在 GPL 传染性判断时,是否为独立作品是非常关键的。这也是我在前面法院判决部分要强调一审法院对独立的认定虽未必符合 GPL 本身解释,但至少前后一致。而二审法院对作品独立的认定甚至前后矛盾。

当然,笔者没太多精力去调查技术细节,点到为止,有兴趣的读者可以进行深入研究。

以上分析,是基于 Eclipse 作为中立主程序(即 Eclipse 主程序著作权人非诉讼参与人),GPL 插件与非 GPL 插件判定的情况,换一种场景以上判断完全或者大部分不成立。关于开源软件和 GPL 的问题还有很多需要注意的,限于篇幅不再进一步说明,对本案或对开源感兴趣的朋友可以找我单独讨论。

付钦伟,集慧智佳高级咨询师、专利代理人,擅长专利分析布局、FTO 调查与风险应对、专利信息应用、开源软件风险与合规指导。

一些简单的 Linux 命令能让你根据需要分割以及重新组合文件,来适应存储或电子邮件附件大小的限制。

Marco Verch

Linux 系统提供了一个非常易于使用的命令来分割文件。在将文件上传到限制大小的存储网站或者作为邮件附件之前,你可能需要执行此操作。要将文件分割为多个文件块,只需使用 split 命令。

$ split bigfile

默认情况下,split 命令使用非常简单的命名方案。文件块将被命名为 xaaxabxac 等,并且,大概地,如果你将足够大的文件分割,你甚至可能会得到名为 xzaxzz 的块。

除非你要求,否则该命令将无任何反馈地运行。但是,如果你想在创建文件块时看到反馈,可以使用 --verbose 选项。

$ split –-verbose bigfile
creating file 'xaa'
creating file 'xab'
creating file 'xac'

你还可以给文件命名前缀。例如,要将你原始文件分割并命名为 bigfile.aabigfile.ab 等,你可以将前缀添加到 split 命令的末尾,如下所示:

$ split –-verbose bigfile bigfile.
creating file 'bigfile.aa'
creating file 'bigfile.ab'
creating file 'bigfile.ac'

请注意,上述命令中显示的前缀的末尾会添加一个点。否则,文件将是 bigfileaa 之类的名称,而不是 bigfile.aa

请注意,split 命令不会删除你的原始文件,只是创建了文件块。如果要指定文件块的大小,可以使用 -b 选项将其添加到命令中。例如:

$ split -b100M bigfile

文件大小可以是 KB、MB,GB,最大可以是 YB!只需使 K、M、G、T、P、E、Z 和 Y 这些合适的字母。

如果要基于每个块中的行数而不是字节数来拆分文件,那么可以使用 -l(行)选项。在此示例中,每个文件将有 1000 行,当然,最后一个文件可能有较少的行。

$ split --verbose -l1000 logfile log.
creating file 'log.aa'
creating file 'log.ab'
creating file 'log.ac'
creating file 'log.ad'
creating file 'log.ae'
creating file 'log.af'
creating file 'log.ag'
creating file 'log.ah'
creating file 'log.ai'
creating file 'log.aj'

如果你需要在远程站点上重新组合文件,那么可以使用如下所示的 cat 命令轻松地完成此操作:

$ cat x?? > original.file
$ cat log.?? > original.file

上面所示的分割和组合命令适合于二进制和文本文件。在此示例中,我们将 zip 二进制文件分割为 50KB 的块,之后使用 cat 重新组合了它们,然后比较了组合后的文件和原始文件。diff 命令验证文件是否相同。

$ split --verbose -b50K zip zip.
creating file 'zip.aa'
creating file 'zip.ab'
creating file 'zip.ac'
creating file 'zip.ad'
creating file 'zip.ae'
$ cat zip.a? > zip.new
$ diff zip zip.new
$                    <== 无输出 = 无差别

我唯一要提醒的一点的是,如果你经常使用 split 并使用默认命名,那么某些文件块可能会覆盖其他的文件块,甚至会比你预期的更多,因为有些是更早之前分割的。


via: https://www.networkworld.com/article/3489256/breaking-linux-files-into-pieces-with-the-split-command.html

作者:Sandra Henry-Stocker 选题:lujun9972 译者:geekpi 校对:wxy

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