mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-26 21:30:55 +08:00
Merge branch 'master' of https://github.com/LCTT/TranslateProject into translating
This commit is contained in:
commit
ff8e1c97b3
@ -0,0 +1,104 @@
|
||||
[#]: subject: "5 open source alternatives to 微软 Exchange"
|
||||
[#]: via: "https://opensource.com/article/21/11/open-source-alternatives-微软-exchange"
|
||||
[#]: author: "Heike Jurzik https://opensource.com/users/hej"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "XiaotingHuang22"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-15705-1.html"
|
||||
|
||||
可以替代微软 Exchange 的 5 个开源软件
|
||||
======
|
||||
|
||||
> 不再将就于微软 Exchange 这一专有软件,试一试这些基于 Linux 系统的电子邮件和群件服务吧。
|
||||
|
||||
![][0]
|
||||
|
||||
几十年来,微软 Exchange 一直统治着电子邮件和群件服务市场。作为领头羊,它主宰着企业界,无处不在的 Outlook 邮件客户端已成为群件的事实标准。由于 Exchange 与微软的 Office 产品紧密联系,无论是桌面客户端还是移动客户端,微软用户都可以轻松使用各种生产力软件和功能。
|
||||
|
||||
然而,许多公司对于将数据存储在微软的云中也心存疑虑。在本文中,我将介绍一些开源替代产品及其优势。这不仅与如何不再受供应商控制和降低成本有关,更关乎使用具有开放标准和不同安全级别的软件 —— 用于组件服务器本身及其背后的操作系统。
|
||||
|
||||
本文中介绍的这五个替代产品都是基于 Linux 的。 虽然 grommunio、Kopano、Nextcloud、ownCloud 和 OX App Suite 在功能上差异很大,吸引到的企业类型各不相同,但它们都提供免费版本,并可选择购买付费支持服务和附加组件。所有产品都可以在本地或云端运行。最重要的是,所有供应商都为其软件提供 SaaS(<ruby>软件即服务<rt>Software as a Service</rt></ruby>)解决方案。
|
||||
|
||||
### grommunio
|
||||
|
||||
[grommunio][2],以前被称为 grammm,在 AGPLv3 许可下发布的。它由奥地利同名公司开发和支持。与 Exchange 不同,grommunio 提供符合标准的邮件服务器,以及功能齐全的群件解决方案,具有电子邮件、联系人、日历、任务、文件共享等功能。grommunio 适用于各种开源和专有邮件客户端,如 Windows Mail、Outlook、Android、Apple Mail/iOS、Thunderbird 等,并支持旧的 RPC over HTTP 协议和 Outlook 标准协议 MAPI over HTTP。除此之外还包含:用于移动设备的 Exchange ActiveSync 和各种标准协议,如 CalDAV(日历)、CardDAV(地址簿)、IMAP、POP3、SMTP 和 LDAP,以及活动目录(用于同步用户帐户)。
|
||||
|
||||
外部对接的开源应用程序还提供了一些微软的 API 或协议不支持的功能。例如,开发人员合并了 [Jitsi][3](视频和音频电话软件)、[Mattermost][4](聊天软件)以及文件共享和同步([ownCloud][5])。除此之外,grommunio 还配备了基本的移动设备管理软件(MDM)。
|
||||
|
||||
grommunio 的设计面向各种不同的用户,并且与 Exchange 一样,它支持数据库分片(数据库在多个主机之间的水平分布)。灵活的存储后端允许管理员通过添加其他服务器或云帐户来扩展他们的设置。grommunio 仅将 MySQL 数据库用于元数据,而所有“内容”(例如邮件和群件对象)都存储在每个用户的 SQLite 数据库中。有关底层架构的更多信息,请查看 [该制造商的网站][6]。
|
||||
|
||||
其社区版是免费的,其中包括所有的 grommunio 功能并支持多达五个用户帐户。
|
||||
|
||||
### Kopano
|
||||
|
||||
来自德国和荷兰的软件制造商 Kopano 出品的 [Kopano][7],也采用 AGPLv3 许可,基于 Zarafa 软件堆栈。与其前身不同,Kopano 的目标不只是成为 Exchange 的替代品。相反,它提供一个完整的群件解决方案,除了电子邮件、联系人、日历、任务、笔记和文档编辑这些标准功能外,它还包括实时通信。Kopano 可以与 [许多其他平台][8]、应用程序和服务交互,其中一些通过插件就能轻松实现。对于视频会议,Kopano 团队基于 WebRTC 开发了自己的开源解决方案:Kopano Meet 提供端到端加密,在 Windows、macOS、Linux、Android 和 iOS 客户端都适用。
|
||||
|
||||
Outlook 客户端通过 ActiveSync(Z-Push 库)或 [Kopano OL Extension][9](KOE)来同步移动数据,KOE 是已经包含了 ActiveSync 的加强版。Kopano 提供本机 Web 客户端(WebApp)、移动设备客户端(Mobility)以及支持 Windows、Linux 和 macOS 的桌面版本(DeskApp)。它可以通过 IMAP、CalDAV 和 CardDAV 连接其他客户端。所有直接连接到 Kopano 服务器的应用程序都使用 SOAP(<ruby>简单对象访问协议<rt>Simple Object Access Protocol</rt></ruby>)中的 MAPI。
|
||||
|
||||
Kopano Groupware 和 Kopano ONE(Kopano Groupware 的特别版)都提供免费的社区版本。 Kopano Meet 还可以作为应用程序或容器下载。
|
||||
|
||||
### Nextcloud
|
||||
|
||||
[Nextcloud][10] 在斯图加特和柏林(德国)都有办事处,采用 AGPLv3 许可。与 ownCloud 或 Dropbox 一样,用户可以通过桌面(Windows、Linux 和 macOS)、网络浏览器或本地应用程序(Android 和 iOS)访问该软件套件。从 18 版本开始,Nextcloud 除了拥有 Nextcloud Files(文件同步和共享)还包括了 Nextcloud Talk(通话、聊天和网络会议)和 Nextcloud Groupware(日历、联系人和邮件),并更名为 Nextcloud Hub。
|
||||
|
||||
用户和群组管理通过 OpenID 或 LDAP 进行。Nextcloud 支持各种存储后端,例如 FTP、S3 和 Dropbox。Nextcloud 可与多种数据库管理系统配合使用,包括 PostgreSQL、MariaDB、SQLite 和 Oracle 数据库。管理员可以通过 [Nextcloud 应用程序商店][11] 中的 200 多个应用程序扩展功能,其中包括实时通信、音频和视频聊天、任务管理、邮件等等。
|
||||
|
||||
Nextcloud 是完全免费的。最重要的是,该公司提供了 Nextcloud Enterprise 版本(针对企业部署进行了预配置、优化和强化)
|
||||
|
||||
### ownCloud
|
||||
|
||||
[ownCloud][12] 是由位于德国纽伦堡的 ownCloud GmbH 公司开发和维护的具有文件同步、共享和内容协作功能的软件。它的客户端-服务器软件的核心和许多社区应用程序都是在 AGPLv3 下发布的。一些扩展功能的企业应用程序以 ownCloud 商业许可证(OCL)的形式授权。
|
||||
|
||||
ownCloud 主要是一款内容协作软件,包括在线办公文档编辑、日历、联系人同步等功能。移动客户端支持 Android 和 iOS,桌面应用可以和 Windows、macOS 和 Linux 的原生文件管理器结合使用。它允许访问 Web 界面,无需安装专用客户端软件。ownCloud 支持 WebDAV、CalDAV 和 CardDAV 协议。LDAP 协议也包含其中,但 ownCloud 也可以连接到支持 OpenID Connect 身份验证标准的其他身份提供者。
|
||||
|
||||
ownCloud 可以整合微软 Office Online Server、Office 365 和微软 Teams,同时为微软 Outlook 和 eM 客户端提供可用插件。如有必要,外部存储功能可连接到不同的存储提供商,例如 Amazon S3、Dropbox、微软 SharePoint、Google Drive、Windows 网络驱动器(SMB)和 FTP。该供应商还为企业客户提供额外的功能,如端到端加密、勒索软件和防病毒保护等(请参阅 [完整功能列表][13])。
|
||||
|
||||
社区版免费且 100% 开源。
|
||||
|
||||
### OX App Suite
|
||||
|
||||
[Open-Xchange][14] 成立于 2005 年,总部位于德国奥尔佩和纽伦堡。今天,OX 在多个欧洲国家、美国和日本设有办事处。[OX App Suite][15] 是一个模块化的电子邮件、通信和协作平台,主要为电信公司、托管公司和其他提供基于云的服务的提供商而设计。
|
||||
|
||||
OX 后端在 GPLv2 协议下发布,前端(UI)在 AGPLv3 下发布。用户可以通过他们喜欢的浏览器(完全个性化的门户)或移动应用程序(Android 和 iOS)访问应用程序套件。或者,原生客户端(移动设备和台式机)也可用于 OX Mail 和 OX Drive。得益于 CardDAV 和 CalDAV 扩展、Exchange Active Sync 和适用于 Android 的 OX Sync App,联系人、日历和任务得以同步。
|
||||
|
||||
OX App Suite 包含用于电子邮件、联系人、日历和任务的应用程序。 还有其他工具和扩展可用,其中一些是开源的,一些功能则要付费,包括 OX Documents(文本文档、电子表格、演示文稿)、OX Drive(管理、共享和同步文件)、OX Guard(电子邮件和文件加密)等等。如需完整列表,请访问 OX 网站的 [一般条款和条件][16]。
|
||||
|
||||
该应用免费提供有限功能的社区版。
|
||||
|
||||
### 开源电子邮件和群件
|
||||
|
||||
电子邮件和群件服务并不是必须花(很多)钱才可获得,当然也没有必要满足于在别人的服务器上托管的专有解决方案。如果你不太热衷于管理职责,那么上述的这五个 Exchange 开源替代品都可以作为 SaaS 解决方案使用。另外,所有供应商都提供专业技术支持,你可以在本地运行软件,一切尽在你的掌握中,但你却不会感觉自己孤军无援。
|
||||
|
||||
(题图由 MJ 生成:Mail Cooperation Groupware Office Open Source hyper realistic, hyper detailed, intricate detail, beautiful lighting, very detailed,Illustration)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/11/open-source-alternatives-微软-exchange
|
||||
|
||||
作者:[Heike Jurzik][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[XiaotingHuang22](https://github.com/XiaotingHuang22)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/hej
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/team_dev_email_chat_video_work_wfm_desk_520.png?itok=6YtME4Hj (Working on a team, busy worklife)
|
||||
[2]: https://grommunio.com/
|
||||
[3]: https://opensource.com/article/20/5/open-source-video-conferencing
|
||||
[4]: https://opensource.com/article/20/7/mattermost
|
||||
[5]: https://opensource.com/article/21/7/owncloud-windows-files
|
||||
[6]: https://grommunio.com/features/architecture/
|
||||
[7]: https://kopano.com/
|
||||
[8]: https://kopano.com/products/interoperability/
|
||||
[9]: https://kb.kopano.io/display/WIKI/Setting+up+the+Kopano+OL+Extension
|
||||
[10]: https://nextcloud.com/
|
||||
[11]: https://apps.nextcloud.com/
|
||||
[12]: https://owncloud.com/
|
||||
[13]: https://owncloud.com/features/
|
||||
[14]: https://www.open-xchange.com/
|
||||
[15]: https://www.open-xchange.com/products/ox-app-suite/
|
||||
[16]: https://www.open-xchange.com/terms-and-conditions/
|
||||
[0]: https://img.linux.net.cn/data/attachment/album/202304/09/114919i7cu0zwk4e663x0c.jpg
|
@ -3,47 +3,43 @@
|
||||
[#]: author: "Seth Kenlon https://opensource.com/users/seth"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "FYJNEVERFOLLOWS"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-15700-1.html"
|
||||
|
||||
使用开源思维导图 Draw.io
|
||||
======
|
||||
|
||||
下次你需要头脑风暴、组织想法或计划项目时,不妨试试 Draw.io
|
||||
![Looking at a map for career journey][1]
|
||||
> 下次你需要头脑风暴、组织想法或计划项目时,不妨试试 Draw.io。
|
||||
|
||||
地图有一些特别之处。我记得小时候打开托尔金《*霍比特人*》的封面,盯着手绘的中土世界地图,感受简单的图画中蕴含着丰富的可能性。除了描述事物与其他事物之间的关系这一明显目的外,我认为地图在表达可能性方面做得很好。你可以到户外去,沿着这条路或那条路走,如果你这样做了,去想想你将能够看到的所有新的、令人兴奋的事物。
|
||||
![][0]
|
||||
|
||||
尽管如此,地图并不一定是有价值和充满可能性的文字。有些地图描述了一个思维过程、计划、算法,甚至是一些随机的想法,这些想法拼命地想要组合成一件潜在的艺术作品。称之为“思维导图”、“流程图”或“创意板”。可以用开源的 [Draw.io][2] 应用程序去很容易地制作。
|
||||
地图有一些特别之处。我记得小时候打开托尔金《霍比特人》的封面,盯着手绘的中土世界地图,感受简单的图画中蕴含着丰富的可能性。除了描述事物与其他事物之间的关系这一明显目的外,我认为地图在表达可能性方面做得很好。你可以到户外去,沿着这条路或那条路走,如果你这样做了,去想想你将能够看到的所有新的、令人兴奋的事物。
|
||||
|
||||
尽管如此,地图并不一定是有价值和充满可能性的文字。有些地图描述了一个思维过程、计划、算法,甚至是一些随机的想法,这些想法拼命地想要组合成一件潜在的艺术作品,它们被称之为“思维导图”、“流程图”或“创意板”。可以用开源的 [Draw.io][2] 应用程序去很容易地制作。
|
||||
|
||||
### 安装 Draw.io
|
||||
|
||||
Draw.io 被设计为一个开源的在线应用程序,因此你可以将其作为在线应用程序使用,下载[桌面版本][3],或[克隆 Git 存储库][4]并将其托管在您自己的服务器上。
|
||||
Draw.io 是一个开源的在线应用程序,因此你可以将其作为在线应用程序使用,下载 [桌面版本][3],或 [克隆 Git 存储库][4] 并将其托管在你自己的服务器上。
|
||||
|
||||
### 使用 Draw.io 进行映射
|
||||
当你第一次启动 Draw.io 时,你需要选择保存数据的位置。如果你自己托管 Draw.io,你的选择取决于你可以访问哪些 API 密钥。你可以从几个在线存储服务中为在线公共实例进行选择,这取决于你的帐户。如果你不想把你的数据存储在别人的服务器上,你也可以选择把你的工作保存在本地存储上。如果你还不确定,可以单击 ``稍后决定`` 继续进入应用程序,而无需选择任何内容。
|
||||
### 使用 Draw.io 画图
|
||||
|
||||
当你第一次启动 Draw.io 时,你需要选择保存数据的位置。如果你自己托管 Draw.io,你的选择取决于你可以访问哪些 API 密钥。你可以从几个在线存储服务中为在线公共实例进行选择,这取决于你的帐户。如果你不想把你的数据存储在别人的服务器上,你也可以选择把你的工作保存在本地存储上。如果你还不确定,可以单击 “<ruby>稍后决定<rt>Decide later</rt></ruby>” 继续进入应用程序,而无需选择任何内容。
|
||||
|
||||
Draw.io 的交互界面中间有一个很大的工作空间,左边是主工具栏,顶部是工具栏,右边是属性面板。
|
||||
|
||||
![Draw.io interface][5]
|
||||
|
||||
(Seth Kenlon, [CC BY-SA 4.0][6])
|
||||
|
||||
工作流程很简单:
|
||||
|
||||
1. 从左侧工具栏中选择一个形状。
|
||||
2. 在工作空间中编辑形状。
|
||||
3. 添加另一个形状,并连接它们。
|
||||
|
||||
|
||||
|
||||
重复这个过程,你就得到了一张地图。
|
||||
重复这个过程,你就得到了一张图。
|
||||
|
||||
![Draw.io example][7]
|
||||
|
||||
(Seth Kenlon, [CC BY-SA 4.0][6])
|
||||
|
||||
### 项目规划
|
||||
|
||||
当你第一次接受一项大任务时,你通常对你想要的结果有一个非常清晰的想法。假设你想开始一个社区项目来画一幅壁画。你想要的结果是一幅壁画。它很容易定义,你可以或多或少地在脑海中描绘出结果。
|
||||
@ -53,16 +49,15 @@ Draw.io 的交互界面中间有一个很大的工作空间,左边是主工具
|
||||
这不仅仅适用于绘制壁画、制作戏剧或电影。它几乎适用于任何不平凡的努力。这正是像 Draw.io 这样的应用程序可以帮助绘制的。
|
||||
|
||||
以下是如何使用 Draw.io 创建项目流程图:
|
||||
|
||||
1. 从头脑风暴开始。没有什么想法是太小或太大的。为每个想法制作一个框,然后双击 Draw.io 工作空间中的框以输入文本。
|
||||
2. 一旦在工作空间中你产生了可能想到的所有想法,就可以将它们拖放到相关的组中。我们的目标是创建一些小任务云或集群,因为它们是同一过程的一部分,所以或多或少会一起进行。
|
||||
3. 一旦你确定了相关任务的集群,就为这些任务命名。例如,如果你正在绘制壁画,那么任务可能是 _approval_、_design_、_purchase_、_paint_,这反映出你需要首先获得当地政府的许可,然后设计壁画,然后购买用品,最后绘制壁画。每个任务都有组成部分,但大体上,你现在已经确定了项目的工作流程。
|
||||
3. 一旦你确定了相关任务的集群,就为这些任务命名。例如,如果你正在绘制壁画,那么任务可能是 “许可”、“设计”、“购买”、“绘制”,这反映出你需要首先获得当地政府的许可,然后设计壁画,然后购买用品,最后绘制壁画。每个任务都有组成部分,但大体上,你现在已经确定了项目的工作流程。
|
||||
4. 用箭头连接主要任务。并不是所有的过程都是完全线性的。例如,在你获得市议会的许可后,一旦你设计了你打算画的东西,你可能必须回到他们那里进行最终批准。这很正常。这是一个循环,有一些来回,但最终,你会突破这个循环,进入下一阶段。
|
||||
5. 掌握好你的流程图,完成每一项任务,直到你达到最终目标。
|
||||
|
||||
### 思维导图
|
||||
|
||||
|
||||
|
||||
思维导图往往不是关于进步,而是关于保持某种状态或对许多想法进行展望。例如,假设你已经决定减少生活中的浪费。你对自己能做什么有一些想法,但你想组织和保存你的想法,这样你就不会忘记它们。
|
||||
|
||||
以下是如何使用 Draw.io 创建思维导图:
|
||||
@ -71,12 +66,8 @@ Draw.io 的交互界面中间有一个很大的工作空间,左边是主工具
|
||||
2. 将你的想法分组或分类。
|
||||
3. 可以选择将想法与彼此直接相关的箭头连接起来。
|
||||
|
||||
|
||||
|
||||
![Draw.io waste reduction example][8]
|
||||
|
||||
(Seth Kenlon, [CC BY-SA 4.0][6])
|
||||
|
||||
### 保存你的图表
|
||||
|
||||
你可以将图表保存为 PNG、JPG 图像、Draw.io XML 或纯 XML 文件。如果将其另存为 XML,则可以在 Draw.io 中再次打开它进行进一步编辑。导出的图像非常适合与他人共享。
|
||||
@ -92,7 +83,7 @@ via: https://opensource.com/article/21/12/open-source-mind-mapping-drawio
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[FYJNEVERFOLLOWS](https://github.com/FYJNEVERFOLLOWS)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
@ -106,3 +97,4 @@ via: https://opensource.com/article/21/12/open-source-mind-mapping-drawio
|
||||
[6]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[7]: https://opensource.com/sites/default/files/uploads/draw-io-example.jpg (Draw.io example)
|
||||
[8]: https://opensource.com/sites/default/files/uploads/draw-io-export.jpg (Draw.io waste reduction example)
|
||||
[0]: https://img.linux.net.cn/data/attachment/album/202304/07/144214m945y0naawn5zb95.jpg
|
@ -0,0 +1,91 @@
|
||||
[#]: subject: "Open Source Software: Is There an Easy Path to Success?"
|
||||
[#]: via: "https://www.opensourceforu.com/2022/07/open-source-software-is-there-an-easy-path-to-success/"
|
||||
[#]: author: "Jules Graybill https://www.opensourceforu.com/author/jules-graybill/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "CanYellow"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-15702-1.html"
|
||||
|
||||
开源软件:存在成功的捷径吗?
|
||||
======
|
||||
|
||||
> 开发开源软件背后的工作是相当庞大的。那么我们如何保证开源项目的成功呢?存在捷径吗?本文认为是没有的。
|
||||
|
||||
![][0]
|
||||
|
||||
今天,开源已经风靡世界。很多大型企业在快速成功的诱惑下被推向开源。但真实情况是世界上并不存在成功的捷径。你无法做到通过一次努力就能让所有的开源项目正常运行。
|
||||
|
||||
事实上,上述公司早期遇到的许多挑战都不是技术上的,而是人员与文化上的。
|
||||
|
||||
开发一个能够在市场上获得成功的开源项目需要在开源的许多层面上下功夫。而维持这样的成功是一个持续的过程。所有这一切的关键在于找到以下这个非常基本的问题的正确答案:开源究竟是什么。
|
||||
|
||||
### 开源是代码
|
||||
|
||||
对于很多新用户而言,他们可能并不完全了解开源的不同层面,答案相当简单:开源就是软件!这当然没有错,毕竟我们多数人就是这样使用它的。不过,相比仅仅被视作软件而言,开源远不止这些。
|
||||
|
||||
任何开源项目的实质仍然是代码本身。代码是使一个开源项目有别于其他项目,并使其对用户有益的根本。当你从事开源工作的时候,代码和软件一样都是产品的一部分。
|
||||
|
||||
从零开始开发一个开源项目或者 <ruby>复刻<rt>fork</rt></ruby> 一个现有项目,即便是在面对一个庞大而复杂的代码库时,也需要编写成千上万行代码。尤其是在创建一个现有项目的复刻的情况下,在移除任何在先的许可证、宣传材料或者其他任何可能已经失去作用的文件时必须小心翼翼(LCTT 校注:部分开源项目不允许你改变其原有的许可证)。终究是一个项目的功能吸引了它的用户群并维持项目的持续发展。当最终用户在考虑是否使用开源软件的时候,他们会阅读项目的源代码,而他们在其中所看到的应当是那些能够建立他们的信心的内容。
|
||||
|
||||
### 开源是社区
|
||||
|
||||
如何参与到社区中也是产品构建的一部分。创建一个社区并维护一个健康的社区关系是开源的核心之一,但对于大部分的领导者而言也往往是最坚难的任务,很少有人能很好地维护它。你可以尝试建立基金会或者提供赞助,但是最终还是由人们自行决定是否想要加入社区。
|
||||
|
||||
重要的是与社区保持一定程度的透明度,并不断保持。社区成员可以在它想要的任何阶段参与进来。除了需要进行的工作之外,诸如安全设置、签发证书、注册商标等,尽可能多的将你所做的工作展示给社区是相当重要的,这有助于取得社区信任。归根到底,你需要对社区负责,你的项目成也社区,败也社区。这可能会导致你的项目开发更谨慎、更缓慢并且向社区公开,不过项目最终会进展顺利。
|
||||
|
||||
如此地公开你正在进行的工作似乎有些令人生怯,尤其是当你担心更新推迟或者是出现漏洞的影响的时候。不过,让社区成员知悉你的进展,不仅有助帮助你建立与社区之间的信任关系,而且能够让社区成员感到被认可。
|
||||
|
||||
另一方面,公开你的工作流也可以获得来自社区成员的监督,他们经常有自己的见解并向你反馈。记录这些反馈是很重要的,这使得你的开源项目如实地反映社区需求。他们是项目的最终用户,而他们的反馈则反映了他们如何看待你的项目的长期发展,以及你的项目最终将有多么成功或者主流。
|
||||
|
||||
举例而言,当我们在考虑一个新功能的时候,我们在 <ruby>征求意见文档<rt>Request for Comments</rt></ruby>(RFC)中发布一个征集意见的请求,我们会收到大量的反馈,我们必须认真思考应当如何吸收这些反馈。
|
||||
|
||||
因为开源是一个大型的合作项目,社区对支持开源项目的主动支持,使项目成为了最好的项目。并非所有的问题都要解决,但只要你有在倾听社区的呼声,社区就会有参与感。
|
||||
|
||||
参与到社区中也存在一些隐患。社区内部、项目维护与社区之间均可能存在不同意见,尤其是在涉及治理的问题上。治理对于一个开源项目来说是相当重要的。这也就是为什么拥有一份清晰的文档化的治理条例对于项目以及社区均是如此重要。
|
||||
|
||||
社区治理是一个关键的而又难啃的骨头。社区授权本身需要相当大的信任。对于一个拥有成千上万行代码的项目,在社区中寻找能够有效领导社区的人物是不容易的。不过开源项目经常是由更小的子项目组成的,这些子项目最好由社区中的某个人进行管理。这有助于社区更紧密地参与到项目中。
|
||||
|
||||
建立社区的过程不是一帆风顺的。让我列举一一些有助于维持社区与我的团队之间平衡的技巧。
|
||||
|
||||
**声明你的原则:** 尤其是在开源项目的早期,在项目代码仍在完善,很多事情还不完美的时候,项目之外的人员很难真正理解你所做的决定。向他们说明你做出决定所依据的原则,有助于你在思考过程上保持坦率,从而让社区不会错误地干扰你的事务。这一经验非常有效,在你做出决定时坚持遵循其中一项原则并展示出来是非常重要的。
|
||||
|
||||
**确定如何进行协作:** 你可以通过 Discord、Slack 或者邮件等途径完成这一工作。但是如果你试图同时使用它们,你将毫不意外的分散项目社区。社区人员将在所有这些途径上互相交流。选择一到两种沟通工具,投身于它们来保证社区的信息同步。
|
||||
|
||||
**珍惜反馈意见:** 倾听来自社区的反馈并付诸行动。即使需要你作出艰难的决定,你也应当向社区展示你是重视社区话语的。
|
||||
|
||||
**维护一套行为准则:** 如果你与社区打交道,你需要定义什么行为是可以接受的。一套落地的行为准则有助于在人们越过红线时警示他们。如果你可以提前制定这些你可以避免很多麻烦。
|
||||
|
||||
**考虑如何分发你的项目:** 存在这样的情况,因为你还没有准备好某一个组件,或者是因为存在一些你不希望所有人都能够访问的项目功能,所以你可能并不希望将你的项目完全向公众公开。关键是制定符合你的要求而不是向用户妥协的分发条款,如此,需要某种功能的用户可以获取所需项目的同时,不需要该功能的用户也不需要在开始使用该项目做出妥协。
|
||||
|
||||
**尽可能地避免投票:** 这是因为部分成员经常会赞成与大部分成员的意见相左的选项,这会使这些人产生一定程度的失望,并让他们觉得被项目所孤立。反之,尽量尝试询问他们想要解决什么问题,并尝试创造一个不需要付出代价的解决方案。
|
||||
|
||||
### 开源是许可
|
||||
|
||||
开源是给予你的用户如何使用你的软件的自由,而许可能够做到这一点。开源项目许可的好处在于,它保证了不论你作为维护者做了什么,你的所有最终用户以及利益相关方总是可以维护一系列的项目复刻版本,这些都是重要的项目复刻。
|
||||
|
||||
许可提供了人们可选择性,如果他们认为有必要,他们可以将项目带到不同的路径中。他们拥有创建副本的权利,这使得许多优秀的软件能够被开发出来。维护者有责任倾听他们的社区成员的声音,并以一个对项目的社区成员有利的方式运营项目。
|
||||
|
||||
我们推荐使用现有的许多可用的许可证,而不是独立制作你自己的许可条款,原因很简单,因为用户以及利益相关方通常都很熟悉常用的许可证,因此你不需要再花费时间在解释许可条款上。这将帮助你将你的精力集中在项目的其他部分上。
|
||||
|
||||
### 最后,开源是一项运动
|
||||
|
||||
开源包括了很多维度,也包含了很多人员。最重要的是,它是关于理解人们想要什么,并创建一个鼓励协作与透明的环境。开源也是关于创建社区,帮助建立走自己想走的开源项目的方式。维护者创造越多的机会让社区自由发挥,开源产品就越好,也越发成功。
|
||||
|
||||
开源是以上所有这些方面,而你的视野越宽阔,你就能越好的利用它。请考虑你如何能够在开源的每一个维度上出类拔萃,因为时至今日,开源的成功之路并无捷径。
|
||||
|
||||
---
|
||||
|
||||
via: https://www.opensourceforu.com/2022/07/open-source-software-is-there-an-easy-path-to-success/
|
||||
|
||||
作者:[Jules Graybill][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[CanYellow](https://github.com/CanYellow)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.opensourceforu.com/author/jules-graybill/
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://www.opensourceforu.com/wp-content/uploads/2022/07/team-work-working-together-1.jpg
|
||||
[0]: https://img.linux.net.cn/data/attachment/album/202304/08/122245uqlbfaxp3flkwf5b.jpg
|
@ -7,14 +7,14 @@
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-15697-1.html"
|
||||
|
||||
NixOS 系列 #5:如何在 NixOS 上设置家庭管理员?
|
||||
NixOS 系列 #5:如何在 NixOS 上设置主目录管理器
|
||||
======
|
||||
|
||||
在发表这篇文章之前,我解释了如何为一个单用户系统 [在 NixOS 中安装和删除软件包][1]。
|
||||
|
||||
但是,如果你正在供多个用户使用,有一个很好的方法来分别满足每个用户的需求。
|
||||
|
||||
在本指南中,我将指导你如何在 NixOS 上设置一个家庭管理员,以及如何使用它来安装软件包。
|
||||
在本指南中,我将指导你如何在 NixOS 上设置一个 <ruby>主目录管理器<rt>Home Manager</rt></ruby>,以及如何使用它来安装软件包。
|
||||
|
||||
如果你是新读者,本系列中讨论的一些资源包括:
|
||||
|
||||
@ -22,18 +22,18 @@ NixOS 系列 #5:如何在 NixOS 上设置家庭管理员?
|
||||
- [在虚拟机上安装 NixOS][3]
|
||||
- [安装 NixOS 后要做的事情][4]
|
||||
|
||||
### 在 NixOS 上设置家庭管理员
|
||||
### 在 NixOS 上设置主目录管理器
|
||||
|
||||
在本指南中,我将指导你通过 2 种方式来设置家庭管理员:
|
||||
在本指南中,我将指导你通过 2 种方式来设置主目录管理器:
|
||||
|
||||
- 独立的家庭管理员(使用单独的配置文件)
|
||||
- 独立的主目录管理器(使用单独的配置文件)
|
||||
- 作为一个 NixOS 模块(在 `configuration.nix` 文件中使用它)
|
||||
|
||||
那么,让我们从独立方式开始。
|
||||
|
||||
#### 独立安装的家庭管理员
|
||||
#### 独立安装的主目录管理器
|
||||
|
||||
如果你使用的是 NixOS 的稳定频道,你可以使用以下命令来配置家庭管理器:
|
||||
如果你使用的是 NixOS 的稳定频道,你可以使用以下命令来配置主目录管理器:
|
||||
|
||||
```
|
||||
nix-channel --add https://github.com/nix-community/home-manager/archive/release-22.11.tar.gz home-manager
|
||||
@ -55,7 +55,7 @@ nix-channel --add https://github.com/nix-community/home-manager/archive/master.t
|
||||
nix-channel --update
|
||||
```
|
||||
|
||||
最后,使用下面的命令来安装家庭管理员:
|
||||
最后,使用下面的命令来安装主目录管理器:
|
||||
|
||||
```
|
||||
nix-shell '<home-manager>' -A install
|
||||
@ -63,19 +63,19 @@ nix-shell '<home-manager>' -A install
|
||||
|
||||
🛠️ 在安装时,可能会出现以下错误:
|
||||
|
||||
![安装家庭管理员时出现构建错误][5]
|
||||
![安装主目录管理器时出现构建错误][5]
|
||||
|
||||
重新启动你的系统并再次使用安装命令,它将开始安装。
|
||||
|
||||
一旦完成,它将显示独立安装的家庭管理员的位置:
|
||||
一旦完成,它将显示独立安装的主目录管理器的位置:
|
||||
|
||||
![家庭管理员在NixOS中的位置][6]
|
||||
![主目录管理器在NixOS中的位置][6]
|
||||
|
||||
#### 将家庭管理员安装为 NixOS 模块
|
||||
#### 将主目录管理器安装为 NixOS 模块
|
||||
|
||||
> ⚠️ 如果你选择将家庭管理员作为 NixOS 模块使用,你将需要 sudo 权限。
|
||||
> ⚠️ 如果你选择将主目录管理器作为 NixOS 模块使用,你将需要 sudo 权限。
|
||||
|
||||
如果你在一个稳定的频道上(在写本文的时候,是 `22.11`),你可以使用下面的命令来添加家庭管理员的稳定频道:
|
||||
如果你在一个稳定的频道上(在写本文的时候,是 `22.11`),你可以使用下面的命令来添加主目录管理器的稳定频道:
|
||||
|
||||
```
|
||||
sudo nix-channel --add https://github.com/nix-community/home-manager/archive/release-22.11.tar.gz home-manager
|
||||
@ -105,7 +105,7 @@ sudo nano /etc/nixos/configuration.nix
|
||||
<home-manager/nixos>
|
||||
```
|
||||
|
||||
![将家庭管理员安装为 NixOS 模块][7]
|
||||
![将主目录管理器安装为 NixOS 模块][7]
|
||||
|
||||
现在,跳到该行的末尾,在 `}` 前添加以下内容:
|
||||
|
||||
@ -115,7 +115,7 @@ home-manager.users.{username} = { pkgs, ... }: {
|
||||
};
|
||||
```
|
||||
|
||||
![NixOS 配置文件中的家庭管理员模块的格式][8]
|
||||
![NixOS 配置文件中的主目录管理器模块的格式][8]
|
||||
|
||||
上面这一行是为了方便安装和删除软件包而添加的,我接下来会告诉你。
|
||||
|
||||
@ -133,9 +133,9 @@ sudo nixos-rebuild switch
|
||||
|
||||
![错误: 选项 `home-manager.users.user.home.stateVersion` 已被使用但未被定义。][10]
|
||||
|
||||
要解决这个问题,你必须在你的家庭管理器块中添加 `home.stateVersion`。
|
||||
要解决这个问题,你必须在你的主目录管理器块中添加 `home.stateVersion`。
|
||||
|
||||
在写这篇文章时,我正在运行 22.11,所以整个家庭管理员块看起来像这样:
|
||||
在写这篇文章时,我正在运行 22.11,所以整个主目录管理器块看起来像这样:
|
||||
|
||||
```
|
||||
home-manager.users.{username} = { pkgs, ... }: {
|
||||
@ -150,11 +150,11 @@ home-manager.users.{username} = { pkgs, ... }: {
|
||||
|
||||
现在,试着重建配置并再次进行切换,应该可以解决问题。
|
||||
|
||||
### 如何在 NixOS 上使用家庭管理员安装软件包
|
||||
### 如何在 NixOS 上使用主目录管理器安装软件包
|
||||
|
||||
现在你已经安装了家庭管理员,如何用它来安装软件包:
|
||||
现在你已经安装了主目录管理器,如何用它来安装软件包:
|
||||
|
||||
#### 使用独立安装的家庭管理员
|
||||
#### 使用独立安装的主目录管理器
|
||||
|
||||
首先,用下面的方法打开配置文件:
|
||||
|
||||
@ -192,7 +192,7 @@ home.packages = with pkgs; [htop];
|
||||
|
||||
例如,在这里,我想安装 `htop`、`firefox`和 `libreoffice`,所以我的 `home` 块会看起来像这样:
|
||||
|
||||
![在 NixOS 上使用家庭管理员安装多个软件包][12]
|
||||
![在 NixOS 上使用主目录管理器安装多个软件包][12]
|
||||
|
||||
一旦你完成了添加你喜欢的软件包,保存配置文件并使用以下命令来安装软件包:
|
||||
|
||||
@ -208,7 +208,7 @@ home-manager switch
|
||||
sudo nano /etc/nixos/configuration.nix
|
||||
```
|
||||
|
||||
在配置部分,我已经添加了家庭管理员块,所以剩下的就是在 `home.packages = [ ];` 里面添加软件包的名称,格式如图所示:
|
||||
在配置部分,我已经添加了主目录管理器块,所以剩下的就是在 `home.packages = [ ];` 里面添加软件包的名称,格式如图所示:
|
||||
|
||||
```
|
||||
home.packages = [ pkgs.package_name ] ;
|
||||
@ -222,9 +222,9 @@ home.packages = [ pkgs.package_name ] ;
|
||||
pkgs.htop pkgs.firefox pkgs.libreoffice
|
||||
```
|
||||
|
||||
然后我的家庭管理员块会看起来像这样:
|
||||
然后我的主目录管理器块会看起来像这样:
|
||||
|
||||
![作为 NixOS 模块在家庭管理员中安装多个软件包][14]
|
||||
![作为 NixOS 模块在主目录管理器中安装多个软件包][14]
|
||||
|
||||
现在,保存修改并退出文本编辑器。
|
||||
|
||||
|
@ -3,14 +3,18 @@
|
||||
[#]: author: "Don Watkins https://opensource.com/users/don-watkins"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "geekpi"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-15703-1.html"
|
||||
|
||||
使用这个开源的会计应用来管理你的小企业
|
||||
======
|
||||
|
||||
[GnuCash][1] 是一个强大而灵活的会计工具,可用于小企业的发票和会计。它的许多功能使它特别适合这一目的,包括跟踪支出和收入、生成报告和管理发票的能力。此外,GnuCash 是免费和开源的,这使得资源有限的小企业也可以使用它。在这篇文章中,我会讨论 GnuCash 的特点,使你可以很容易地在自己的小企业中开始使用它。
|
||||
![][0]
|
||||
|
||||
> 用 GnuCash 跟踪客户和发票的情况。
|
||||
|
||||
[GnuCash][1] 是一个强大而灵活的会计工具,可用于小企业的发票和会计。它的许多功能使它特别适合这一目的,包括跟踪支出和收入、生成报告和管理发票的能力。此外,GnuCash 是自由开源的,这使得资源有限的小企业也可以使用它。在这篇文章中,我会讨论 GnuCash 的特点,使你可以很容易地在自己的小企业中开始使用它。
|
||||
|
||||
几年前,我开始使用 GnuCash 进行个人财务管理,但发现它也可以作为我的小企业的一个有用工具。在我企业的大部分时间里,我一直在使用一个专有的解决方案。我逐渐厌倦了被迫定期升级以获取我的小企业的发票和报表。转而使用 GnuCash,使我能够在不牺牲任何功能的情况下,将我的小企业会计与我的个人财务相结合。
|
||||
|
||||
@ -33,33 +37,33 @@ $ sudo dnf install gnucash
|
||||
GnuCash 带有一个账户设置向导,可以帮助你建立一个普通的商业账户配置。要访问它:
|
||||
|
||||
- 启动 GnuCash。
|
||||
- 点击**文件**菜单,选择**新文件**。
|
||||
- 点击 “<ruby>文件<rt>File</rt></ruby>” 菜单,选择 “<ruby>新文件<rt>New File</rt></ruby>”。
|
||||
|
||||
按照屏幕上出现的 GnuCash 助手来创建你的新商业账户文件。
|
||||
|
||||
屏幕上的说明将指导你完成设置业务的过程。单击“**助手**”窗口右上角的“**下一步**”。系统会提示你输入公司名称、地址、联系信息和你自己选择的公司 ID。你还必须选择默认税表和日期格式。
|
||||
屏幕上的说明将指导你完成设置业务的过程。单击 “<ruby>助手<rt>Assistant</rt></ruby>” 窗口右上角的 “<ruby>下一步<rt>Next</rt></ruby>”。系统会提示你输入公司名称、地址、联系信息和你自己选择的公司 ID。你还必须选择默认税表和日期格式。
|
||||
|
||||
下一个页面提示你选择货币,有大量的货币支持。
|
||||
|
||||
然后提示你选择你要创建的账户。选择创建**企业账户**的选项。你可以随时定制账户列表,GnuCash 提供了[大量的文档][2],帮助你更好地根据个人需求进行定制。
|
||||
然后提示你选择你要创建的账户。选择创建 “<ruby>企业账户<rt>Business Accounts</rt></ruby>” 的选项。你可以随时定制账户列表,GnuCash 提供了 [大量的文档][2],帮助你更好地根据个人需求进行定制。
|
||||
|
||||
完成助手,然后单击 GnuCash **助手**窗口右上角的**应用**。
|
||||
完成助手,然后单击 GnuCash “助手” 窗口右上角的 “<ruby>应用<rt>Apply</rt></ruby>”。
|
||||
|
||||
### 添加客户
|
||||
|
||||
GnuCash 的顶部菜单有一个标有**业务**的菜单项。该菜单上的第一个项目是**客户**,其次是**客户概览**。在这里你可以查看你所有客户的列表。
|
||||
GnuCash 的顶部菜单有一个标有 “<ruby>业务<rt>Business</rt></ruby>” 的菜单项。该菜单上的第一个项目是 “<ruby>客户<rt>Customers</rt></ruby>”,其次是 “<ruby>客户概览<rt>Customers Overview</rt></ruby>”。在这里你可以查看你所有客户的列表。
|
||||
|
||||
下一个项目是**新客户**。这是你输入新客户的地方。对话框为客户信息提供了一个位置,包括帐单信息、运输地址、电子邮件地址、电话号码等。
|
||||
下一个项目是 “<ruby>新客户<rt>New Customer</rt></ruby>”。这是你输入新客户的地方。对话框为客户信息提供了一个位置,包括帐单信息、运输地址、电子邮件地址、电话号码等。
|
||||
|
||||
### 创建一个发票
|
||||
|
||||
添加客户后,你可以开始创建发票的过程。点击**业务**菜单,选择**客户**,然后点击**新发票**。
|
||||
添加客户后,你可以开始创建发票的过程。点击 “业务” 菜单,选择 “客户”,然后点击 “<ruby>新发票<rt>New Invoice</rt></ruby>”。
|
||||
|
||||
付款处理也很简单。这位于**业务**菜单中。选择**客户**,然后**处理付款**。
|
||||
付款处理也很简单。这位于 “业务” 菜单中。选择 “客户”,然后 “<ruby>处理付款<rt>Process Payment</rt></ruby>”。
|
||||
|
||||
### 你在做生意了
|
||||
|
||||
如果你的业务需要,**业务**菜单还包括输入供应商和雇员的选项。有一个菜单项用于销售税和许多其他选项,以确保你符合当地的要求。
|
||||
如果你的业务需要,“业务” 菜单还包括输入供应商和雇员的选项。有一个菜单项用于销售税和许多其他选项,以确保你符合当地的要求。
|
||||
|
||||
使用 GnuCash,你的数据不是以专有格式存储的,所以如果你需要,你可以在将来迁移到任何其他平台。数据存储的开放标准,特别是当这些数据是法律要求的时候,是很重要的,可以让你完全拥有你的商业历史。使用 GnuCash 使你能控制你的小企业。
|
||||
|
||||
@ -70,11 +74,12 @@ via: https://opensource.com/article/23/3/open-source-accounting-run-business
|
||||
作者:[Don Watkins][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/don-watkins
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://www.gnucash.org/
|
||||
[2]: https://www.gnucash.org/docs/v4/C/gnucash-guide/bus_setup.html
|
||||
[2]: https://www.gnucash.org/docs/v4/C/gnucash-guide/bus_setup.html
|
||||
[0]: https://img.linux.net.cn/data/attachment/album/202304/08/150133rfxheuuce1ufi99c.jpg
|
@ -3,52 +3,56 @@
|
||||
[#]: author: "Jessica Cherry https://opensource.com/users/cherrybomb"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "geekpi"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-15699-1.html"
|
||||
|
||||
用 Emojicode 写一个可爱的程序
|
||||
======
|
||||
|
||||
在这篇文章中,我将介绍一个最好的编码语言,学习如何制作任何东西!它叫做 Emojicode。Emojicode 由 Theo Belaire 在 2014 年创建,是一种开源的编程语言,使用 emoji 字符来表示其语法。在 Emojicode 中工作时,emoji 被用来创建变量、函数和控制结构。因为它是一种静态类型的语言,变量类型必须在使用前声明,但它也支持类和继承等面向对象的概念。这种语言可以在每个操作系统上运行,它是一种超级有趣的编码方式,特别是当你是一个非英语母语的人时。这很有帮助,因为象形图表示法可以把我们大家聚集在一起,让我们以类似数学的方式说同样的语言。
|
||||
![][0]
|
||||
|
||||
> 这种有趣的开源语言是学习编码真正的完美选择。
|
||||
|
||||
在这篇文章中,我将介绍一个最好的编码语言,学习如何制作任何东西!它叫做 Emojicode,是由 Theo Belaire 在 2014 年创建的,它是一种开源的编程语言,使用 emoji 字符来表示其语法。当用 Emojicode 开发时,表情符被用来创建变量、函数和控制结构。因为它是一种静态类型的语言,变量类型必须在使用前声明,但它也支持类和继承等面向对象的概念。这种语言可以在每个操作系统上运行,它是一种超级有趣的编码方式,特别是当你是一个非英语母语的人时。这很有帮助,因为象形表示可以把我们大家聚集在一起,让我们以类似数学的方式说同样的语言。
|
||||
|
||||
### 先决条件
|
||||
|
||||
在本教程中,我使用的是基于 Debian 的操作系统。我的工具可能与你的操作系统的要求不同。以下是我所使用的工具:
|
||||
|
||||
- [Geany][1],一个 Linux 下的开源 IDE。
|
||||
- IBus,它允许你挑选 emoji 并把它们放在你的编辑器中。我使用的界面被称为 **emoji picker**。
|
||||
- IBus,它允许你挑选表情符并把它们放在你的编辑器中。我使用的界面被称为 **emoji picker**。
|
||||
- 基于 Debian 的 Linux。
|
||||
- 一个 C++ 编译器。我使用的是 `g++` 编译器。
|
||||
- [Emojicode][2]
|
||||
|
||||
我使用的是 Linux 操作系统,但你可以[阅读文档][3]了解在其他操作系统上使用它时可能需要采取的任何特殊步骤。
|
||||
我使用的是 Linux 操作系统,但你可以 [阅读文档][3] 了解在其他操作系统上使用它时可能需要采取的任何特殊步骤。
|
||||
|
||||
### 在 Linux 上安装 Emojicode
|
||||
|
||||
有几种方法可以在你的电脑上安装 Emojicode,但他们有一个很酷的[神奇的安装页面][4],可以告诉你具体该怎么做。下面是我的做法:
|
||||
有几种方法可以在你的电脑上安装 Emojicode,但它们有一个很酷的 [神奇的安装页面][4],可以告诉你具体该怎么做。下面是我的做法:
|
||||
|
||||
```
|
||||
$ wget https://github.com/emojicode/emojicode/releases/download/v1.0-beta.2/Emojicode-1.0-beta.2-Linux-x86_64.tar.gz -O emojicode.tar.gz \
|
||||
&& tar -xzf emojicode.tar.gz && rm emojicode.tar.gz \
|
||||
&& cd Emojicode-1.0-beta.2-Linux-x86_64 && ./install.sh \
|
||||
&& cd .. && rm -r Emojicode-1.0-beta.2-Linux-x86_64
|
||||
&& tar -xzf emojicode.tar.gz && rm emojicode.tar.gz \
|
||||
&& cd Emojicode-1.0-beta.2-Linux-x86_64 && ./install.sh \
|
||||
&& cd .. && rm -r Emojicode-1.0-beta.2-Linux-x86_64
|
||||
```
|
||||
|
||||
emojicode的安装过程提供了有用的反馈。
|
||||
Emojicode 的安装过程提供了有用的反馈。
|
||||
|
||||
![The emojicode installation procedure provides useful feedback along the way.][5]
|
||||
|
||||
现在,你已经安装好了,是时候开始编写代码了!
|
||||
|
||||
### 整体是如何进行的?
|
||||
### 它是怎么运作的?
|
||||
|
||||
首先,所有 Emojicode 文件的扩展名都以文件名.🍇结尾,但因为你不能在一般的文件名中这样做,所以它被翻译成 `filename.emojic`。这里是最重要的语法元素:
|
||||
首先,所有 Emojicode 文件的扩展名都以文件名 `.🍇` 结尾,但因为你不能在一般的文件名中这样做,所以它被翻译成 `filename.emojic`。这里是最重要的语法元素:
|
||||
|
||||
- 把 🏁 放在一行的开头,表示要执行哪些代码块
|
||||
- 用 🍇 开始一个代码块
|
||||
- 用 🍉 来结束一个代码块
|
||||
- 想打印什么吗?就用 😀 🔤 `<string>` 🔤 ❗
|
||||
- 把 `🏁` 放在一行的开头,表示要执行哪些代码块
|
||||
- 用 `🍇` 开始一个代码块
|
||||
- 用 `🍉` 来结束一个代码块
|
||||
- 想打印什么吗?就用 `😀 🔤 <string> 🔤 ❗`
|
||||
|
||||
还有很多其他的,所以这里有一些实际的例子。
|
||||
|
||||
@ -75,7 +79,6 @@ haiku haiku.emojic haiku.o
|
||||
|
||||
正如你所看到的,代码已经被编译并生成了两个文件,其中一个是可执行的。运行 `haiku` 文件:
|
||||
|
||||
|
||||
```
|
||||
$ ./haiku
|
||||
Emojicode is great,
|
||||
@ -85,27 +88,27 @@ no sadness, just joy.
|
||||
|
||||
### 数学和变量操作
|
||||
|
||||
接下来,你要同时做几件事:一点点数学和变量的改变。首先,将一个变量赋值为0:
|
||||
接下来,你要同时做几件事:一点点数学和变量的改变。首先,将一个变量赋值为 0:
|
||||
|
||||
```
|
||||
0 ➡️ 🖍🆕x
|
||||
```
|
||||
|
||||
你刚刚使用 crayon emoji 和变量名称旁边的新 emoij 创建了一个新变量,同时还将该变量赋值为 0。
|
||||
你刚刚使用蜡笔(`🖍`)表情符、新建(`🆕`)表情符和变量名称创建了一个新变量,同时还将该变量赋值为 0。
|
||||
|
||||
接下来,用磁铁的 emoji 打印一行包括该变量的内容:
|
||||
接下来,用磁铁(`🧲`)表情符打印一行包括该变量的内容:
|
||||
|
||||
```
|
||||
😀 🔤The value is 🧲x🧲 🔤 ❗
|
||||
```
|
||||
|
||||
接下来,使用加号和箭头 emoji 改变变量:
|
||||
接下来,使用加号(`➕`)和箭头(`⬅️`)表情符改变变量:
|
||||
|
||||
```
|
||||
x ⬅️➕ 1
|
||||
```
|
||||
|
||||
然后打印另一行的值。我继续这样做了一会儿,然后打印出最终的数值。下面是我的做法:
|
||||
然后打印另一行的值。如此这般,然后打印出最终的数值。如下:
|
||||
|
||||
```
|
||||
🏁 🍇
|
||||
@ -140,7 +143,7 @@ The value is 7
|
||||
The final value is 3
|
||||
```
|
||||
|
||||
如你所见,作为变量打印出来的所有内容都已使用新数学进行了更新。你可以用许多数学 emoji 来进一步操作。下面是一些更多的运算符:
|
||||
如你所见,作为变量打印出来的所有内容都已使用新数学进行了更新。你可以用许多数学表情符来进一步操作。下面是一些更多的运算符:
|
||||
|
||||
```
|
||||
🚮 is your modulo
|
||||
@ -166,11 +169,11 @@ The final value is 3
|
||||
🆕🔡▶️👂🏼❗️ ➡️ inputText
|
||||
```
|
||||
|
||||
我试图让它工作,但当我发现它时我的编译器出现了一些问题。 你也可能会在这里和那里遇到一些小问题。 如果你遇到了,请创建一个[问题][6],这样它就有可能被修复。
|
||||
我试图让它工作,我的编译器出现了一些问题,我发现了这个问题。你也可能会在这里和那里遇到一些小问题。如果你遇到了,请创建一个 [议题][6],这样它就有可能被修复。
|
||||
|
||||
### 没有技巧,只有很棒的代码
|
||||
|
||||
虽然我可以了解更多,但我可以向你保证,这段令人惊叹的代码背后的文档非常广泛。尽管我写这篇文章只是为了赶上愚人节的乐趣,但我不得不承认这是有史以来最好的语言之一,因为它教会了你很多非常真实的编程概念。我恳切地建议把它作为一种有趣的方式来教你的一些朋友、孩子,或者是对编码感兴趣的同学。 希望你度过了一个充满乐趣的愚人节!
|
||||
虽然我可以介绍更多内容,但我可以向你保证,这段令人惊叹的代码背后的文档非常丰富。尽管我写这篇文章只是为了赶上愚人节的乐趣,但我不得不承认这是有史以来最好的语言之一,因为它教会了你很多非常真实的编程概念。我恳切地建议把它作为一种有趣的方式来教你的一些朋友、孩子,或者是对编码感兴趣的同学。希望你度过了一个充满乐趣的愚人节!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -179,7 +182,7 @@ via: https://opensource.com/article/23/4/emojicode
|
||||
作者:[Jessica Cherry][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
@ -191,3 +194,4 @@ via: https://opensource.com/article/23/4/emojicode
|
||||
[4]: https://www.emojicode.org/docs/guides/
|
||||
[5]: https://opensource.com/sites/default/files/2023-03/emoji.webp
|
||||
[6]: https://github.com/emojicode/emojicode/issues
|
||||
[0]: https://img.linux.net.cn/data/attachment/album/202304/07/141230yv2s53itmiw45ots.jpg
|
151
published/20230407.2 ⭐️⭐️ Xubuntu 23.04 Best New Features.md
Normal file
151
published/20230407.2 ⭐️⭐️ Xubuntu 23.04 Best New Features.md
Normal file
@ -0,0 +1,151 @@
|
||||
[#]: subject: "Xubuntu 23.04: Best New Features"
|
||||
[#]: via: "https://www.debugpoint.com/xubuntu-23-04/"
|
||||
[#]: author: "Arindam https://www.debugpoint.com/author/admin1/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "wxy"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-15706-1.html"
|
||||
|
||||
Xubuntu 23.04 的最佳新功能
|
||||
======
|
||||
|
||||
![][0]
|
||||
|
||||
> 一些很酷的功能将在 Xubuntu 23.04 “Lunar Lobster” 版本中出现。
|
||||
|
||||
Xubuntu 23.04,基于即将发布的 [Ubuntu 23.04 “Lunar Lobster”][1],将于 2023 年 4 月 20 日到达。这个最新版本建立在 Linux 内核 6.2 上,带来了最新的硬件、CPU、GPU 和文件系统支持。
|
||||
|
||||
从改进的小程序到更强大的 Thunar 文件管理器,Xubuntu 23.04 提供了大量的改进和错误修复,通过所有 Linux 桌面的 “OG” —— Xfce 4.18,提供了更精炼的用户体验。
|
||||
|
||||
![Xubuntu 23.04 桌面][2]
|
||||
|
||||
如果你正在使用之前的 Xubuntu 22.10,那么你可能会注意到桌面环境的明显变化。Xubuntu 22.10 以开发版 Xfce 4.17 为特色,并带有来自 Xfce 4.18 少量功能。
|
||||
|
||||
考虑到这一点,让我们来看看 Xubuntu 23.04 “Lunar Lobster” 的最佳新功能。
|
||||
|
||||
### Xubuntu 23.04 的最佳新功能
|
||||
|
||||
#### Xfce 4.18 更新
|
||||
|
||||
这个版本中的一个重要变化是对 Xfce 桌面环境的更新。Xubuntu 23.04 基于 2022 年 12 月发布的 [最新的 Xfce 4.18][3]。Xfce 4.18 是该桌面环境的一个重要里程碑,提供了对 GTK4 的更新、对 Wayland 的初始支持以及对核心原生应用的改造,更新量很大。
|
||||
|
||||
顶部主面板已经更新了新的设置和调整,而整体外观仍与早期版本相同。一些默认的面板小程序在这个版本中也被改变了,而桌面图标、右键上下文菜单和项目保持不变。
|
||||
|
||||
![Xfce 4.18 中的面板偏好][4]
|
||||
|
||||
面板首选项得到了增强,有两个新的选项。首先,面板的长度现在是以像素为单位,而不是百分比。其次,增加了一个新的选项,“保持面板在窗口上方”,允许用户将窗口对话放到面板后面。在早期版本中,应用程序窗口只能达到面板边缘。
|
||||
|
||||
在 Xfce 4.18 中对时钟小程序的设置进行了大修。用户终于可以改变 Xfce 时钟小程序的字体风格,并且有四个时钟布局选项:只显示日期,只显示时间,日期和时间,以及时间和日期。
|
||||
|
||||
#### Thunar 文件管理器的更新
|
||||
|
||||
由于在 [Google Summer of Code 2022][5] 期间所做的工作,用户现在可以在 Thunar 的嵌入式侧边栏中看到图片预览,或者在独立模式下出现在右侧的新面板中,这可以通过偏好设置进行更改。
|
||||
|
||||
Thunar 的设置也得到了加强,增加了一个新的标签用于定制键盘快捷键。用户现在可以直接指定新的组合键,并从这个标签中改变现有的组合键。
|
||||
|
||||
一个新的搜索图标已经取代了工具栏中的重载按钮,当点击它时,它会在地址栏中调出搜索,用用户的搜索关键词进行递归搜索。重载按钮已被移至 “<ruby>查看<rt>View</rt></ruby>” 菜单。另一个新项目,“<ruby>最近<rt>Recent</rt></ruby>”,已被添加到左边的导航栏。同时,元数据被组织得更好了(从逗号分隔换成竖线分隔),一个新的上下文菜单项允许用户选择他们想要的选项。
|
||||
|
||||
Thunar 的主菜单也发生了重大变化。引入了一个新的 “<ruby>书签<rt>Bookmarks</rt></ruby>” 菜单,允许用户将当前文件夹作为快捷方式添加到侧边栏中。“<ruby>编辑<rt>Edit</rt></ruby>”菜单现在包括 “<ruby>撤销<rt>Undo</rt></ruby>” 和 “<ruby>重做<rt>Redo</rt></ruby>” 选项,而 “<ruby>前往<rt>Go</rt></ruby>” 菜单则有 “最近”和 “<ruby>按文件搜索<rt>Search for the file</rt></ruby>”选项。
|
||||
|
||||
![Thunar 带有分割视图和图像预览][6]
|
||||
|
||||
Thunar 还首次通过 “<ruby>视图<rt>View</rt></ruby>” 菜单项增加了分割视图,使用户能够在视图面板中拖放项目。另外,为了组织你的文件夹以加快工作流程,Thunar 还为你的文件夹及其名称引入了背景颜色。
|
||||
|
||||
![带有文件夹高亮选项的 Thunar][7]
|
||||
|
||||
除了 Xfce 4.18 的功能外,Xubuntu 23.04 还为窗口管理器和桌面的提供了更多的错误修复和性能改进。这些改进是在底层进行的;用户可以期待一个更精巧的 Xfce 桌面体验。
|
||||
|
||||
虽然 Xfce 桌面核心和本地应用程序的 Wayland 迁移工作已经开始,但它仍然远远没有准备好。因此,这个 Xubuntu 23.04 可能是未来 Wayland 工作的基础,希望可以出现在下一个 Xubuntu LTS 之前。虽然,考虑到 Xfce 团队的规模和其他方面,这不太有信心。
|
||||
|
||||
#### 最小化 ISO
|
||||
|
||||
正如我之前所报道的,Xubuntu 23.04 也引入了一个最小化的 ISO 镜像,其中只有基本的 Xfce 桌面组件,没有任何额外的预装软件。你可以试试这个最小化的 ISO,为你的工作流程建立你自己的桌面设置。
|
||||
|
||||
最小化的 ISO 大小目前为 1.9GB,团队正在努力在未来的版本中进一步减少它。
|
||||
|
||||
你可以在 [这篇文章中][8] 阅读更多关于 Xubuntu 最小化 ISO 的信息。
|
||||
|
||||
![Xubuntu 最小化和标准安装比较][9]
|
||||
|
||||
#### Flathub 和 Snap
|
||||
|
||||
几周前,Canonical 宣布已决定从所有 Ubuntu 官方风味版中默认删除 Flatpak 支持。因此,在 Xubuntu 23.04 中,你将不会默认安装 Flatpak。
|
||||
|
||||
Ubuntu 自己的 Snap 将默认安装所有相关组件,以运行几个 Snap 应用程序,如 Firefox。
|
||||
|
||||
但是,在 Xubuntu 中设置 Flatpak 和 Flathub 非常容易,[只需要两个命令][10]。
|
||||
|
||||
#### 其他变化和总结
|
||||
|
||||
在核心方面,Xubuntu 23.04 基于 [Linux 内核 6.2][11] 主线版本,它带来了对领先制造商的最新 CPU/GPU 产品的支持。此外,这个内核版本还引入了内存优化、安全修复和许多附件支持。
|
||||
|
||||
应用程序栈和 GNOME 组件的更新如下:
|
||||
|
||||
- Firefox 111.0(Snap)
|
||||
- Thunderbird 102.9
|
||||
- Thunar 4.18.4
|
||||
- Parole media player 4.18
|
||||
- LibreOffice 7.5
|
||||
- GNOME Software 44.0
|
||||
- Catfish file search 4.16.4
|
||||
- Transmission 3.0
|
||||
|
||||
![GNOME 软件应用 44 在 Xubuntu 23.04 中][12]
|
||||
|
||||
在核心部分,Python 3.11 现在可以在 Xubuntu 23.04 中开箱即用。你不需要再单独 [安装 Python 3.11][13] 了。值得一提的是,Python 3.12版本将在今年发布,目前正在进行多个 RC 测试。下面是这个版本中核心模块的总结:
|
||||
|
||||
- Python 3.11
|
||||
- GCC 13
|
||||
- GlibC 2.37
|
||||
- Ruby 3.1
|
||||
- golang 1.2
|
||||
- LLVM 16
|
||||
|
||||
### 下载
|
||||
|
||||
你可以从下面的链接中下载 Xubuntu 23.04(测试版)。请记住,它仍然在进行测试。所以,请谨慎使用它。
|
||||
|
||||
> **[下载 Xubuntu 23.04 - Beta][14]**
|
||||
|
||||
如果你想要 Xubuntu 23.04 的最小化 ISO,你可以从下面的链接获得该文件。[了解更多关于 Xubuntu-mini][8]。
|
||||
|
||||
> **[下载 Xubuntu 23.04 (mini-ISO) - Beta][14]**
|
||||
|
||||
### 总结
|
||||
|
||||
总之,Xubuntu 23.04 是一个重要的版本,具有 Xfce 4.18 桌面环境的若干改进和功能。由于专注于提高用户体验,Xubuntu 用户可以享受到最新的 Linux 内核、改进后的 Thunar 文件管理器以及其他一些调整和变化。
|
||||
|
||||
这将是 Xubuntu 对每个人来说最好的版本之一。
|
||||
|
||||
(题图由 MJ 生成:https://s.mj.run/0robf_nipRw Lunar Lobster hyper detailed, intricate detail, beautiful lighting, Illustration --q 2 --ar 16:9 --v 5)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.debugpoint.com/xubuntu-23-04/
|
||||
|
||||
作者:[Arindam][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.debugpoint.com/author/admin1/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://www.debugpoint.com/ubuntu-23-04-features/
|
||||
[2]: https://www.debugpoint.com/wp-content/uploads/2023/04/Xubuntu-23.04-Desktop.jpg
|
||||
[3]: https://www.debugpoint.com/xfce-4-18-features/
|
||||
[4]: https://www.debugpoint.com/wp-content/uploads/2022/11/Panel-preferences-in-Xfce-4.18.jpg
|
||||
[5]: https://debugpointnews.com/xfce-gsoc-2022/
|
||||
[6]: https://www.debugpoint.com/wp-content/uploads/2023/04/Thunar-with-split-view-and-image-preview.jpg
|
||||
[7]: https://www.debugpoint.com/wp-content/uploads/2023/04/Thunar-with-folder-highlight-option.jpg
|
||||
[8]: https://www.debugpoint.com/xubuntu-minimal/
|
||||
[9]: https://www.debugpoint.com/wp-content/uploads/2023/03/Xubuntu-minimal-and-standard-install-comparison.jpg
|
||||
[10]: https://www.debugpoint.com/how-to-install-flatpak-apps-ubuntu-linux/
|
||||
[11]: https://www.debugpoint.com/linux-kernel-6-2/
|
||||
[12]: https://www.debugpoint.com/wp-content/uploads/2023/04/GNOME-Software-44-in-Xubuntu-23.04.jpg
|
||||
[13]: https://www.debugpoint.com/install-python-3-11-ubuntu/
|
||||
[14]: https://cdimage.ubuntu.com/xubuntu/releases/lunar/beta/
|
||||
[15]: https://www.xfce-look.org/p/1953253
|
||||
[0]: https://img.linux.net.cn/data/attachment/album/202304/09/164211inurec5cc59cqtmc.jpg
|
@ -0,0 +1,52 @@
|
||||
[#]: subject: "Linux Mint Team Announced “Styles” for Cinnamon Desktop"
|
||||
[#]: via: "https://debugpointnews.com/cinnamon-styles-announcement/"
|
||||
[#]: author: "arindam https://debugpointnews.com/author/dpicubegmail-com/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Linux Mint Team Announced “Styles” for Cinnamon Desktop
|
||||
======
|
||||
|
||||
**The Linux Mint team has introduced “styles” in Cinnamon desktop for the upcoming release, Linux Mint 21.2, allowing for easier customization and a visually appealing interface.**
|
||||
|
||||
In their monthly update post, the Linux Mint team gave a sneak peek of what’s in store for the upcoming release, Linux Mint 21.2. One of the exciting new features that caught the eye was the introduction of “styles” in the Cinnamon desktop.
|
||||
|
||||
The next iteration of Cinnamon will present a novel concept known as “styles”. Styles come in three modes: mixed, dark, and light, and each of these modes can have colour “variants”. A variant is a combination of themes that blend together. The goal of styles, modes, and variants is to make it easier to switch to a beautiful-looking interface and quickly browse through available options. You won’t have to find matching elements because styles will do that for you, no matter how many individual themes you have installed.
|
||||
|
||||
In the style combo, you will see popular styles such as Adwaita, Mint-X, Mint-Y, among others. Just select one mode, and the colour variants will be displayed. You can switch between styles, modes, and colour variants with a few clicks of a button. However, if you prefer a specific combination of not proposed themes, you can click on “Advanced settings” and choose individual themes.
|
||||
|
||||
![Adwaita Cinnamon Style][1]
|
||||
|
||||
![Mint-Y Cinnamon Style][2]
|
||||
|
||||
![Mint-L Cinnamon Style][3]
|
||||
|
||||
The best part is that everything will work out of the box for everybody. The Linux Mint team wants Cinnamon styles to work well for everyone, including third-party theme artists and other distributions. Cinnamon styles are defined in JSON files in `/usr/share/cinnamon/styles.d/`. These files are read alphabetically, and styles can override one another if they have the same name. This approach lets distributions or theme artists define their styles while also using Cinnamon’s style definitions.
|
||||
|
||||
This entire feature should arrive as part of [Linux Mint 21.2 “Victoria” release][4], due on June 2023. If you want to contribute or want to develop your own theme for Mint, you can create a post in Mint forums.
|
||||
|
||||
Linux Mint users have long awaited this new feature and are sure to make the desktop environment more visually appealing and customizable. We can’t wait to see what the Linux Mint team has in store for us with the upcoming release!
|
||||
|
||||
Image credits: Linux Mint team, Via [announcement][5]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://debugpointnews.com/cinnamon-styles-announcement/
|
||||
|
||||
作者:[arindam][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://debugpointnews.com/author/dpicubegmail-com/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://debugpointnews.com/wp-content/uploads/2023/04/Adwaita-Cinnamon-Style.jpg
|
||||
[2]: https://debugpointnews.com/wp-content/uploads/2023/04/Mint-Y-Cinnamon-Style.jpg
|
||||
[3]: https://debugpointnews.com/wp-content/uploads/2023/04/Mint-L-Cinnamon-Style.jpg
|
||||
[4]: https://debugpointnews.com/linux-mint-21-2-announcement/
|
||||
[5]: https://blog.linuxmint.com/?p=4486
|
@ -0,0 +1,93 @@
|
||||
[#]: subject: "Color Variants & Styles Are Coming to Linux Mint 21.2"
|
||||
[#]: via: "https://news.itsfoss.com/color-styles-linux-mint/"
|
||||
[#]: author: "Ankush Das https://news.itsfoss.com/author/ankush/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Color Variants & Styles Are Coming to Linux Mint 21.2
|
||||
======
|
||||
|
||||
New visual elements and options for Linux Mint Cinnamon. What do you think?
|
||||
|
||||
![linux mint style][1]
|
||||
|
||||
![][2]
|
||||
|
||||
Recently, Linux Mint has been focusing on its look and feel more than usual.
|
||||
|
||||
With [Linux Mint 21.1][3], you get a new default theme and several subtle visual refinements.
|
||||
|
||||
And it looks like more such changes are in progress for the upcoming Linux Mint 21.2. As usual, a new monthly blog post highlighted some progress planned for the next release.
|
||||
|
||||
The highlight for me is the new concept, "**Styles,**" coming to the **Cinnamon desktop**.
|
||||
|
||||
### Cinnamon Upgrade With Linux Mint 21.2
|
||||
|
||||
Adding Styles to Cinnamon results in a simplified version of theme tweaks.
|
||||
|
||||
With the theme controls on Cinnamon, you must choose the mouse pointer, icon theme, and desktop theme separately.
|
||||
|
||||
![linux mint theme selection][4]
|
||||
|
||||
There are a lot of themes to select from. So, with the "Styles" approach, the tweaks get simplified to two things:
|
||||
|
||||
![linux mint style selection][5]
|
||||
|
||||
- **Style**
|
||||
- **Appearance**
|
||||
|
||||
The Style preset will feature options like Adwaita, Mint-X, Mint-Y, etc (the same options you find with the current theme selection).
|
||||
|
||||
![styles in linux mint][6]
|
||||
|
||||
For the appearance, you will choose the theme mode as dark/light or mixed as per your preferences.
|
||||
|
||||
The mixed mode will have both dark/light modes of apps for contrast, and the other modes are self-explanatory.
|
||||
|
||||
Once you have set these two options, you can find new color variants, combining themes. You can also play around with it to see what color combination works best for your style.
|
||||
|
||||
![linux mint yaru theme][7]
|
||||
|
||||
You can always find the current way of setting things under "**Advance settings**".
|
||||
|
||||
In addition, as shown in the screenshot above, you will find new color combinations available to add variety, even if the theme sounds similar.
|
||||
|
||||
Other changes expected with Linux Mint 21.2 include:
|
||||
|
||||
![][8]
|
||||
|
||||
- **Dropping the folder stripes**
|
||||
- **Removing monochrome icons and dark theme icons to replace them with symbolic icons to ensure compatibility and uniform contrast with all themes**
|
||||
|
||||
Sure, these simplifications of options for the end-user may not be massive overhauls but meaningful changes to improve the Cinnamon desktop experience overall.
|
||||
|
||||
Of course, it could become one of the [reasons you choose Cinnamon][9].
|
||||
|
||||
Not just limited to Linux Mint, these improvements will also help elevate the experience with any other distributions offering a Cinnamon desktop edition. You can head to Linux Mint's [official blog][10] to explore more about it.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://news.itsfoss.com/color-styles-linux-mint/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://news.itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://news.itsfoss.com/content/images/size/w1304/2023/04/mint-21-2-color-styles.png
|
||||
[2]: https://news.itsfoss.com/content/images/2023/03/linux-mega-packt.webp
|
||||
[3]: https://news.itsfoss.com/linux-mint-21-1-release/
|
||||
[4]: https://news.itsfoss.com/content/images/2023/04/linux-mint-theme-current.jpg
|
||||
[5]: https://news.itsfoss.com/content/images/2023/04/adwaita.png
|
||||
[6]: https://news.itsfoss.com/content/images/2023/04/mint-y-styles.png
|
||||
[7]: https://news.itsfoss.com/content/images/2023/04/yaru.png
|
||||
[8]: https://news.itsfoss.com/content/images/2023/04/nemo.png
|
||||
[9]: https://itsfoss.com/why-cinnamon/?ref=news.itsfoss.com
|
||||
[10]: https://blog.linuxmint.com/?ref=news.itsfoss.com
|
@ -0,0 +1,117 @@
|
||||
[#]: subject: "TUXEDO Stellaris 16 (Gen5) is The Ultimate Linux Laptop You Can Find Now"
|
||||
[#]: via: "https://news.itsfoss.com/tuxedo-stellaris-16-gen-5/"
|
||||
[#]: author: "Sourav Rudra https://news.itsfoss.com/author/sourav/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
TUXEDO Stellaris 16 (Gen5) is The Ultimate Linux Laptop You Can Find Now
|
||||
======
|
||||
|
||||
The laptop packs in a punch with specs
|
||||
like RTX 4090 and i9 processor! Don't you think?
|
||||
|
||||
![tuxedo stellar][1]
|
||||
|
||||
![][2]
|
||||
|
||||
TUXEDO Computers is a well-known brand in the Linux space that provide customizable Linux notebooks and Desktop PCs at various price/performance points.
|
||||
|
||||
Not to forget, it is one of the trusted places to [buy Linux computers][3].
|
||||
|
||||
With a recent announcement, they have launched the **next evolution of their Stellaris 16-inch laptop.**
|
||||
|
||||
I must say that this can be a proper challenger to Framework's recently announced high-performance 16-inch Laptop.
|
||||
|
||||
Let's take a look at it.
|
||||
|
||||
### TUXEDO Stellaris 16 Gen 5: Overview ⭐
|
||||
|
||||
![a photo of the tuxedo stellaris 16 - gen5 laptop][4]
|
||||
|
||||
The major highlight of this laptop is that it features Nvidia's latest and greatest [**RTX40 series**][5] of mobile GPUs that offer some great graphics performance and features.
|
||||
|
||||
Paired with Intel's top-of-the-line processor, the **[i9 13900HX][6]**, with its 24-cores that can turbo up to a **blazing fast 5.40 GHz** at a whopping **157 W TDP** (Turbo), it is a compelling package.
|
||||
|
||||
Though keep in mind; that for running the laptop at its full potential, TUXEDO recommends using **their external water cooling solution**, [TUXEDO Aquaris][7], which can be attached magnetically to the back of the laptop.
|
||||
|
||||
> 📋 You will have to pay extra for this when ordering the laptop.
|
||||
|
||||
The processor can run at a base TDP of **55 W** with a 3.90 GHz clock speed when it is not running at full blast.
|
||||
|
||||
That's still a decent performance figure for such a thin laptop! 😮
|
||||
|
||||
Some other key highlights of the Stellaris 16 – Gen5 laptop are as follows:
|
||||
|
||||
- **The chassis is made up of a combination of Aluminum and Plastics.**
|
||||
- **A 240 Hz 16-inch IPS display with Nvidia G-SYNC support.**
|
||||
- **GPU options up to an Nvidia RTX 4090.**
|
||||
- **Up to 64 GB DDR5, 5600 MHz RAM (2x 32 GB).**
|
||||
- **A 99 Wh Battery that allows for runtimes of up to 10 hours (idle), and around 6–7 hours under typical loads.**
|
||||
|
||||
### ⚡ TUXEDO Stellaris 16: What Makes it a Powerhouse?
|
||||
|
||||
The high-end hardware aboard the Stellaris 16 – Gen5 laptop won't only appeal to gamers but also to a wide variety of other users such as content creators, AI/ML engineers, UI/UX artists, and more.
|
||||
|
||||
**So, what makes it such a complete package?**
|
||||
|
||||
Well, it is the freedom of choosing from various possible setups.
|
||||
|
||||
For starters, you get to pick from a relatively calmer GPU in the form of an **RTX 4060**, all the way to the **insane RTX 4090,** with the **RTX 4070** and **RTX 4080** filling in the gaps.
|
||||
|
||||
![tuxedo stellaris laptop][8]
|
||||
|
||||
Then there are the keyboard options that let you decide between their newly added [Cherry MX Ultra-Low Profile][9] clicky switches for a tactile and audible feel during typing or the usual silent membrane switches.
|
||||
|
||||
You can also pick from an extensive **list of keyboard layouts** that include English, German, Spanish, Hungarian, and more with the backlit TUX super-key.
|
||||
|
||||
The RAM offerings are no less; you can decide between two performance tiers. One is a 'Performance' tier that offers **DDR5 RAM** running at a swift **4800 MHZ** and a 'High Performance' tier that offers DDR5 RAM running at an eye-watering **5600 MHz**.
|
||||
|
||||
These tiers offer a maximum of 64 GB RAM, mixing sticks from **SK Hynix**, **Samsung,** and **Micron**.
|
||||
|
||||
As for storage, two M.2 2280 SSD slots run on PCIe 4.0 x4 with various Samsung SSDs.
|
||||
|
||||
The lineup starts with a **Samsung 970 EVO Plus**, with the **Samsung 980** in the middle and the blazing-fast **Samsung 980 Pro** being the range-topper.
|
||||
|
||||
> 📋 A maximum of 4 TB of storage is possible with both the M.2 slots occupied.
|
||||
|
||||
Besides that, the Stellaris 16 – Gen5 features **Wi-Fi 6E** with **Bluetooth 5.3** and comes with the **[TUXEDO OS][10] pre-installed** unless you choose a different one at checkout.
|
||||
|
||||
The OS offerings include the likes of **Ubuntu 22.04 LTS**, **Kubuntu 22.04 LTS**, **Ubuntu Budgie 22.04 LTS,** and **Windows 11 Home/Pro**.
|
||||
|
||||
### 💸 Availability and Pricing
|
||||
|
||||
The TUXEDO Stellaris 16 – Gen5 is **available for pre-order**, with **deliveries starting at the end of April**.
|
||||
|
||||
Prices start at **1763,87 EUR for the base config** with the i9 processor, an RTX 4060, 16 GB of RAM (2x 8 GB) running at 5600MHZ, a 500 GB Samsung 980 SSD, and TUXEDO OS.
|
||||
|
||||
[Pre-Order][11]
|
||||
|
||||
Head over to the [official store listing][11], and start configuring as per your requirements.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://news.itsfoss.com/tuxedo-stellaris-16-gen-5/
|
||||
|
||||
作者:[Sourav Rudra][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://news.itsfoss.com/author/sourav/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://news.itsfoss.com/content/images/size/w1304/2023/04/tuxedo-stellaris-gen-5.png
|
||||
[2]: https://news.itsfoss.com/content/images/2023/03/linux-mega-packt.webp
|
||||
[3]: https://itsfoss.com/get-linux-laptops/?ref=news.itsfoss.com
|
||||
[4]: https://news.itsfoss.com/content/images/2023/04/TUXEDO_Stellaris_16-Gen5.jpg
|
||||
[5]: https://www.nvidia.com/en-us/geforce/laptops/?ref=news.itsfoss.com
|
||||
[6]: https://ark.intel.com/content/www/us/en/ark/products/232171/intel-core-i913900hx-processor-36m-cache-up-to-5-40-ghz.html?ref=news.itsfoss.com
|
||||
[7]: https://www.tuxedocomputers.com/en/Linux-Hardware/Accessories/Further-accessories/TUXEDO-Aquaris--External-Water-Cooling-Device_1.tuxedo?ref=news.itsfoss.com
|
||||
[8]: https://news.itsfoss.com/content/images/2023/04/stellaris16-gen5_back.jpg
|
||||
[9]: https://www.cherrymx.de/en/cherry-mx/mx-ultra-low-profile/mx-ulp-click.html?ref=news.itsfoss.com
|
||||
[10]: https://www.tuxedocomputers.com/os?ref=news.itsfoss.com
|
||||
[11]: https://www.tuxedocomputers.com/en/TUXEDO-Stellaris-16-Gen5.tuxedo?ref=news.itsfoss.com
|
@ -0,0 +1,86 @@
|
||||
[#]: subject: "Open source community analysis with actionable insights"
|
||||
[#]: via: "https://opensource.com/article/23/4/open-source-community-analysis-actionable-insights"
|
||||
[#]: author: "Cali Dolfi https://opensource.com/users/cdolfi"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Open source community analysis with actionable insights
|
||||
======
|
||||
|
||||
Organizations are increasingly adopting open source software development models and open source aspects of organizational culture. As a result, interest in how open source communities succeed is reaching an all-time high.
|
||||
|
||||
Until recent years, measuring the success of open source communities was haphazard and anecdotal. Ask someone what makes one community more successful than another, and you will likely get observations such as, "The software is great, so the community is too," or "The people in this community just mesh well." The problem with these evaluations is not that they are necessarily wrong, but that they don't provide information that others can use to reproduce successful results. What works for one community is not necessarily going to work for another.
|
||||
|
||||
Research universities, businesses, and other organizations interested in determining what makes open source projects successful have begun to collaborate on finding ways to measure aspects of community in a qualitative and data-driven way. One of the more prominent efforts is [CHAOSS][1], a Linux Foundation project focused on creating metrics, metrics models, and software to better understand open source community health on a global scale. Unhealthy projects hurt both their communities and the organizations relying on those projects, so identifying measures of robustness isn't just an interesting project. It's critical to the open source ecosystem.
|
||||
|
||||
CHAOSS is a great tool for looking at a pressing set of questions. First, how should [community health][2] be defined? Second, as metrics begin to take shape, how can we transition from reacting to one-off requests for data-based information about a given community to creating an entire process pipeline, literally and theoretically, for this work? The development of [Project Aspen][3] is the culmination of this pipeline, which will ultimately bring community data analysis to everyone.
|
||||
|
||||
### Collecting community data
|
||||
|
||||
In 2017, Harish Pillay created Prospector with the aim of presenting information from core data sources in a graphical dashboard. This resonated with [CHAOSS][1], which had a goal to better understand the health of open source communities. Prospector was donated to CHAOSS in 2017. Project Aspen builds upon that work.
|
||||
|
||||
Aspen is backed by a database generated from the [Augur Project][4], a CHAOSS-based project that collects, organizes, and validates the completeness of open source software trace data. With this database, we can store all types of data points around the Git-based repositories from which we collect data, such as pull requests, reviews, and contributors. The data is already collected and cleaned, which, from a data science perspective, is where the most significant time drains occur. The continued data collection allows us to act agilely when questions arise. Over time, we will grow our pipeline to collect data from many other avenues in addition to Git-based repositories, such as Stack Overflow and Reddit.
|
||||
|
||||
As Augur regularly collects data on our selected repositories, the data is updated within a week and cleaned. With all the data collection and most preprocessing already completed, we are much better equipped to answer the analysis questions we receive and generate our own questions too. No matter where the questions come from, the same analysis process is necessary.
|
||||
|
||||
For every visualization or analysis, community leaders need to consider these questions:
|
||||
|
||||
- What perspective are you looking to gain or give?
|
||||
- What question can you directly answer from the data available to you?
|
||||
- What assumptions am I making, and what biases may I hold?
|
||||
- Who can I work with to get feedback and a different perspective?
|
||||
|
||||
Everyone's individual experiences and expertise impact the lens through which they look at a problem. Some people have experience in code review, while others' expertise lies in community management. How can we start comparing community aspects like apples to apples instead of oranges? Quantifying what people in different roles in open source are looking at when examining a project community can address this problem.
|
||||
|
||||
Community metrics empower all members to communicate in a common domain and share their unique expertise. Different perspectives lead to further insights, and Project Aspen uses data to make those insights more accessible to the entire community through data visualizations.
|
||||
|
||||
### Assumptions and analysis
|
||||
|
||||
Analysis is a tool for narrative building, not an oracle. Data analysis can help take the ambiguity and bias out of inferences we make, but interpreting data is not simple. A bar chart showing an increase in commits over time is not, by itself, a positive indicator of community health. Nor is a stable or decreasing number always a negative sign. What any chart gives you is more information and areas to explore.
|
||||
|
||||
For instance, you could build from a commits-over-time visualization, creating a graph that plots the "depth" of a commit, perhaps defined as the number of line changes. Or you could dive into the specific work of your community to see what these trends actually represent.
|
||||
|
||||
Comparing an issues-over-time graph (**Figure 1**) to an issues staleness graph (**Figure 2**) is a great illustration of why perspective matters. These visualizations reflect the same data but reveal completely different insights. From the issue staleness graph, we can see not only how many issues are open, but how many have been open for various time intervals.
|
||||
|
||||
This figure shows that over many months, there's relative consistency in how many issues are opened and closed:
|
||||
|
||||
![A graph showing relative consistency in how many issues are opened and closed.][5]
|
||||
|
||||
On the other hand, this figure highlights the growing number of issues that have been open for over 30 days:
|
||||
|
||||
![A graph showing issues over the past 30 days.][6]
|
||||
|
||||
The same data populates each graph, but a fuller picture can only come from seeing both. By adding the perspective of the growth in issue staleness, communities can clearly see that there is a growing backlog of issues and take steps to understand what it means for their community. At that point, they will be well-equipped to devise a strategy and prioritize actions based on both good data and thoughtful analysis.
|
||||
|
||||
### Using data wisely
|
||||
|
||||
Including multiple points of view also provides much-needed insight and helps guard against false positives and gamification. Economists have a saying: "When a measure becomes a target, it ceases to be a good measure." In other words, measures used to reward performance create an incentive to manipulate measurement. As people learn which measures bring attention, money, or power, open source communities run the risk of encouraging actions taken just to play the system. Using multiple perspectives to define success will keep your metrics meaningful, so they have genuine value in maintaining your community.
|
||||
|
||||
To that end, Project Aspen is an exciting tool for building your own knowledge and making better decisions about communities. Whether you want to understand where your community is most vulnerable or the seasonality of activity within the community, having quality data to inform your analysis is essential. To see some of the work being done around community data analysis, please check out our [Git repositories][3] or the demo [8Knot][7] app instance.
|
||||
|
||||
_This article was originally published with [Red Hat Research Quarterly][8] and has been republished with the author's permission._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/23/4/open-source-community-analysis-actionable-insights
|
||||
|
||||
作者:[Cali Dolfi][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/cdolfi
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://chaoss.community/
|
||||
[2]: https://opensource.com/article/22/11/community-metrics
|
||||
[3]: https://github.com/oss-aspen
|
||||
[4]: https://github.com/chaoss/augur
|
||||
[5]: https://opensource.com/sites/default/files/2023-03/100000010000046C00000235638F4D10A6C6F100.webp
|
||||
[6]: https://opensource.com/sites/default/files/2023-03/100000010000046C0000025A21BB9C28E7B4B93C.webp
|
||||
[7]: https://eightknot.osci.io/
|
||||
[8]: https://research.redhat.com/blog/article/measuring-open-source-success-developing-analysis-for-actionable-insights/?intcmp=7013a000002qLH8AAM
|
@ -2,7 +2,7 @@
|
||||
[#]: via: "https://opensource.com/article/21/12/kdenlive-linux-creative-app"
|
||||
[#]: author: "Seth Kenlon https://opensource.com/users/seth"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: " "
|
||||
[#]: translator: "yjacks"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
@ -2,7 +2,7 @@
|
||||
[#]: via: "https://itsfoss.com/speek/"
|
||||
[#]: author: "Pratham Patel https://itsfoss.com/author/pratham/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: translator: "XiaotingHuang22"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
@ -87,7 +87,7 @@ via: https://itsfoss.com/speek/
|
||||
|
||||
作者:[Pratham Patel][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
译者:[XiaotingHuang22](https://github.com/XiaotingHuang22)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -2,7 +2,7 @@
|
||||
[#]: via: "https://opensource.com/article/22/8/css-html-project-documentation"
|
||||
[#]: author: "Jim Hall https://opensource.com/users/jim-hall"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: translator: "XiaotingHuang22"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
@ -265,7 +265,7 @@ via: https://opensource.com/article/22/8/css-html-project-documentation
|
||||
|
||||
作者:[Jim Hall][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
译者:[XiaotingHuang22](https://github.com/XiaotingHuang22)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -24,16 +24,20 @@ I’ve heard a million times about the dangers of floating point arithmetic, lik
|
||||
But I find all of this a little abstract on its own, and I really wanted some
|
||||
specific examples of floating point bugs in real-world programs.
|
||||
|
||||
So I [asked on Mastodon][1] for
|
||||
examples of how floating point has gone wrong for them in real programs, and as
|
||||
always folks delivered! Here are a bunch of examples. I’ve also written some
|
||||
example programs for some of them to see exactly what happens. Here’s a table of contents:
|
||||
So I [asked on Mastodon][1] for examples of how floating point has gone wrong for them in real programs, and as always folks delivered! Here are a bunch of examples. I’ve also written some example programs for some of them to see exactly what happens. Here’s a table of contents:
|
||||
|
||||
[how does floating point work?][2][floating point isn’t “bad” or random][3][example 1: the odometer that stopped][4][example 2: tweet IDs in Javascript][5][example 3: a variance calculation gone wrong][6][example 4: different languages sometimes do the same floating point calculation differently][7][example 5: the deep space kraken][8][example 6: the inaccurate timestamp][9][example 7: splitting a page into columns][10][example 8: collision checking][11]
|
||||
- [how does floating point work?][2]
|
||||
- [floating point isn’t “bad” or random][3]
|
||||
- [example 1: the odometer that stopped][4]
|
||||
- [example 2: tweet IDs in Javascript][5]
|
||||
- [example 3: a variance calculation gone wrong][6]
|
||||
- [example 4: different languages sometimes do the same floating point calculation differently][7]
|
||||
- [example 5: the deep space kraken][8]
|
||||
- [example 6: the inaccurate timestamp][9]
|
||||
- [example 7: splitting a page into columns][10]
|
||||
- [example 8: collision checking][11]
|
||||
|
||||
None of these 8 examples talk about NaNs or +0/-0 or infinity values or
|
||||
subnormals, but it’s not because those things don’t cause problems – it’s just
|
||||
that I got tired of writing at some point :).
|
||||
None of these 8 examples talk about NaNs or +0/-0 or infinity values or subnormals, but it’s not because those things don’t cause problems – it’s just that I got tired of writing at some point :).
|
||||
|
||||
Also I’ve probably made some mistakes in this post.
|
||||
|
||||
@ -45,35 +49,21 @@ I’m not going to write a long explanation of how floating point works in this
|
||||
|
||||
#### floating point isn’t “bad” or random
|
||||
|
||||
I don’t want you to read this post and conclude that floating point is bad.
|
||||
It’s an amazing tool for doing numerical calculations. So many smart people
|
||||
have done so much work to make numerical calculations on computers efficient and
|
||||
accurate! Two points about how all of this isn’t floating point’s fault:
|
||||
I don’t want you to read this post and conclude that floating point is bad. It’s an amazing tool for doing numerical calculations. So many smart people have done so much work to make numerical calculations on computers efficient and accurate! Two points about how all of this isn’t floating point’s fault:
|
||||
|
||||
- Doing numerical computations on a computer inherently involves
|
||||
some approximation and rounding, especially if you want to do it
|
||||
efficiently. You can’t always store an arbitrary amount of precision for
|
||||
- Doing numerical computations on a computer inherently involves some approximation and rounding, especially if you want to do it efficiently. You can’t always store an arbitrary amount of precision for
|
||||
every single number you’re working with.
|
||||
- Floating point is standardized (IEEE 754), so operations like addition on
|
||||
floating point numbers are deterministic – my understanding is that 0.1 +
|
||||
0.2 will always give you the exact same result (0.30000000000000004), even
|
||||
across different architectures. It might not be the result you _expected_,
|
||||
but it’s actually very predictable.
|
||||
- Floating point is standardized (IEEE 754), so operations like addition on floating point numbers are deterministic – my understanding is that 0.1 + 0.2 will always give you the exact same result (0.30000000000000004), even across different architectures. It might not be the result you _expected_, but it’s actually very predictable.
|
||||
|
||||
My goal for this post is just to explain what kind of problems can come up with
|
||||
floating point numbers and why they happen so that you know when to be
|
||||
careful with them, and when they’re not appropriate.
|
||||
My goal for this post is just to explain what kind of problems can come up with floating point numbers and why they happen so that you know when to be careful with them, and when they’re not appropriate.
|
||||
|
||||
Now let’s get into the examples.
|
||||
|
||||
#### example 1: the odometer that stopped
|
||||
|
||||
One person said that they were working on an odometer that was continuously
|
||||
adding small amounts to a 32-bit float to measure distance travelled, and
|
||||
things went very wrong.
|
||||
One person said that they were working on an odometer that was continuously adding small amounts to a 32-bit float to measure distance travelled, and things went very wrong.
|
||||
|
||||
To make this concrete, let’s say that we’re adding numbers to the odometer 1cm
|
||||
at a time. What does it look like after 10,000 kilometers?
|
||||
To make this concrete, let’s say that we’re adding numbers to the odometer 1cm at a time. What does it look like after 10,000 kilometers?
|
||||
|
||||
Here’s a C program that simulates that:
|
||||
|
||||
@ -101,10 +91,7 @@ This is VERY bad – it’s not a small error, 262km is a LOT less than 10,000km
|
||||
|
||||
#### what went wrong: gaps between floating point numbers get big
|
||||
|
||||
The problem in this case is that, for 32-bit floats, 262144.0 + 0.01 = 262144.0.
|
||||
So it’s not just that the number is inaccurate, it’ll actually never increase
|
||||
at all! If we travelled another 10,000 kilometers, the odometer would still be
|
||||
stuck at 262144 meters (aka 262.144km).
|
||||
The problem in this case is that, for 32-bit floats, 262144.0 + 0.01 = 262144.0. So it’s not just that the number is inaccurate, it’ll actually never increase at all! If we travelled another 10,000 kilometers, the odometer would still be stuck at 262144 meters (aka 262.144km).
|
||||
|
||||
Why is this happening? Well, floating point numbers get farther apart as they get bigger. In this example, for 32-bit floats, here are 3 consecutive floating point numbers:
|
||||
|
||||
@ -116,13 +103,9 @@ I got those numbers by going to [https://float.exposed/0x48800000][13] and incre
|
||||
|
||||
So, there are no 32-bit floating point numbers between 262144.0 and 262144.03125. Why is that a problem?
|
||||
|
||||
The problem is that 262144.03125 is about 262144.0 + 0.03. So when we try to
|
||||
add 0.01 to 262144.0, it doesn’t make sense to round up to the next number. So
|
||||
the sum just stays at 262144.0.
|
||||
The problem is that 262144.03125 is about 262144.0 + 0.03. So when we try to add 0.01 to 262144.0, it doesn’t make sense to round up to the next number. So the sum just stays at 262144.0.
|
||||
|
||||
Also, it’s not a coincidence that 262144 is a power of 2 (it’s 2^18). The gaps
|
||||
been floating point numbers change after every power of 2, and at 2^18 the gap
|
||||
between 32-bit floats is 0.03125, increasing from 0.016ish.
|
||||
Also, it’s not a coincidence that 262144 is a power of 2 (it’s 2^18). The gaps been floating point numbers change after every power of 2, and at 2^18 the gap between 32-bit floats is 0.03125, increasing from 0.016ish.
|
||||
|
||||
#### one way to solve this: use a double
|
||||
|
||||
@ -133,41 +116,26 @@ Expected: 10000.000000 km
|
||||
Got: 9999.999825 km
|
||||
```
|
||||
|
||||
There are still some small inaccuracies here – we’re off about 17 centimeters.
|
||||
Whether this matters or not depends on the context: being slightly off could very
|
||||
well be disastrous if we were doing a precision space maneuver or something, but
|
||||
it’s probably fine for an odometer.
|
||||
There are still some small inaccuracies here – we’re off about 17 centimeters. Whether this matters or not depends on the context: being slightly off could very well be disastrous if we were doing a precision space maneuver or something, but it’s probably fine for an odometer.
|
||||
|
||||
Another way to improve this would be to increment the odometer in bigger chunks
|
||||
– instead of adding 1cm at a time, maybe we could update it less frequently,
|
||||
like every 50cm.
|
||||
Another way to improve this would be to increment the odometer in bigger chunks – instead of adding 1cm at a time, maybe we could update it less frequently, like every 50cm.
|
||||
|
||||
If we use a double **and** increment by 50cm instead of 1cm, we get the exact
|
||||
correct answer:
|
||||
If we use a double **and** increment by 50cm instead of 1cm, we get the exact correct answer:
|
||||
|
||||
```
|
||||
Expected: 10000.000000 km
|
||||
Got: 10000.000000 km
|
||||
```
|
||||
|
||||
A third way to solve this could be to use an **integer**: maybe we decide that
|
||||
the smallest unit we care about is 0.1mm, and then measure everything as
|
||||
integer multiples of 0.1mm. I have never built an odometer so I can’t say what
|
||||
the best approach is.
|
||||
A third way to solve this could be to use an **integer**: maybe we decide that the smallest unit we care about is 0.1mm, and then measure everything as integer multiples of 0.1mm. I have never built an odometer so I can’t say what the best approach is.
|
||||
|
||||
#### example 2: tweet IDs in Javascript
|
||||
|
||||
Javascript only has floating point numbers – it doesn’t have an integer type.
|
||||
The biggest integer you can represent in a 64-bit floating point number is
|
||||
2^53.
|
||||
Javascript only has floating point numbers – it doesn’t have an integer type. The biggest integer you can represent in a 64-bit floating point number is 2^53.
|
||||
|
||||
But tweet IDs are big numbers, bigger than 2^53. The Twitter API now returns
|
||||
them as both integers and strings, so that in Javascript you can just use the
|
||||
string ID (like “1612850010110005250”), but if you tried to use the integer
|
||||
version in JS, things would go very wrong.
|
||||
But tweet IDs are big numbers, bigger than 2^53. The Twitter API now returns them as both integers and strings, so that in Javascript you can just use the string ID (like “1612850010110005250”), but if you tried to use the integer version in JS, things would go very wrong.
|
||||
|
||||
You can check this yourself by taking a tweet ID and putting it in the
|
||||
Javascript console, like this:
|
||||
You can check this yourself by taking a tweet ID and putting it in the Javascript console, like this:
|
||||
|
||||
```
|
||||
>> 1612850010110005250
|
||||
@ -176,8 +144,7 @@ Javascript console, like this:
|
||||
|
||||
Notice that 1612850010110005200 is NOT the same number as 1612850010110005250!! It’s 50 less!
|
||||
|
||||
This particular issue doesn’t happen in Python (or any other language that I
|
||||
know of), because Python has integers. Here’s what happens if we enter the same number in a Python REPL:
|
||||
This particular issue doesn’t happen in Python (or any other language that I know of), because Python has integers. Here’s what happens if we enter the same number in a Python REPL:
|
||||
|
||||
```
|
||||
In [3]: 1612850010110005250
|
||||
@ -188,14 +155,9 @@ Same number, as you’d expect.
|
||||
|
||||
#### example 2.1: the corrupted JSON data
|
||||
|
||||
This is a small variant of the “tweet IDs in Javascript” issue, but even if
|
||||
you’re _not_ actually writing Javascript code, numbers in JSON are still sometimes
|
||||
treated as if they’re floats. This mostly makes sense to me because JSON has
|
||||
“Javascript” in the name, so it seems reasonable to decode the values the way
|
||||
Javascript would.
|
||||
This is a small variant of the “tweet IDs in Javascript” issue, but even if you’re _not_ actually writing Javascript code, numbers in JSON are still sometimes treated as if they’re floats. This mostly makes sense to me because JSON has “Javascript” in the name, so it seems reasonable to decode the values the way Javascript would.
|
||||
|
||||
For example, if we pass some JSON through `jq`, we see the exact same issue:
|
||||
the number 1612850010110005250 gets changed into 1612850010110005200.
|
||||
For example, if we pass some JSON through `jq`, we see the exact same issue: the number 1612850010110005250 gets changed into 1612850010110005200.
|
||||
|
||||
```
|
||||
$ echo '{"id": 1612850010110005250}' | jq '.'
|
||||
@ -206,19 +168,13 @@ $ echo '{"id": 1612850010110005250}' | jq '.'
|
||||
|
||||
But it’s not consistent across all JSON libraries Python’s `json` module will decode `1612850010110005250` as the correct integer.
|
||||
|
||||
Several people mentioned issues with sending floats in JSON, whether either
|
||||
they were trying to send a large integer (like a pointer address) in JSON and
|
||||
it got corrupted, or sending smaller floating point values back and forth
|
||||
repeatedly and the value slowly diverging over time.
|
||||
Several people mentioned issues with sending floats in JSON, whether either they were trying to send a large integer (like a pointer address) in JSON and it got corrupted, or sending smaller floating point values back and forth repeatedly and the value slowly diverging over time.
|
||||
|
||||
#### example 3: a variance calculation gone wrong
|
||||
|
||||
Let’s say you’re doing some statistics, and you want to calculate the variance
|
||||
of many numbers. Maybe more numbers than you can easily fit in memory, so you
|
||||
want to do it in a single pass.
|
||||
Let’s say you’re doing some statistics, and you want to calculate the variance of many numbers. Maybe more numbers than you can easily fit in memory, so you want to do it in a single pass.
|
||||
|
||||
There’s a simple (but bad!!!) algorithm you can use to calculate the variance in a single pass,
|
||||
from [this blog post][14]. Here’s some Python code:
|
||||
There’s a simple (but bad!!!) algorithm you can use to calculate the variance in a single pass, from [this blog post][14]. Here’s some Python code:
|
||||
|
||||
```
|
||||
def calculate_bad_variance(nums):
|
||||
@ -246,7 +202,7 @@ Bad variance: 13.840000000000003 <- pretty close!
|
||||
Now, let’s try it the same 100,000 large numbers that are very close together (distributed between 100000000 and 100000000.06)
|
||||
|
||||
```
|
||||
In [7]: calculate_bad_variance(np.random.uniform(100000000, 100000000.06, 100000))
|
||||
In [7]: calculate_bad_variance(np.random.uniform(100000000, 100000000.06, 100000))
|
||||
Real variance: 0.00029959105209321173
|
||||
Bad variance: -138.93632 <- OH NO
|
||||
```
|
||||
@ -255,50 +211,27 @@ This is extremely bad: not only is the bad variance way off, it’s NEGATIVE! (t
|
||||
|
||||
#### what went wrong: catastrophic cancellation
|
||||
|
||||
What’s going here is similar to our odometer number problem: the
|
||||
`sum_of_squares` number gets extremely big (about 10^21 or 2^69), and at that point, the
|
||||
gap between consecutive floating point numbers is also very big – it’s 2**46.
|
||||
So we just lose all precision in our calculations.
|
||||
What’s going here is similar to our odometer number problem: the `sum_of_squares` number gets extremely big (about 10^21 or 2^69), and at that point, the gap between consecutive floating point numbers is also very big – it’s 2**46. So we just lose all precision in our calculations.
|
||||
|
||||
The term for this problem is “catastrophic cancellation” – we’re subtracting
|
||||
two very large floating point numbers which are both going to be pretty far
|
||||
from the correct value of the calculation, so the result of the subtraction is
|
||||
also going to be wrong.
|
||||
The term for this problem is “catastrophic cancellation” – we’re subtracting two very large floating point numbers which are both going to be pretty far from the correct value of the calculation, so the result of the subtraction is also going to be wrong. [The blog post I mentioned before][14]
|
||||
talks about a better algorithm people use to compute variance called Welford’s algorithm, which doesn’t have the catastrophic cancellation issue.
|
||||
|
||||
[The blog post I mentioned before][14]
|
||||
talks about a better algorithm people use to compute variance called
|
||||
Welford’s algorithm, which doesn’t have the catastrophic cancellation issue.
|
||||
|
||||
And of course, the solution for most people is to just use a scientific
|
||||
computing library like Numpy to calculate variance instead of trying to do it
|
||||
yourself :)
|
||||
And of course, the solution for most people is to just use a scientific computing library like Numpy to calculate variance instead of trying to do it yourself :)
|
||||
|
||||
#### example 4: different languages sometimes do the same floating point calculation differently
|
||||
|
||||
A bunch of people mentioned that different platforms will do the same
|
||||
calculation in different ways. One way this shows up in practice is – maybe
|
||||
you have some frontend code and some backend code that do the exact same
|
||||
floating point calculation. But it’s done slightly differently in Javascript
|
||||
and in PHP, so you users end up seeing discrepancies and getting confused.
|
||||
A bunch of people mentioned that different platforms will do the same calculation in different ways. One way this shows up in practice is – maybe you have some frontend code and some backend code that do the exact same floating point calculation. But it’s done slightly differently in Javascript and in PHP, so you users end up seeing discrepancies and getting confused.
|
||||
|
||||
In principle you might think that different implementations should work the
|
||||
same way because of the IEEE 754 standard for floating point, but here are a
|
||||
couple of caveats that were mentioned:
|
||||
In principle you might think that different implementations should work the same way because of the IEEE 754 standard for floating point, but here are a couple of caveats that were mentioned:
|
||||
|
||||
- math operations in libc (like sin/log) behave differently in different
|
||||
implementations. So code using glibc could give you different results than
|
||||
code using musl
|
||||
- some x86 instructions can use 80 bit precision for some double operations
|
||||
internally instead of 64 bit precision. [Here’s a GitHub issue talking about
|
||||
that][15]
|
||||
- math operations in libc (like sin/log) behave differently in different implementations. So code using glibc could give you different results than code using musl
|
||||
- some x86 instructions can use 80 bit precision for some double operations internally instead of 64 bit precision. [Here’s a GitHub issue talking about that][15]
|
||||
|
||||
I’m not very sure about these points and I don’t have concrete examples I can reproduce.
|
||||
|
||||
#### example 5: the deep space kraken
|
||||
|
||||
Kerbal Space Program is a space simulation game, and it used to have a bug
|
||||
called the [Deep Space Kraken][16] where when
|
||||
you moved very fast, your ship would start getting destroyed due to floating point issues. This is similar to the other problems we’ve talked out involving big floating numbers (like the variance problem), but I wanted to mention it because:
|
||||
Kerbal Space Program is a space simulation game, and it used to have a bug called the [Deep Space Kraken][16] where when you moved very fast, your ship would start getting destroyed due to floating point issues. This is similar to the other problems we’ve talked out involving big floating numbers (like the variance problem), but I wanted to mention it because:
|
||||
|
||||
- it has a funny name
|
||||
- it seems like a very common bug in video games / astrophysics / simulations in general – if you have points that are very far from the origin, your math gets messed up
|
||||
@ -307,32 +240,24 @@ Another example of this is the [Far Lands][17] in Minecraft.
|
||||
|
||||
#### example 6: the inaccurate timestamp
|
||||
|
||||
I promise this is the last example of “very large floating numbers can ruin your day”.
|
||||
But! Just one more! Let’s imagine that we try to represent the current Unix epoch in nanoseconds
|
||||
(about 1673580409000000000) as a 64-bit floating point number.
|
||||
I promise this is the last example of “very large floating numbers can ruin your day”. But! Just one more! Let’s imagine that we try to represent the current Unix epoch in nanoseconds (about 1673580409000000000) as a 64-bit floating point number.
|
||||
|
||||
This is no good! 1673580409000000000 is about 2^60 (crucially, bigger than 2^53), and the next 64-bit float after it is 1673580409000000256.
|
||||
|
||||
So this would be a great way to end up with inaccuracies in your time math. Of
|
||||
course, time libraries actually represent times as integers, so this isn’t
|
||||
usually a problem. (there’s always still the [year 2038 problem][18], but that’s not
|
||||
related to floats)
|
||||
So this would be a great way to end up with inaccuracies in your time math. Of course, time libraries actually represent times as integers, so this isn’t usually a problem. (there’s always still the [year 2038 problem][18], but that’s not related to floats)
|
||||
|
||||
In general, the lesson here is that sometimes it’s better to use integers.
|
||||
|
||||
#### example 7: splitting a page into columns
|
||||
|
||||
Now that we’ve talked about problems with big floating point numbers, let’s do
|
||||
a problem with small floating point numbers.
|
||||
Now that we’ve talked about problems with big floating point numbers, let’s do a problem with small floating point numbers.
|
||||
|
||||
Let’s say you have a page width, and a column width, and you want to figure out:
|
||||
|
||||
- how many columns fit on the page
|
||||
- how much space is left over
|
||||
|
||||
You might reasonably try `floor(page_width / column_width)` for the first
|
||||
question and `page_width % column_width` for the second question. Because
|
||||
that would work just fine with integers!
|
||||
You might reasonably try `floor(page_width / column_width)` for the first question and `page_width % column_width` for the second question. Because that would work just fine with integers!
|
||||
|
||||
```
|
||||
In [5]: math.floor(13.716 / 4.572)
|
||||
@ -344,21 +269,15 @@ Out[6]: 4.571999999999999
|
||||
|
||||
This is wrong! The amount of space left is 0!
|
||||
|
||||
A better way to calculate the amount of space left might have been
|
||||
`13.716 - 3 * 4.572`, which gives us a very small negative number.
|
||||
A better way to calculate the amount of space left might have been `13.716 - 3 * 4.572`, which gives us a very small negative number.
|
||||
|
||||
I think the lesson here is to never calculate the same thing in 2 different ways with floats.
|
||||
|
||||
This is a very basic example but I can kind of see how this would create all
|
||||
kinds of problems if I was doing page layout with floating point numbers, or
|
||||
doing CAD drawings.
|
||||
This is a very basic example but I can kind of see how this would create all kinds of problems if I was doing page layout with floating point numbers, or doing CAD drawings.
|
||||
|
||||
#### example 8: collision checking
|
||||
|
||||
Here’s a very silly Python program, that starts a variable at 1000 and
|
||||
decrements it until it collides with 0. You can imagine that this is part of a
|
||||
pong game or something, and that `a` is a ball that’s supposed to collide with
|
||||
a wall.
|
||||
Here’s a very silly Python program, that starts a variable at 1000 and decrements it until it collides with 0. You can imagine that this is part of a pong game or something, and that `a` is a ball that’s supposed to collide with a wall.
|
||||
|
||||
```
|
||||
a = 1000
|
||||
@ -366,21 +285,15 @@ while a != 0:
|
||||
a -= 0.001
|
||||
```
|
||||
|
||||
You might expect this program to terminate. But it doesn’t! `a` is never 0,
|
||||
instead it goes from 1.673494676862619e-08 to -0.0009999832650532314.
|
||||
You might expect this program to terminate. But it doesn’t! `a` is never 0, instead it goes from 1.673494676862619e-08 to -0.0009999832650532314.
|
||||
|
||||
The lesson here is that instead of checking for float equality, usually you
|
||||
want to check if two numbers are different by some very small amount. Or here
|
||||
we could just write `while a > 0`.
|
||||
The lesson here is that instead of checking for float equality, usually you want to check if two numbers are different by some very small amount. Or here we could just write `while a > 0`.
|
||||
|
||||
#### that’s all for now
|
||||
|
||||
I didn’t even get to NaNs (the are so many of them!) or infinity or +0 / -0 or subnormals, but we’ve
|
||||
already written 2000 words and I’m going to just publish this.
|
||||
I didn’t even get to NaNs (the are so many of them!) or infinity or +0 / -0 or subnormals, but we’ve already written 2000 words and I’m going to just publish this.
|
||||
|
||||
I might write another followup post later – that Mastodon thread has literally
|
||||
15,000 words of floating point problems in it, there’s a lot of material! Or I
|
||||
might not, who knows :)
|
||||
I might write another followup post later – that Mastodon thread has literally 15,000 words of floating point problems in it, there’s a lot of material! Or I might not, who knows :)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -12,18 +12,22 @@ Examples of problems with integers
|
||||
|
||||
Hello! A few days back we talked about [problems with floating point numbers][1].
|
||||
|
||||
This got me thinking – but what about integers? Of course integers have all
|
||||
kinds of problems too – anytime you represent a number in a small fixed amount of
|
||||
space (like 8/16/32/64 bits), you’re going to run into problems.
|
||||
This got me thinking – but what about integers? Of course integers have all kinds of problems too – anytime you represent a number in a small fixed amount of space (like 8/16/32/64 bits), you’re going to run into problems.
|
||||
|
||||
So I [asked on Mastodon again][2] for examples of integer problems and got all kinds of great responses again. Here’s a table of contents.
|
||||
|
||||
[example 1: the small database primary key][3][example 2: integer overflow/underflow][4][aside: how do computers represent negative integers?][5][example 3: decoding a binary format in Java][6][example 4: misinterpreting an IP address or string as an integer][7][example 5: security problems because of integer overflow][8][example 6: the case of the mystery byte order][9][example 7: modulo of negative numbers][10][example 8: compilers removing integer overflow checks][11][example 9: the && typo][12]
|
||||
- [example 1: the small database primary key][3]
|
||||
- [example 2: integer overflow/underflow][4]
|
||||
- [aside: how do computers represent negative integers?][5]
|
||||
- [example 3: decoding a binary format in Java][6]
|
||||
- [example 4: misinterpreting an IP address or string as an integer][7]
|
||||
- [example 5: security problems because of integer overflow][8]
|
||||
- [example 6: the case of the mystery byte order][9]
|
||||
- [example 7: modulo of negative numbers][10]
|
||||
- [example 8: compilers removing integer overflow checks][11]
|
||||
- [example 9: the && typo][12]
|
||||
|
||||
Like last time, I’ve written some example programs to demonstrate these
|
||||
problems. I’ve tried to use a variety of languages in the examples (Go,
|
||||
Javascript, Java, and C) to show that these problems don’t just show up in
|
||||
super low level C programs – integers are everywhere!
|
||||
Like last time, I’ve written some example programs to demonstrate these problems. I’ve tried to use a variety of languages in the examples (Go, Javascript, Java, and C) to show that these problems don’t just show up in super low level C programs – integers are everywhere!
|
||||
|
||||
Also I’ve probably made some mistakes in here, I learned several things while writing this.
|
||||
|
||||
@ -36,9 +40,7 @@ One of the most classic (and most painful!) integer problems is:
|
||||
- oh no!
|
||||
- You need to do a database migration to switch your primary key to be a 64-bit integer instead
|
||||
|
||||
If the primary key actually reaches its maximum value I’m not sure exactly what
|
||||
happens, I’d imagine you wouldn’t be able to create any new database rows and
|
||||
it would be a very bad day for your massively successful service.
|
||||
If the primary key actually reaches its maximum value I’m not sure exactly what happens, I’d imagine you wouldn’t be able to create any new database rows and it would be a very bad day for your massively successful service.
|
||||
|
||||
#### example 2: integer overflow/underflow
|
||||
|
||||
@ -87,20 +89,15 @@ Some brief notes about other languages:
|
||||
- In C, you can compile with `clang -fsanitize=unsigned-integer-overflow`. Then if your code has an overflow/underflow like this, the program will crash.
|
||||
- Similarly in Rust, if you compile your program in debug mode it’ll crash if there’s an integer overflow. But in release mode it won’t crash, it’ll just happily decide that 0 - 1 = 4294967295.
|
||||
|
||||
The reason Rust doesn’t check for overflows if you compile your program in
|
||||
release mode (and the reason C and Go don’t check) is that – these checks are
|
||||
expensive! Integer arithmetic is a very big part of many computations, and
|
||||
making sure that every single addition isn’t overflowing makes it slower.
|
||||
The reason Rust doesn’t check for overflows if you compile your program in release mode (and the reason C and Go don’t check) is that – these checks are expensive! Integer arithmetic is a very big part of many computations, and making sure that every single addition isn’t overflowing makes it slower.
|
||||
|
||||
#### aside: how do computers represent negative integers?
|
||||
|
||||
I mentioned in the last section that `0xFFFFFFFF` can mean either `-1` or
|
||||
`4294967295`. You might be thinking – what??? Why would `0xFFFFFFFF` mean `-1`?
|
||||
I mentioned in the last section that `0xFFFFFFFF` can mean either `-1` or `4294967295`. You might be thinking – what??? Why would `0xFFFFFFFF` mean `-1`?
|
||||
|
||||
So let’s talk about how computers represent negative integers for a second.
|
||||
|
||||
I’m going to simplify and talk about 8-bit integers instead of 32-bit integers,
|
||||
because there are less of them and it works basically the same way.
|
||||
I’m going to simplify and talk about 8-bit integers instead of 32-bit integers, because there are less of them and it works basically the same way.
|
||||
|
||||
You can represent 256 different numbers with an 8-bit integer: 0 to 255
|
||||
|
||||
@ -112,9 +109,7 @@ You can represent 256 different numbers with an 8-bit integer: 0 to 255
|
||||
11111111 -> 255
|
||||
```
|
||||
|
||||
But what if you want to represent _negative_ integers? We still only have 8
|
||||
bits! So we need to reassign some of these and treat them as negative numbers
|
||||
instead.
|
||||
But what if you want to represent _negative_ integers? We still only have 8 bits! So we need to reassign some of these and treat them as negative numbers instead.
|
||||
|
||||
Here’s the way most modern computers do it:
|
||||
|
||||
@ -147,9 +142,7 @@ That’s how we end up with `0xFFFFFFFF` meaning -1.
|
||||
|
||||
#### there are multiple ways to represent negative integers
|
||||
|
||||
The way we just talked about of representing negative integers (“it’s the equivalent positive integer, but you subtract 2^n”) is called
|
||||
**two’s complement**, and it’s the most common on modern computers. There are several other ways
|
||||
though, the [wikipedia article has a list][14].
|
||||
The way we just talked about of representing negative integers (“it’s the equivalent positive integer, but you subtract 2^n”) is called **two’s complement**, and it’s the most common on modern computers. There are several other ways though, the [wikipedia article has a list][14].
|
||||
|
||||
#### weird thing: the absolute value of -128 is negative
|
||||
|
||||
@ -182,16 +175,13 @@ This prints out:
|
||||
-128
|
||||
```
|
||||
|
||||
This is because the signed 8-bit integers go from -128 to 127 – there **is** no +128!
|
||||
Some programs might crash when you try to do this (it’s an overflow), but Go
|
||||
doesn’t.
|
||||
This is because the signed 8-bit integers go from -128 to 127 – there **is** no +128! Some programs might crash when you try to do this (it’s an overflow), but Go doesn’t.
|
||||
|
||||
Now that we’ve talked about signed integers a bunch, let’s dig into another example of how they can cause problems.
|
||||
|
||||
#### example 3: decoding a binary format in Java
|
||||
|
||||
Let’s say you’re parsing a binary format in Java, and you want to get the first
|
||||
4 bits of the byte `0x90`. The correct answer is 9.
|
||||
Let’s say you’re parsing a binary format in Java, and you want to get the first 4 bits of the byte `0x90`. The correct answer is 9.
|
||||
|
||||
```
|
||||
public class Main {
|
||||
@ -222,9 +212,7 @@ Let’s break down what those two facts mean for our little calculation `b >> 4`
|
||||
|
||||
#### what can you do about it?
|
||||
|
||||
I don’t the actual idiomatic way to do this in Java is, but the way I’d naively
|
||||
approach fixing this is to put in a bit mask before doing the right shift. So
|
||||
instead of:
|
||||
I don’t the actual idiomatic way to do this in Java is, but the way I’d naively approach fixing this is to put in a bit mask before doing the right shift. So instead of:
|
||||
|
||||
```
|
||||
b >> 4
|
||||
@ -238,20 +226,15 @@ we’d write
|
||||
|
||||
`b & 0xFF` seems redundant (`b` is already a byte!), but it’s actually not because `b` is being promoted to an integer.
|
||||
|
||||
Now instead of `0x90 -> 0xFFFFFF90 -> 0xFFFFFFF9`, we end up calculating `0x90 -> 0xFFFFFF90 -> 0x00000090 -> 0x00000009`, which is the result we wanted: 9.
|
||||
Now instead of `0x90 -> 0xFFFFFF90 -> 0xFFFFFFF9`, we end up calculating `0x90 -> 0xFFFFFF90 -> 0x00000090 -> x00000009`, which is the result we wanted: 9.
|
||||
|
||||
And when we actually try it, it prints out “9”.
|
||||
|
||||
Also, if we were using a language with unsigned integers, the natural way to
|
||||
deal with this would be to treat the value as an unsigned integer in the first
|
||||
place. But that’s not possible in Java.
|
||||
Also, if we were using a language with unsigned integers, the natural way to deal with this would be to treat the value as an unsigned integer in the first place. But that’s not possible in Java.
|
||||
|
||||
#### example 4: misinterpreting an IP address or string as an integer
|
||||
|
||||
I don’t know if this is technically a “problem with integers” but it’s funny
|
||||
so I’ll mention it: [Rachel by the bay][16] has a bunch of great
|
||||
examples of things that are not integers being interpreted as integers. For
|
||||
example, “HTTP” is `0x48545450` and `2130706433` is `127.0.0.1`.
|
||||
I don’t know if this is technically a “problem with integers” but it’s funny so I’ll mention it: [Rachel by the bay][16] has a bunch of great examples of things that are not integers being interpreted as integers. For example, “HTTP” is `0x48545450` and `2130706433` is `127.0.0.1`.
|
||||
|
||||
She points out that you can actually ping any integer, and it’ll convert that integer into an IP address, for example:
|
||||
|
||||
@ -266,8 +249,7 @@ PING 132848123841239999988888888888234234234234234234 (251.164.101.122): 56 data
|
||||
|
||||
#### example 5: security problems because of integer overflow
|
||||
|
||||
Another integer overflow example: here’s a [search for CVEs involving integer overflows][17].
|
||||
There are a lot! I’m not a security person, but here’s one random example: this [json parsing library bug][18]
|
||||
Another integer overflow example: here’s a [search for CVEs involving integer overflows][17]. There are a lot! I’m not a security person, but here’s one random example: this [json parsing library bug][18]
|
||||
|
||||
My understanding of that json parsing bug is roughly:
|
||||
|
||||
@ -276,40 +258,25 @@ My understanding of that json parsing bug is roughly:
|
||||
- but the JSON file is still 3GB, so it gets copied into the tiny buffer with almost 0 bytes of memory
|
||||
- this overwrites all kinds of other memory that it’s not supposed to
|
||||
|
||||
The CVE says “This vulnerability mostly impacts process availability”, which I
|
||||
think means “the program crashes”, but sometimes this kind of thing is much
|
||||
worse and can result in arbitrary code execution.
|
||||
The CVE says “This vulnerability mostly impacts process availability”, which I think means “the program crashes”, but sometimes this kind of thing is much worse and can result in arbitrary code execution.
|
||||
|
||||
My impression is that there are a large variety of different flavours of
|
||||
security vulnerabilities caused by integer overflows.
|
||||
My impression is that there are a large variety of different flavours of security vulnerabilities caused by integer overflows.
|
||||
|
||||
#### example 6: the case of the mystery byte order
|
||||
|
||||
One person said that they’re do scientific computing and sometimes they need to
|
||||
read files which contain data with an unknown byte order.
|
||||
One person said that they’re do scientific computing and sometimes they need to read files which contain data with an unknown byte order.
|
||||
|
||||
Let’s invent a small example of this: say you’re reading a file which contains 4
|
||||
bytes - `00`, `00`, `12`, and `81` (in that order), that you happen to know
|
||||
represent a 4-byte integer. There are 2 ways to interpret that integer:
|
||||
Let’s invent a small example of this: say you’re reading a file which contains 4 bytes - `00`, `00`, `12`, and `81` (in that order), that you happen to know represent a 4-byte integer. There are 2 ways to interpret that integer:
|
||||
|
||||
- `0x00001281` (which translates to 4737). This order is called “big endian”
|
||||
- `0x81120000` (which translates to 2165440512). This order is called “little endian”.
|
||||
|
||||
Which one is it? Well, maybe the file contains some metadata that specifies the
|
||||
endianness. Or maybe you happen to know what machine it was generated on and
|
||||
what byte order that machine uses. Or maybe you just read a bunch of values,
|
||||
try both orders, and figure out which makes more sense. Maybe 2165440512 is too
|
||||
big to make sense in the context of whatever your data is supposed to mean, or
|
||||
maybe `4737` is too small.
|
||||
Which one is it? Well, maybe the file contains some metadata that specifies the endianness. Or maybe you happen to know what machine it was generated on and what byte order that machine uses. Or maybe you just read a bunch of values, try both orders, and figure out which makes more sense. Maybe 2165440512 is too big to make sense in the context of whatever your data is supposed to mean, or maybe `4737` is too small.
|
||||
|
||||
A couple more notes on this:
|
||||
|
||||
- this isn’t just a problem with integers, floating point numbers have byte
|
||||
order too
|
||||
- this also comes up when reading data from a network, but in that case the
|
||||
byte order isn’t a “mystery”, it’s just going to be big endian. But x86
|
||||
machines (and many others) are little endian, so you have to swap the byte
|
||||
order of all your numbers.
|
||||
- this isn’t just a problem with integers, floating point numbers have byte order too
|
||||
- this also comes up when reading data from a network, but in that case the byte order isn’t a “mystery”, it’s just going to be big endian. But x86 machines (and many others) are little endian, so you have to swap the byte order of all your numbers.
|
||||
|
||||
#### example 7: modulo of negative numbers
|
||||
|
||||
@ -317,17 +284,13 @@ This is more of a design decision about how different programming languages desi
|
||||
|
||||
Let’s say you write `-13 % 3` in your program, or `13 % -3`. What’s the result?
|
||||
|
||||
It turns out that different programming languages do it differently, for
|
||||
example in Python `-13 % 3 = 2` but in Javascript `-13 % 3 = -1`.
|
||||
It turns out that different programming languages do it differently, for example in Python `-13 % 3 = 2` but in Javascript `-13 % 3 = -1`.
|
||||
|
||||
There’s a table in [this blog post][19] that
|
||||
describes a bunch of different programming languages’ choices.
|
||||
There’s a table in [this blog post][19] that describes a bunch of different programming languages’ choices.
|
||||
|
||||
#### example 8: compilers removing integer overflow checks
|
||||
|
||||
We’ve been hearing a lot about integer overflow and why it’s bad. So let’s
|
||||
imagine you try to be safe and include some checks in your programs – after
|
||||
each addition, you make sure that the calculation didn’t overflow. Like this:
|
||||
We’ve been hearing a lot about integer overflow and why it’s bad. So let’s imagine you try to be safe and include some checks in your programs – after each addition, you make sure that the calculation didn’t overflow. Like this:
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
@ -356,39 +319,26 @@ $ gcc -O3 check_overflow.c -o check_overflow && ./check_overflow
|
||||
0
|
||||
```
|
||||
|
||||
That’s weird – when we compile with `gcc`, we get the answer we expected, but
|
||||
with `gcc -O3`, we get a different answer. Why?
|
||||
That’s weird – when we compile with `gcc`, we get the answer we expected, but with `gcc -O3`, we get a different answer. Why?
|
||||
|
||||
#### what’s going on?
|
||||
|
||||
My understanding (which might be wrong) is:
|
||||
|
||||
- Signed integer overflow in C is **undefined behavior**. I think that’s
|
||||
because different C implementations might be using different representations
|
||||
of signed integers (maybe they’re using one’s complement instead of two’s
|
||||
complement or something)
|
||||
- Signed integer overflow in C is **undefined behavior**. I think that’s because different C implementations might be using different representations of signed integers (maybe they’re using one’s complement instead of two’s complement or something)
|
||||
- “undefined behaviour” in C means “the compiler is free to do literally whatever it wants after that point” (see this post [With undefined behaviour, anything is possible][20] by Raph Levine for a lot more)
|
||||
- Some compiler optimizations assume that undefined behaviour will never
|
||||
happen. They’re free to do this, because – if that undefined behaviour
|
||||
_did_ happen, then they’re allowed to do whatever they want, so “run the
|
||||
code that I optimized assuming that this would never happen” is fine.
|
||||
- So this `if (n + 100 < 0)` check is irrelevant – if that did
|
||||
happen, it would be undefined behaviour, so there’s no need to execute the
|
||||
contents of that if statement.
|
||||
- Some compiler optimizations assume that undefined behaviour will never happen. They’re free to do this, because – if that undefined behaviour _did_ happen, then they’re allowed to do whatever they want, so “run the code that I optimized assuming that this would never happen” is fine.
|
||||
- So this `if (n + 100 < 0)` check is irrelevant – if that did happen, it would be undefined behaviour, so there’s no need to execute the contents of that if statement.
|
||||
|
||||
So, that’s weird. I’m not going to write a “what can you do about it?” section here because I’m pretty out of my depth already.
|
||||
|
||||
I certainly would not have expected that though.
|
||||
|
||||
My impression is that “undefined behaviour” is really a C/C++ concept, and
|
||||
doesn’t exist in other languages in the same way except in the case of “your
|
||||
program called some C code in an incorrect way and that C code did something
|
||||
weird because of undefined behaviour”. Which of course happens all the time.
|
||||
My impression is that “undefined behaviour” is really a C/C++ concept, and doesn’t exist in other languages in the same way except in the case of “your program called some C code in an incorrect way and that C code did something weird because of undefined behaviour”. Which of course happens all the time.
|
||||
|
||||
#### example 9: the && typo
|
||||
|
||||
This one was mentioned as a very upsetting bug. Let’s say you have two integers
|
||||
and you want to check that they’re both nonzero.
|
||||
This one was mentioned as a very upsetting bug. Let’s say you have two integers and you want to check that they’re both nonzero.
|
||||
|
||||
In Javascript, you might write:
|
||||
|
||||
@ -406,9 +356,7 @@ if a & b {
|
||||
}
|
||||
```
|
||||
|
||||
This is still perfectly valid code, but it means something completely different
|
||||
– it’s a bitwise and instead of a boolean and. Let’s go into a Javascript
|
||||
console and look at bitwise vs boolean and for `9` and `4`:
|
||||
This is still perfectly valid code, but it means something completely different – it’s a bitwise and instead of a boolean and. Let’s go into a Javascript console and look at bitwise vs boolean and for `9` and `4`:
|
||||
|
||||
```
|
||||
> 9 && 4
|
||||
@ -421,20 +369,15 @@ console and look at bitwise vs boolean and for `9` and `4`:
|
||||
4
|
||||
```
|
||||
|
||||
It’s easy to imagine this turning into a REALLY annoying bug since it would be
|
||||
intermittent – often `x & y` does turn out to be truthy if `x && y` is truthy.
|
||||
It’s easy to imagine this turning into a REALLY annoying bug since it would be intermittent – often `x & y` does turn out to be truthy if `x && y` is truthy.
|
||||
|
||||
#### what to do about it?
|
||||
|
||||
For Javascript, ESLint has a [no-bitwise check][21] check), which
|
||||
requires you manually flag “no, I actually know what I’m doing, I want to do
|
||||
bitwise and” if you use a bitwise and in your code. I’m sure many other linters
|
||||
have a similar check.
|
||||
For Javascript, ESLint has a [no-bitwise check][21] check), which requires you manually flag “no, I actually know what I’m doing, I want to do bitwise and” if you use a bitwise and in your code. I’m sure many other linters have a similar check.
|
||||
|
||||
#### that’s all for now!
|
||||
|
||||
There are definitely more problems with integers than this, but this got pretty
|
||||
long again and I’m tired of writing again so I’m going to stop :)
|
||||
There are definitely more problems with integers than this, but this got pretty long again and I’m tired of writing again so I’m going to stop :)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -10,26 +10,16 @@
|
||||
Why does 0.1 + 0.2 = 0.30000000000000004?
|
||||
======
|
||||
|
||||
Hello! I was trying to write about floating point yesterday,
|
||||
and I found myself wondering about this calculation, with 64-bit floats:
|
||||
Hello! I was trying to write about floating point yesterday, and I found myself wondering about this calculation, with 64-bit floats:
|
||||
|
||||
```
|
||||
>>> 0.1 + 0.2
|
||||
0.30000000000000004
|
||||
```
|
||||
|
||||
I realized that I didn’t understand exactly how it worked. I mean, I know
|
||||
floating point calculations are inexact, and I know that you can’t exactly
|
||||
represent `0.1` in binary, but: there’s a floating point number that’s closer to
|
||||
0.3 than `0.30000000000000004`! So why do we get the answer
|
||||
`0.30000000000000004`?
|
||||
I realized that I didn’t understand exactly how it worked. I mean, I know floating point calculations are inexact, and I know that you can’t exactly represent `0.1` in binary, but: there’s a floating point number that’s closer to 0.3 than `0.30000000000000004`! So why do we get the answer `0.30000000000000004`?
|
||||
|
||||
If you don’t feel like reading this whole post with a bunch of calculations, the short answer is that
|
||||
`0.1000000000000000055511151231257827021181583404541015625 + 0.200000000000000011102230246251565404236316680908203125` lies exactly between
|
||||
2 floating point numbers,
|
||||
`0.299999999999999988897769753748434595763683319091796875` (usually printed as `0.3`) and
|
||||
`0.3000000000000000444089209850062616169452667236328125` (usually printed as `0.30000000000000004`). The answer is
|
||||
`0.30000000000000004` (the second one) because its significand is even.
|
||||
If you don’t feel like reading this whole post with a bunch of calculations, the short answer is that `0.1000000000000000055511151231257827021181583404541015625 + 0.200000000000000011102230246251565404236316680908203125` lies exactly between 2 floating point numbers, `0.299999999999999988897769753748434595763683319091796875` (usually printed as `0.3`) and `0.3000000000000000444089209850062616169452667236328125` (usually printed as `0.30000000000000004`). The answer is `0.30000000000000004` (the second one) because its significand is even.
|
||||
|
||||
#### how floating point addition works
|
||||
|
||||
@ -38,9 +28,7 @@ This is roughly how floating point addition works:
|
||||
- Add together the numbers (with extra precision)
|
||||
- Round the result to the nearest floating point number
|
||||
|
||||
So let’s use these rules to calculate 0.1 + 0.2. I just learned how floating
|
||||
point addition works yesterday so it’s possible I’ve made some mistakes in this
|
||||
post, but I did get the answers I expected at the end.
|
||||
So let’s use these rules to calculate 0.1 + 0.2. I just learned how floating point addition works yesterday so it’s possible I’ve made some mistakes in this post, but I did get the answers I expected at the end.
|
||||
|
||||
#### step 1: find out what 0.1 and 0.2 are
|
||||
|
||||
@ -53,9 +41,7 @@ First, let’s use Python to figure out what the exact values of `0.1` and `0.2`
|
||||
'0.20000000000000001110223024625156540423631668090820312500000000000000000000000000'
|
||||
```
|
||||
|
||||
These really are the exact values: because floating point numbers are in base
|
||||
2, you can represent them all exactly in base 10. You just need a lot of digits
|
||||
sometimes :)
|
||||
These really are the exact values: because floating point numbers are in base 2, you can represent them all exactly in base 10. You just need a lot of digits sometimes :)
|
||||
|
||||
#### step 2: add the numbers together
|
||||
|
||||
@ -79,8 +65,7 @@ Now, let’s look at the floating point numbers around `0.3`. Here’s the close
|
||||
'0.29999999999999998889776975374843459576368331909179687500000000000000000000000000'
|
||||
```
|
||||
|
||||
We can figure out the next floating point number after `0.3` by serializing
|
||||
`0.3` to 8 bytes with `struct.pack`, adding 1, and then using `struct.unpack`:
|
||||
We can figure out the next floating point number after `0.3` by serializing `0.3` to 8 bytes with `struct.pack`, adding 1, and then using `struct.unpack`:
|
||||
|
||||
```
|
||||
>>> struct.pack("!d", 0.3)
|
||||
@ -100,17 +85,13 @@ Apparently you can also do this with `math.nextafter`:
|
||||
0.30000000000000004
|
||||
```
|
||||
|
||||
So the two 64-bit floats around
|
||||
`0.3` are
|
||||
`0.299999999999999988897769753748434595763683319091796875` and
|
||||
So the two 64-bit floats around `0.3` are `0.299999999999999988897769753748434595763683319091796875` and
|
||||
`0.3000000000000000444089209850062616169452667236328125`
|
||||
|
||||
#### step 4: find out which one is closest to our result
|
||||
|
||||
It turns out that `0.3000000000000000166533453693773481063544750213623046875`
|
||||
is exactly in the middle of
|
||||
`0.299999999999999988897769753748434595763683319091796875` and
|
||||
`0.3000000000000000444089209850062616169452667236328125`.
|
||||
It turns out that `0.3000000000000000166533453693773481063544750213623046875` is exactly in the middle of
|
||||
`0.299999999999999988897769753748434595763683319091796875` and `0.3000000000000000444089209850062616169452667236328125`.
|
||||
|
||||
You can see that with this calculation:
|
||||
|
||||
@ -123,10 +104,7 @@ So neither of them is closest.
|
||||
|
||||
#### how does it know which one to round to?
|
||||
|
||||
In the binary representation of a floating point number, there’s a number
|
||||
called the “significand”. In cases like this (where the result is exactly in
|
||||
between 2 successive floating point number, it’ll round to the one with the
|
||||
even significand.
|
||||
In the binary representation of a floating point number, there’s a number called the “significand”. In cases like this (where the result is exactly in between 2 successive floating point number, it’ll round to the one with the even significand.
|
||||
|
||||
In this case that’s `0.300000000000000044408920985006261616945266723632812500`
|
||||
|
||||
@ -135,20 +113,13 @@ We actually saw the significand of this number a bit earlier:
|
||||
- 0.30000000000000004 is `struct.unpack('!d', b'?\xd3333334')`
|
||||
- 0.3 is `struct.unpack('!d', b'?\xd3333333')`
|
||||
|
||||
The last digit of the big endian hex representation of `0.30000000000000004` is
|
||||
`4`, so that’s the one with the even significand (because the significand is at
|
||||
the end).
|
||||
The last digit of the big endian hex representation of `0.30000000000000004` is `4`, so that’s the one with the even significand (because the significand is at the end).
|
||||
|
||||
#### let’s also work out the whole calculation in binary
|
||||
|
||||
Above we did the calculation in decimal, because that’s a little more intuitive
|
||||
to read. But of course computers don’t do these calculations in decimal –
|
||||
they’re done in a base 2 representation. So I wanted to get an idea of how that
|
||||
worked too.
|
||||
Above we did the calculation in decimal, because that’s a little more intuitive to read. But of course computers don’t do these calculations in decimal – they’re done in a base 2 representation. So I wanted to get an idea of how that worked too.
|
||||
|
||||
I don’t think this binary calculation part of the post is particularly clear
|
||||
but it was helpful for me to write out. There are a really a lot of numbers and
|
||||
it might be terrible to read.
|
||||
I don’t think this binary calculation part of the post is particularly clear but it was helpful for me to write out. There are a really a lot of numbers and it might be terrible to read.
|
||||
|
||||
#### how 64-bit floats numbers work: exponent and significand
|
||||
|
||||
@ -181,11 +152,9 @@ def get_significand(f):
|
||||
return x ^ (exponent << 52)
|
||||
```
|
||||
|
||||
I’m ignoring the sign bit (the first bit) because we only need these functions
|
||||
to work on two numbers (0.1 and 0.2) and those two numbers are both positive.
|
||||
I’m ignoring the sign bit (the first bit) because we only need these functions to work on two numbers (0.1 and 0.2) and those two numbers are both positive.
|
||||
|
||||
First, let’s get the exponent and significand of 0.1. We need to subtract 1023
|
||||
to get the actual exponent because that’s how floating point works.
|
||||
First, let’s get the exponent and significand of 0.1. We need to subtract 1023 to get the actual exponent because that’s how floating point works.
|
||||
|
||||
```
|
||||
>>> get_exponent(0.1) - 1023
|
||||
@ -203,9 +172,7 @@ Here’s that calculation in Python:
|
||||
0.1
|
||||
```
|
||||
|
||||
(you might legitimately be worried about floating point accuracy issues with
|
||||
this calculation, but in this case I’m pretty sure it’s fine because these
|
||||
numbers by definition don’t have accuracy issues – the floating point numbers starting at `2**-4` go up in steps of `1/2**(52 + 4)`)
|
||||
(you might legitimately be worried about floating point accuracy issues with this calculation, but in this case I’m pretty sure it’s fine because these numbers by definition don’t have accuracy issues – the floating point numbers starting at `2**-4` go up in steps of `1/2**(52 + 4)`)
|
||||
|
||||
We can do the same thing for `0.2`:
|
||||
|
||||
@ -309,10 +276,7 @@ That’s the answer we expected:
|
||||
|
||||
#### this probably isn’t exactly how it works in hardware
|
||||
|
||||
The way I’ve described the operations here isn’t literally exactly
|
||||
what happens when you do floating point addition (it’s not “solving for X” for
|
||||
example), I’m sure there are a lot of efficient tricks. But I think it’s about
|
||||
the same idea.
|
||||
The way I’ve described the operations here isn’t literally exactly what happens when you do floating point addition (it’s not “solving for X” for example), I’m sure there are a lot of efficient tricks. But I think it’s about the same idea.
|
||||
|
||||
#### printing out floating point numbers is pretty weird
|
||||
|
||||
@ -325,48 +289,31 @@ We said earlier that the floating point number 0.3 isn’t equal to 0.3. It’s
|
||||
|
||||
So when you print out that number, why does it display `0.3`?
|
||||
|
||||
The computer isn’t actually printing out the exact value of the number, instead
|
||||
it’s printing out the _shortest_ decimal number `d` which has the property that
|
||||
our floating point number `f` is the closest floating point number to `d`.
|
||||
The computer isn’t actually printing out the exact value of the number, instead it’s printing out the _shortest_ decimal number `d` which has the property that our floating point number `f` is the closest floating point number to `d`.
|
||||
|
||||
It turns out that doing this efficiently isn’t trivial at all, and there are a bunch of academic papers about it like [Printing Floating-Point Numbers Quickly and Accurately][1]. or [How to print floating point numbers accurately][2].
|
||||
|
||||
#### would it be more intuitive if computers printed out the exact value of a float?
|
||||
|
||||
Rounding to a nice clean decimal value is nice, but in a way I feel like it
|
||||
might be more intuitive if computers just printed out the exact value of a
|
||||
floating point number – it might make it seem a lot less surprising when you
|
||||
get weird results.
|
||||
Rounding to a nice clean decimal value is nice, but in a way I feel like it might be more intuitive if computers just printed out the exact value of a floating point number – it might make it seem a lot less surprising when you get weird results.
|
||||
|
||||
To me,
|
||||
0.1000000000000000055511151231257827021181583404541015625 +
|
||||
0.200000000000000011102230246251565404236316680908203125
|
||||
= 0.3000000000000000444089209850062616169452667236328125 feels less surprising than 0.1 + 0.2 = 0.30000000000000004.
|
||||
To me, 0.1000000000000000055511151231257827021181583404541015625 + 0.200000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125 feels less surprising than 0.1 + 0.2 = 0.30000000000000004.
|
||||
|
||||
Probably this is a bad idea, it would definitely use a lot of screen space.
|
||||
|
||||
#### a quick note on PHP
|
||||
|
||||
Someone in the comments somewhere pointed out that `<?php echo (0.1 + 0.2 );?>`
|
||||
prints out `0.3`. Does that mean that floating point math is different in PHP?
|
||||
Someone in the comments somewhere pointed out that `<?php echo (0.1 + 0.2 );?>` prints out `0.3`. Does that mean that floating point math is different in PHP?
|
||||
|
||||
I think the answer is no – if I run:
|
||||
|
||||
`<?php echo (0.1 + 0.2 )- 0.3);?>` on [this
|
||||
page][3], I get the exact same answer as in
|
||||
Python 5.5511151231258E-17. So it seems like the underlying floating point
|
||||
math is the same.
|
||||
`<?php echo (0.1 + 0.2 )- 0.3);?>` on [this page][3], I get the exact same answer as in Python 5.5511151231258E-17. So it seems like the underlying floating point math is the same.
|
||||
|
||||
I think the reason that `0.1 + 0.2` prints out `0.3` in PHP is that PHP’s
|
||||
algorithm for displaying floating point numbers is less precise than Python’s
|
||||
– it’ll display `0.3` even if that number isn’t the closest floating point
|
||||
number to 0.3.
|
||||
I think the reason that `0.1 + 0.2` prints out `0.3` in PHP is that PHP’s algorithm for displaying floating point numbers is less precise than Python’s – it’ll display `0.3` even if that number isn’t the closest floating point number to 0.3.
|
||||
|
||||
#### that’s all!
|
||||
|
||||
I kind of doubt that anyone had the patience to follow all of that arithmetic,
|
||||
but it was helpful for me to write down, so I’m publishing this post anyway.
|
||||
Hopefully some of this makes sense.
|
||||
I kind of doubt that anyone had the patience to follow all of that arithmetic, but it was helpful for me to write down, so I’m publishing this post anyway. Hopefully some of this makes sense.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -383,4 +330,4 @@ via: https://jvns.ca/blog/2023/02/08/why-does-0-1-plus-0-2-equal-0-3000000000000
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://legacy.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
|
||||
[2]: https://lists.nongnu.org/archive/html/gcl-devel/2012-10/pdfkieTlklRzN.pdf
|
||||
[3]: https://replit.com/languages/php_cli
|
||||
[3]: https://replit.com/languages/php_cli
|
||||
|
@ -10,33 +10,19 @@
|
||||
Some notes on using nix
|
||||
======
|
||||
|
||||
Recently I started using a Mac for the first time. The biggest downside I’ve
|
||||
noticed so far is that the package management is much worse than on Linux.
|
||||
At some point I got frustrated with homebrew because I felt like it was
|
||||
spending too much time upgrading when I installed new packages, and so I
|
||||
thought – maybe I’ll try the [nix][1] package manager!
|
||||
Recently I started using a Mac for the first time. The biggest downside I’ve noticed so far is that the package management is much worse than on Linux. At some point I got frustrated with homebrew because I felt like it was spending too much time upgrading when I installed new packages, and so I thought – maybe I’ll try the [nix][1] package manager!
|
||||
|
||||
nix has a reputation for being confusing (it has its whole
|
||||
own programming language!), so I’ve been trying to figure out how to use nix in
|
||||
a way that’s as simple as possible and does not involve managing any
|
||||
configuration files or learning a new programming language. Here’s what I’ve
|
||||
figured out so far! We’ll talk about how to:
|
||||
nix has a reputation for being confusing (it has its whole own programming language!), so I’ve been trying to figure out how to use nix in a way that’s as simple as possible and does not involve managing any configuration files or learning a new programming language. Here’s what I’ve figured out so far! We’ll talk about how to:
|
||||
|
||||
- install packages with nix
|
||||
- build a custom nix package for a C++ program called [paperjam][2]
|
||||
- install a 5-year-old version of [hugo][3] with nix
|
||||
|
||||
As usual I’ve probably gotten some stuff wrong in this post since I’m still
|
||||
pretty new to nix. I’m also still not sure how much I like nix – it’s very
|
||||
confusing! But it’s helped me compile some software that I was struggling to
|
||||
compile otherwise, and in general it seems to install things faster than
|
||||
homebrew.
|
||||
As usual I’ve probably gotten some stuff wrong in this post since I’m still pretty new to nix. I’m also still not sure how much I like nix – it’s very confusing! But it’s helped me compile some software that I was struggling to compile otherwise, and in general it seems to install things faster than homebrew.
|
||||
|
||||
#### what’s interesting about nix?
|
||||
|
||||
People often describe nix as “declarative package management”. I don’t
|
||||
care that much about declarative package management, so here are two things
|
||||
that I appreciate about nix:
|
||||
People often describe nix as “declarative package management”. I don’t care that much about declarative package management, so here are two things that I appreciate about nix:
|
||||
|
||||
- It provides binary packages (hosted at [https://cache.nixos.org/][4]) that you can quickly download and install
|
||||
- For packages which don’t have binary packages, it makes it easier to compile them
|
||||
@ -44,12 +30,8 @@ that I appreciate about nix:
|
||||
I think that the reason nix is good at compiling software is that:
|
||||
|
||||
- you can have multiple versions of the same library or program installed at a time (you could have 2 different versions of libc for instance). For example I have two versions of node on my computer right now, one at `/nix/store/4ykq0lpvmskdlhrvz1j3kwslgc6c7pnv-nodejs-16.17.1` and one at `/nix/store/5y4bd2r99zhdbir95w5pf51bwfg37bwa-nodejs-18.9.1`.
|
||||
- when nix builds a package, it builds it in isolation, using only the
|
||||
specific versions of its dependencies that you explicitly declared. So
|
||||
there’s no risk that the package secretly depends on another package on your
|
||||
system that you don’t know about. No more fighting with `LD_LIBRARY_PATH`!
|
||||
- a lot of people have put a lot of work into writing down all of the
|
||||
dependencies of packages
|
||||
- when nix builds a package, it builds it in isolation, using only the specific versions of its dependencies that you explicitly declared. So there’s no risk that the package secretly depends on another package on your
|
||||
system that you don’t know about. No more fighting with `LD_LIBRARY_PATH`! - a lot of people have put a lot of work into writing down all of the dependencies of packages
|
||||
|
||||
I’ll give a couple of examples later in this post of two times nix made it easier for me to compile software.
|
||||
|
||||
@ -72,15 +54,11 @@ nix-env -iA nixpkgs.fish
|
||||
|
||||
This seems to just download some binaries from [https://cache.nixos.org][8] – pretty simple.
|
||||
|
||||
Some people use nix to install their Node and Python and Ruby packages, but I haven’t
|
||||
been doing that – I just use `npm install` and `pip install` the same way I
|
||||
always have.
|
||||
Some people use nix to install their Node and Python and Ruby packages, but I haven’t been doing that – I just use `npm install` and `pip install` the same way I always have.
|
||||
|
||||
#### some nix features I’m not using
|
||||
|
||||
There are a bunch of nix features/tools that I’m not using, but that I’ll
|
||||
mention. I originally thought that you _had_ to use these features to use nix,
|
||||
because most of the nix tutorials I’ve read talk about them. But you don’t have to use them.
|
||||
There are a bunch of nix features/tools that I’m not using, but that I’ll mention. I originally thought that you _had_ to use these features to use nix, because most of the nix tutorials I’ve read talk about them. But you don’t have to use them.
|
||||
|
||||
- NixOS (a Linux distribution)
|
||||
- [nix-shell][9]
|
||||
@ -88,8 +66,7 @@ because most of the nix tutorials I’ve read talk about them. But you don’t h
|
||||
- [home-manager][11]
|
||||
- [devenv.sh][12]
|
||||
|
||||
I won’t go into these because I haven’t really used them and there are lots of
|
||||
explanations out there.
|
||||
I won’t go into these because I haven’t really used them and there are lots of explanations out there.
|
||||
|
||||
#### where are nix packages defined?
|
||||
|
||||
@ -107,16 +84,14 @@ I found a way to search nix packages from the command line that I liked better:
|
||||
|
||||
#### everything is installed with symlinks
|
||||
|
||||
One of nix’s major design choices is that there isn’t one single `bin` with all
|
||||
your packages, instead you use symlinks. There are a lot of layers of symlinks. A few examples of symlinks:
|
||||
One of nix’s major design choices is that there isn’t one single `bin` with all your packages, instead you use symlinks. There are a lot of layers of symlinks. A few examples of symlinks:
|
||||
|
||||
- `~/.nix-profile` on my machine is (indirectly) a symlink to `/nix/var/nix/profiles/per-user/bork/profile-111-link/`
|
||||
- `~/.nix-profile/bin/fish` is a symlink to `/nix/store/afkwn6k8p8g97jiqgx9nd26503s35mgi-fish-3.5.1/bin/fish`
|
||||
|
||||
When I install something, it creates a new `profile-112-link` directory with new symlinks and updates my `~/.nix-profile` to point to that directory.
|
||||
|
||||
I think this means that if I install a new version of `fish` and I don’t like it, I can
|
||||
easily go back just by running `nix-env --rollback` – it’ll move me to my previous profile directory.
|
||||
I think this means that if I install a new version of `fish` and I don’t like it, I can easily go back just by running `nix-env --rollback` – it’ll move me to my previous profile directory.
|
||||
|
||||
#### uninstalling packages doesn’t delete them
|
||||
|
||||
@ -161,28 +136,19 @@ I haven’t really upgraded anything yet. I think that if something goes wrong w
|
||||
nix-env --rollback
|
||||
```
|
||||
|
||||
Someone linked me to [this post from Ian Henry][15] that
|
||||
talks about some confusing problems with `nix-env --upgrade` – maybe it
|
||||
doesn’t work the way you’d expect? I guess I’ll be wary around upgrades.
|
||||
Someone linked me to [this post from Ian Henry][15] that talks about some confusing problems with `nix-env --upgrade` – maybe it doesn’t work the way you’d expect? I guess I’ll be wary around upgrades.
|
||||
|
||||
#### next goal: make a custom package of paperjam
|
||||
|
||||
After a few months of installing existing packages, I wanted to make a custom package with nix for a program called [paperjam][2] that wasn’t already packaged.
|
||||
|
||||
I was actually struggling to compile `paperjam` at all even without nix because the version I had
|
||||
of `libiconv` I has on my system was wrong. I thought it might be easier to
|
||||
compile it with nix even though I didn’t know how to make nix packages yet. And
|
||||
it actually was!
|
||||
I was actually struggling to compile `paperjam` at all even without nix because the version I had of `libiconv` I has on my system was wrong. I thought it might be easier to compile it with nix even though I didn’t know how to make nix packages yet. And it actually was!
|
||||
|
||||
But figuring out how to get there was VERY confusing, so here are some notes about how I did it.
|
||||
|
||||
#### how to build an example package
|
||||
|
||||
Before I started working on my `paperjam` package, I wanted to build an example existing package just to
|
||||
make sure I understood the process for building a package. I was really
|
||||
struggling to figure out how to do this, but I asked in Discord and someone
|
||||
explained to me how I could get a working package from [https://github.com/NixOS/nixpkgs/][13] and build it. So here
|
||||
are those instructions:
|
||||
Before I started working on my `paperjam` package, I wanted to build an example existing package just to make sure I understood the process for building a package. I was really struggling to figure out how to do this, but I asked in Discord and someone explained to me how I could get a working package from [https://github.com/NixOS/nixpkgs/][13] and build it. So here are those instructions:
|
||||
|
||||
**step 1:** Download some arbitrary package from [nixpkgs][13] on github, for example the `dash` package:
|
||||
|
||||
@ -190,8 +156,7 @@ are those instructions:
|
||||
wget https://raw.githubusercontent.com/NixOS/nixpkgs/47993510dcb7713a29591517cb6ce682cc40f0ca/pkgs/shells/dash/default.nix -O dash.nix
|
||||
```
|
||||
|
||||
**step 2**: Replace the first statement (`{ lib , stdenv , buildPackages , autoreconfHook , pkg-config , fetchurl , fetchpatch , libedit , runCommand , dash }:` with `with import <nixpkgs> {};` I don’t know why you have to do this,
|
||||
but it works.
|
||||
**step 2**: Replace the first statement (`{ lib , stdenv , buildPackages , autoreconfHook , pkg-config , fetchurl , fetchpatch , libedit , runCommand , dash }:` with `with import <nixpkgs> {};` I don’t know why you have to do this, but it works.
|
||||
|
||||
**step 3**: Run `nix-build dash.nix`
|
||||
|
||||
@ -207,11 +172,7 @@ That’s all! Once I’d done that, I felt like I could modify the `dash` packag
|
||||
|
||||
`paperjam` has one dependency (`libpaper`) that also isn’t packaged yet, so I needed to build `libpaper` first.
|
||||
|
||||
Here’s `libpaper.nix`. I basically just wrote this by copying and pasting from
|
||||
other packages in the [nixpkgs][13] repository.
|
||||
My guess is what’s happening here is that nix has some default rules for
|
||||
compiling C packages (like “run `make install`”), so the `make install` happens
|
||||
default and I don’t need to configure it explicitly.
|
||||
Here’s `libpaper.nix`. I basically just wrote this by copying and pasting from other packages in the [nixpkgs][13] repository. My guess is what’s happening here is that nix has some default rules for compiling C packages (like “run `make install`”), so the `make install` happens default and I don’t need to configure it explicitly.
|
||||
|
||||
```
|
||||
with import <nixpkgs> {};
|
||||
@ -249,10 +210,7 @@ Next, I needed to compile `paperjam`. Here’s a link to the [nix package I wrot
|
||||
|
||||
I set the hashes by first leaving the hash empty, then running `nix-build` to get an error message complaining about a mismatched hash. Then I copied the correct hash out of the error message.
|
||||
|
||||
I figured out how to set `installFlags` just by running `rg PREFIX`
|
||||
in the nixpkgs repository – I figured that needing to set a `PREFIX` was
|
||||
pretty common and someone had probably done it before, and I was right. So I
|
||||
just copied and pasted that line from another package.
|
||||
I figured out how to set `installFlags` just by running `rg PREFIX` in the nixpkgs repository – I figured that needing to set a `PREFIX` was pretty common and someone had probably done it before, and I was right. So I just copied and pasted that line from another package.
|
||||
|
||||
Then I ran:
|
||||
|
||||
@ -265,29 +223,17 @@ and then everything worked and I had `paperjam` installed! Hooray!
|
||||
|
||||
#### next goal: install a 5-year-old version of hugo
|
||||
|
||||
Right now I build this blog using Hugo 0.40, from 2018. I don’t need any new
|
||||
features so I haven’t felt a need to upgrade. On Linux this is easy: Hugo’s
|
||||
releases are a static binary, so I can just download the 5-year-old binary from
|
||||
the [releases page][17] and
|
||||
run it. Easy!
|
||||
Right now I build this blog using Hugo 0.40, from 2018. I don’t need any new features so I haven’t felt a need to upgrade. On Linux this is easy: Hugo’s releases are a static binary, so I can just download the 5-year-old binary from the [releases page][17] and run it. Easy!
|
||||
|
||||
But on this Mac I ran into some complications. Mac hardware has changed in the
|
||||
last 5 years, so the Mac Hugo binary I downloaded crashed. And when I tried to
|
||||
build it from source with `go build`, that didn’t work either because Go build
|
||||
norms have changed in the last 5 years as well.
|
||||
But on this Mac I ran into some complications. Mac hardware has changed in the last 5 years, so the Mac Hugo binary I downloaded crashed. And when I tried to build it from source with `go build`, that didn’t work either because Go build norms have changed in the last 5 years as well.
|
||||
|
||||
I was working around this by running Hugo in a Linux docker container, but I
|
||||
didn’t love that: it was kind of slow and it felt silly. It shouldn’t be that
|
||||
hard to compile one Go program!
|
||||
I was working around this by running Hugo in a Linux docker container, but I didn’t love that: it was kind of slow and it felt silly. It shouldn’t be that hard to compile one Go program!
|
||||
|
||||
Nix to the rescue! Here’s what I did to install the old version of Hugo with
|
||||
nix.
|
||||
Nix to the rescue! Here’s what I did to install the old version of Hugo with nix.
|
||||
|
||||
#### installing Hugo 0.40 with nix
|
||||
|
||||
I wanted to install Hugo 0.40 and put it in my PATH as `hugo-0.40`. Here’s how
|
||||
I did it. I did this in a kind of weird way, but it worked ([Searching and installing old versions of Nix packages][18]
|
||||
describes a probably more normal method).
|
||||
I wanted to install Hugo 0.40 and put it in my PATH as `hugo-0.40`. Here’s how I did it. I did this in a kind of weird way, but it worked ([Searching and installing old versions of Nix packages][18] describes a probably more normal method).
|
||||
|
||||
**step 1**: Search through the nixpkgs repo to find Hugo 0.40
|
||||
|
||||
@ -318,33 +264,19 @@ I figured out how to run this by running `rg 'mv '` in the nixpkgs repository an
|
||||
|
||||
I installed into my `~/.nix-profile/bin` by running `nix-env -i -f hugo.nix`.
|
||||
|
||||
And it all works! I put the final `.nix` file into my own personal [nixpkgs repo][20] so that I can use it again later if I
|
||||
want.
|
||||
And it all works! I put the final `.nix` file into my own personal [nixpkgs repo][20] so that I can use it again later if I want.
|
||||
|
||||
#### reproducible builds aren’t magic, they’re really hard
|
||||
|
||||
I think it’s worth noting here that this `hugo.nix` file isn’t magic – the
|
||||
reason I can easily compile Hugo 0.40 today is that many people worked for a long time to make it possible to
|
||||
package that version of Hugo in a reproducible way.
|
||||
I think it’s worth noting here that this `hugo.nix` file isn’t magic – the reason I can easily compile Hugo 0.40 today is that many people worked for a long time to make it possible to package that version of Hugo in a reproducible way.
|
||||
|
||||
#### that’s all!
|
||||
|
||||
Installing `paperjam` and this 5-year-old version of Hugo were both
|
||||
surprisingly painless and actually much easier than compiling it without nix,
|
||||
because nix made it much easier for me to compile the `paperjam` package with
|
||||
the right version of `libiconv`, and because someone 5 years ago had already
|
||||
gone to the trouble of listing out the exact dependencies for Hugo.
|
||||
Installing `paperjam` and this 5-year-old version of Hugo were both surprisingly painless and actually much easier than compiling it without nix, because nix made it much easier for me to compile the `paperjam` package with the right version of `libiconv`, and because someone 5 years ago had already gone to the trouble of listing out the exact dependencies for Hugo.
|
||||
|
||||
I don’t have any plans to get much more complicated with nix (and it’s still
|
||||
very possible I’ll get frustrated with it and go back to homebrew!), but we’ll
|
||||
see what happens! I’ve found it much easier to start in a simple way and then
|
||||
start using more features if I feel the need instead of adopting a whole bunch
|
||||
of complicated stuff all at once.
|
||||
I don’t have any plans to get much more complicated with nix (and it’s still very possible I’ll get frustrated with it and go back to homebrew!), but we’ll see what happens! I’ve found it much easier to start in a simple way and then start using more features if I feel the need instead of adopting a whole bunch of complicated stuff all at once.
|
||||
|
||||
I probably won’t use nix on Linux – I’ve always been happy enough with `apt`
|
||||
(on Debian-based distros) and `pacman` (on Arch-based distros), and they’re
|
||||
much less confusing. But on a Mac it seems like it might be worth it. We’ll
|
||||
see! It’s very possible in 3 months I’ll get frustrated with nix and just go back to homebrew.
|
||||
I probably won’t use nix on Linux – I’ve always been happy enough with `apt` (on Debian-based distros) and `pacman` (on Arch-based distros), and they’re much less confusing. But on a Mac it seems like it might be worth it. We’ll see! It’s very possible in 3 months I’ll get frustrated with nix and just go back to homebrew.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -10,8 +10,7 @@
|
||||
How do Nix builds work?
|
||||
======
|
||||
|
||||
Hello! For some reason after the last [nix post][1] I got nerdsniped by trying to understand how Nix builds
|
||||
work under the hood, so here’s a quick exploration I did today. There are probably some mistakes in here.
|
||||
Hello! For some reason after the last [nix post][1] I got nerdsniped by trying to understand how Nix builds work under the hood, so here’s a quick exploration I did today. There are probably some mistakes in here.
|
||||
|
||||
I started by [complaining on Mastodon][2]:
|
||||
|
||||
@ -31,24 +30,18 @@ complicated C program.
|
||||
|
||||
#### the goal: compile a C program, without using Nix’s standard machinery
|
||||
|
||||
Our goal is to compile a C program called `paperjam`. This is a real C program
|
||||
that wasn’t in the Nix repository already. I already figured out how to
|
||||
compile it in [this post][1] by copying and pasting a bunch of stuff I didn’t understand, but this time I wanted to do
|
||||
it in a more principled way where I actually understand more of the steps.
|
||||
Our goal is to compile a C program called `paperjam`. This is a real C program that wasn’t in the Nix repository already. I already figured out how to
|
||||
compile it in [this post][1] by copying and pasting a bunch of stuff I didn’t understand, but this time I wanted to do it in a more principled way where I actually understand more of the steps.
|
||||
|
||||
We’re going to avoid using most of Nix’s helpers for compiling C programs.
|
||||
|
||||
The plan is to start with an almost empty build script, and then resolve errors
|
||||
until we have a working build.
|
||||
The plan is to start with an almost empty build script, and then resolve errors until we have a working build.
|
||||
|
||||
#### first: what’s a derivation?
|
||||
|
||||
I said that we weren’t going to talk about too many Nix abstractions (and we won’t!), but understanding what a derivation is really helped me.
|
||||
|
||||
Everything I read about Nix talks about derivations all the time, but I was
|
||||
really struggling to figure out what a derivation _is_. It turns out that `derivation`
|
||||
is a function in the Nix language. But not just any function! The whole point of the Nix language seems to be to
|
||||
to call this function. The [official documentation for the `derivation` function][5] is actually extremely clear. Here’s what I took away:
|
||||
Everything I read about Nix talks about derivations all the time, but I was really struggling to figure out what a derivation _is_. It turns out that `derivation` is a function in the Nix language. But not just any function! The whole point of the Nix language seems to be to to call this function. The [official documentation for the `derivation` function][5] is actually extremely clear. Here’s what I took away:
|
||||
|
||||
`derivation` takes a bunch of keys and values as input. There are 3 required keys:
|
||||
|
||||
@ -56,8 +49,7 @@ to call this function. The [official documentation for the `derivation` function
|
||||
- `name`: the name of the package you’re building
|
||||
- `builder`: a program (usually a bash script) that runs the build
|
||||
|
||||
Every other key is an arbitrary string that gets passed as an environment
|
||||
variable to the `builder` shell script.
|
||||
Every other key is an arbitrary string that gets passed as an environment variable to the `builder` shell script.
|
||||
|
||||
#### derivations automatically build all their inputs
|
||||
|
||||
@ -69,15 +61,12 @@ Nix will:
|
||||
- put the resulting output directory somewhere like `/nix/store/4garxzr1rpdfahf374i9p9fbxnx56519-qpdf-11.1.0`
|
||||
- expand `pkgs.qpdf` into that output directory (as a string), so that I can reference it in my build script
|
||||
|
||||
The derivation function does some other things (described in the
|
||||
[documentation][5]), but “it builds all of its inputs” is all we really need to know
|
||||
The derivation function does some other things (described in the [documentation][5]), but “it builds all of its inputs” is all we really need to know
|
||||
for now.
|
||||
|
||||
#### step 1: write a derivation file
|
||||
|
||||
Let’s write a very simple build script and call the `derivation` function. These don’t work yet,
|
||||
but I found it pretty fun to go through all the errors, fix them one at a time,
|
||||
and learn a little more about how Nix works by fixing them.
|
||||
Let’s write a very simple build script and call the `derivation` function. These don’t work yet, but I found it pretty fun to go through all the errors, fix them one at a time, and learn a little more about how Nix works by fixing them.
|
||||
|
||||
Here’s the build script (`build_paperjam.sh`). This just unpacks the tarball and runs `make install`.
|
||||
|
||||
@ -115,9 +104,7 @@ The main things here are:
|
||||
|
||||
#### problem 1: tar: command not found
|
||||
|
||||
Nix needs you to declare all the dependencies for your builds. It forces this
|
||||
by removing your `PATH` environment variable so that you have no binaries in
|
||||
your PATH at all.
|
||||
Nix needs you to declare all the dependencies for your builds. It forces this by removing your `PATH` environment variable so that you have no binaries in your PATH at all.
|
||||
|
||||
This is pretty easy to fix: we just need to edit our `PATH`.
|
||||
|
||||
@ -150,11 +137,9 @@ The next error was:
|
||||
> #include <qpdf/QPDF.hh>
|
||||
```
|
||||
|
||||
Makes sense: everything is isolated, so it can’t access my system header files.
|
||||
Figuring out how to handle this was a little more confusing though.
|
||||
Makes sense: everything is isolated, so it can’t access my system header files. Figuring out how to handle this was a little more confusing though.
|
||||
|
||||
It turns out that the way Nix handles header files is that it has a shell
|
||||
script wrapper around `clang`. So when you run `clang++`, you’re actually
|
||||
It turns out that the way Nix handles header files is that it has a shell script wrapper around `clang`. So when you run `clang++`, you’re actually
|
||||
running a shell script.
|
||||
|
||||
On my system, the `clang++` wrapper script was at `/nix/store/d929v59l9a3iakvjccqpfqckqa0vflyc-clang-wrapper-11.1.0/bin/clang++`. I searched that file for `LDFLAGS` and found that it uses 2 environment variables:
|
||||
@ -194,22 +179,15 @@ Here’s the next error:
|
||||
|
||||
I started by adding `-L ${pkgs.libiconv}/lib` to my `NIX_LDFLAGS` environment variable, but that didn’t fix it. Then I spent a while going around in circles and being confused.
|
||||
|
||||
I eventually figured out how to fix this by taking a working version of the `paperjam` build that I’d made before
|
||||
and editing my `clang++` wrapper file to print out all of its environment
|
||||
variables. The `LDFLAGS` environment variable in the working version was different from mine: it had `-liconv` in it.
|
||||
I eventually figured out how to fix this by taking a working version of the `paperjam` build that I’d made before and editing my `clang++` wrapper file to print out all of its environment variables. The `LDFLAGS` environment variable in the working version was different from mine: it had `-liconv` in it.
|
||||
|
||||
So I added `-liconv` to `NIX_LDFLAGS` as well and that fixed it.
|
||||
|
||||
#### why doesn’t the original Makefile have -liconv?
|
||||
|
||||
I was a bit puzzled by this `-liconv` thing though: the original Makefile links
|
||||
in `libqpdf` and `libpaper` by passing `-lqpdf -lpaper`. So why doesn’t it link in iconv, if it requires the
|
||||
iconv library?
|
||||
I was a bit puzzled by this `-liconv` thing though: the original Makefile links in `libqpdf` and `libpaper` by passing `-lqpdf -lpaper`. So why doesn’t it link in iconv, if it requires the iconv library?
|
||||
|
||||
I think the reason for this is that the original Makefile assumed that you were
|
||||
running on Linux and using glibc, and glibc includes these iconv functions by
|
||||
default. But I guess Mac OS libc doesn’t include iconv, so we need to
|
||||
explicitly set the linker flag `-liconv` to add the iconv library.
|
||||
I think the reason for this is that the original Makefile assumed that you were running on Linux and using glibc, and glibc includes these iconv functions by default. But I guess Mac OS libc doesn’t include iconv, so we need to explicitly set the linker flag `-liconv` to add the iconv library.
|
||||
|
||||
#### problem 6: missing codesign_allocate
|
||||
|
||||
@ -219,8 +197,7 @@ Time for the next error:
|
||||
libc++abi: terminating with uncaught exception of type std::runtime_error: Failed to spawn codesign_allocate: No such file or directory
|
||||
```
|
||||
|
||||
I guess this is some kind of Mac code signing thing. I used `find /nix/store -name codesign_allocate` to find `codesign_allocate` on my system. It’s at
|
||||
`/nix/store/a17dwfwqj5ry734zfv3k1f5n37s4wxns-cctools-binutils-darwin-973.0.1/bin/codesign_allocate`.
|
||||
I guess this is some kind of Mac code signing thing. I used `find /nix/store -name codesign_allocate` to find `codesign_allocate` on my system. It’s at `/nix/store/a17dwfwqj5ry734zfv3k1f5n37s4wxns-cctools-binutils-darwin-973.0.1/bin/codesign_allocate`.
|
||||
|
||||
But this doesn’t tell us what the package is called – we need to be able to refer to it as `${pkgs.XXXXXXX}` and `${pkgs.cctools-binutils-darwin}` doesn’t work.
|
||||
|
||||
@ -289,8 +266,7 @@ make install PREFIX="$out"
|
||||
|
||||
#### let’s look at our compiled derivation!
|
||||
|
||||
Now that we understand this configuration a little better, let’s talk about
|
||||
what `nix-build` is doing a little more.
|
||||
Now that we understand this configuration a little better, let’s talk about what `nix-build` is doing a little more.
|
||||
|
||||
Behind the scenes, `nix-build paperjam.nix` actually runs `nix-instantiate` and `nix-store --realize`:
|
||||
|
||||
@ -300,11 +276,7 @@ $ nix-instantiate paperjam.nix
|
||||
$ nix-store --realize /nix/store/xp8kibpll55s0bm40wlpip51y7wnpfs0-paperjam-fake.drv
|
||||
```
|
||||
|
||||
I think what this means is that `paperjam.nix` get compiled to some
|
||||
intermediate representation (also called a derivation?), and then the Nix
|
||||
runtime takes over and is in charge of actually running the build scripts.
|
||||
|
||||
We can look at this `.drv` intermediate representation with `nix show-derivation`
|
||||
I think what this means is that `paperjam.nix` get compiled to some intermediate representation (also called a derivation?), and then the Nix runtime takes over and is in charge of actually running the build scripts. We can look at this `.drv` intermediate representation with `nix show-derivation`
|
||||
|
||||
```
|
||||
{
|
||||
@ -345,13 +317,11 @@ We can look at this `.drv` intermediate representation with `nix show-derivation
|
||||
}
|
||||
```
|
||||
|
||||
This feels surprisingly easy to understand – you can see that there are a
|
||||
bunch of environment variables, our bash script, and the paths to our inputs.
|
||||
This feels surprisingly easy to understand – you can see that there are a bunch of environment variables, our bash script, and the paths to our inputs.
|
||||
|
||||
#### the compilation helpers we’re not using: stdenv
|
||||
|
||||
Normally when you build a package with Nix, you don’t do all of this stuff
|
||||
yourself. Instead, you use a helper called `stdenv`, which seems to have two parts:
|
||||
Normally when you build a package with Nix, you don’t do all of this stuff yourself. Instead, you use a helper called `stdenv`, which seems to have two parts:
|
||||
|
||||
- a function called `stdenv.mkDerivation` which takes some arguments and generates a bunch of environment variables (it seems to be [documented here][6])
|
||||
- a 1600-line bash build script ([setup.sh][7]) that consumes those environment variables. This is like our `build-paperjam.sh`, but much more generalized.
|
||||
@ -370,8 +340,7 @@ and probably lots more useful things I don’t know about yet
|
||||
|
||||
#### let’s look at the derivation for jq
|
||||
|
||||
Let’s look at one more compiled derivation, for `jq`. This is quite long but there
|
||||
are some interesting things in here. I wanted to look at this because I wanted to see what a more typical derivation generated by `stdenv.mkDerivation` looked like.
|
||||
Let’s look at one more compiled derivation, for `jq`. This is quite long but there are some interesting things in here. I wanted to look at this because I wanted to see what a more typical derivation generated by `stdenv.mkDerivation` looked like.
|
||||
|
||||
```
|
||||
$ nix show-derivation /nix/store/q9cw5rp0ibpl6h4i2qaq0vdjn4pyms3p-jq-1.6.drv
|
||||
@ -451,8 +420,7 @@ $ nix show-derivation /nix/store/q9cw5rp0ibpl6h4i2qaq0vdjn4pyms3p-jq-1.6.drv
|
||||
}
|
||||
```
|
||||
|
||||
I thought it was interesting that some of the environment variables in here are actually bash scripts themselves – for example the `postInstallCheck` environment variable is a bash script.
|
||||
Those bash script environment variables are `eval`ed in the main bash script (you can [see that happening in setup.sh here][8])
|
||||
I thought it was interesting that some of the environment variables in here are actually bash scripts themselves – for example the `postInstallCheck` environment variable is a bash script. Those bash script environment variables are `eval`ed in the main bash script (you can [see that happening in setup.sh here][8])
|
||||
|
||||
The `postInstallCheck` environment variable in this particular derivation starts like this:
|
||||
|
||||
@ -469,11 +437,7 @@ All of my compiler experiments used about 3GB of disk space, but `nix-collect-ga
|
||||
|
||||
#### let’s recap the process!
|
||||
|
||||
I feel like I understand Nix a bit better after going through this. I still
|
||||
don’t feel very motivated to learn the Nix language, but now I have some
|
||||
idea of what Nix programs are actually doing under the hood!
|
||||
|
||||
My understanding is:
|
||||
I feel like I understand Nix a bit better after going through this. I still don’t feel very motivated to learn the Nix language, but now I have some idea of what Nix programs are actually doing under the hood! My understanding is:
|
||||
|
||||
- First, `.nix` files get compiled into a `.drv` file, which is mostly a bunch of inputs and outputs and environment variables. This is where the Nix language stops being relevant.
|
||||
- Then all the environment variables get passed to a build script, which is in charge of doing the actual build
|
||||
|
@ -10,9 +10,7 @@
|
||||
Some possible reasons for 8-bit bytes
|
||||
======
|
||||
|
||||
I’ve been working on a zine about how computers represent thing in binary, and
|
||||
one question I’ve gotten a few times is – why does the x86 architecture use 8-bit bytes? Why not
|
||||
some other size?
|
||||
I’ve been working on a zine about how computers represent thing in binary, and one question I’ve gotten a few times is – why does the x86 architecture use 8-bit bytes? Why not some other size?
|
||||
|
||||
With any question like this, I think there are two options:
|
||||
|
||||
@ -20,34 +18,18 @@ With any question like this, I think there are two options:
|
||||
- 8 bits is objectively the Best Option for some reason, even if history had played out differently we would still use 8-bit bytes
|
||||
- some mix of 1 & 2
|
||||
|
||||
I’m not super into computer history (I like to use computers a lot more than I
|
||||
like reading about them), but I am always curious if there’s an essential
|
||||
reason for why a computer thing is the way it is today, or whether it’s mostly
|
||||
a historical accident. So we’re going to talk about some computer history.
|
||||
I’m not super into computer history (I like to use computers a lot more than I like reading about them), but I am always curious if there’s an essential reason for why a computer thing is the way it is today, or whether it’s mostly a historical accident. So we’re going to talk about some computer history.
|
||||
|
||||
As an example of a historical accident: DNS has a `class` field which has 5
|
||||
possible values (“internet”, “chaos”, “hesiod”, “none”, and “any”). To me that’s
|
||||
a clear example of a historical accident – I can’t imagine that we’d define
|
||||
the class field the same way if we could redesign DNS today without worrying about backwards compatibility. I’m
|
||||
not sure if we’d use a class field at all!
|
||||
As an example of a historical accident: DNS has a `class` field which has 5 possible values (“internet”, “chaos”, “hesiod”, “none”, and “any”). To me that’s a clear example of a historical accident – I can’t imagine that we’d define the class field the same way if we could redesign DNS today without worrying about backwards compatibility. I’m not sure if we’d use a class field at all!
|
||||
|
||||
There aren’t any definitive answers in this post, but I asked [on Mastodon][1] and
|
||||
here are some potential reasons I found for the 8-bit byte. I think the answer
|
||||
is some combination of these reasons.
|
||||
There aren’t any definitive answers in this post, but I asked [on Mastodon][1] and here are some potential reasons I found for the 8-bit byte. I think the answer is some combination of these reasons.
|
||||
|
||||
#### what’s the difference between a byte and a word?
|
||||
|
||||
First, this post talks about “bytes” and “words” a lot. What’s the difference between a byte and a word? My understanding is:
|
||||
|
||||
- the **byte size** is the smallest unit you can address. For example in a program on my machine `0x20aa87c68` might be the address of one byte, then `0x20aa87c69` is the address of the next byte.
|
||||
- The **word size** is some multiple of the byte size. I’ve been confused about
|
||||
this for years, and the Wikipedia definition is incredibly vague (“a word is
|
||||
the natural unit of data used by a particular processor design”). I
|
||||
originally thought that the word size was the same as your register size (64
|
||||
bits on x86-64). But according to section 4.1 (“Fundamental Data Types”) of the [Intel architecture manual][2],
|
||||
on x86 a word is 16 bits even though the registers are 64 bits. So I’m
|
||||
confused – is a word on x86 16 bits or 64 bits? Can it mean both, depending
|
||||
on the context? What’s the deal?
|
||||
- The **word size** is some multiple of the byte size. I’ve been confused about this for years, and the Wikipedia definition is incredibly vague (“a word is the natural unit of data used by a particular processor design”). I originally thought that the word size was the same as your register size (64 bits on x86-64). But according to section 4.1 (“Fundamental Data Types”) of the [Intel architecture manual][2], on x86 a word is 16 bits even though the registers are 64 bits. So I’m confused – is a word on x86 16 bits or 64 bits? Can it mean both, depending on the context? What’s the deal?
|
||||
|
||||
Now let’s talk about some possible reasons that we use 8-bit bytes!
|
||||
|
||||
@ -65,18 +47,11 @@ Here’s a [video interview with Fred Brooks (who managed the project)][4] talki
|
||||
> My most important technical decision in my IBM career was to go with the 8-bit byte for the 360.
|
||||
> And on the basis of I believe character processing was going to become important as opposed to decimal digits.
|
||||
|
||||
It makes sense that an 8-bit byte would be better for text processing: 2^6 is
|
||||
64, so 6 bits wouldn’t be enough for lowercase letters, uppercase letters, and symbols.
|
||||
It makes sense that an 8-bit byte would be better for text processing: 2^6 is 64, so 6 bits wouldn’t be enough for lowercase letters, uppercase letters, and symbols.
|
||||
|
||||
To go with the 8-bit byte, System/360 also introduced the [EBCDIC][5] encoding, which is an 8-bit character encoding.
|
||||
|
||||
It looks like the next important machine in 8-bit-byte history was the
|
||||
[Intel 8008][6], which was built to be
|
||||
used in a computer terminal (the Datapoint 2200). Terminals need to be able to
|
||||
represent letters as well as terminal control codes, so it makes sense for them
|
||||
to use an 8-bit byte.
|
||||
[This Datapoint 2200 manual from the Computer History Museum][7]
|
||||
says on page 7 that the Datapoint 2200 supported ASCII (7 bit) and EBCDIC (8 bit).
|
||||
It looks like the next important machine in 8-bit-byte history was the [Intel 8008][6], which was built to be used in a computer terminal (the Datapoint 2200). Terminals need to be able to represent letters as well as terminal control codes, so it makes sense for them to use an 8-bit byte. [This Datapoint 2200 manual from the Computer History Museum][7] says on page 7 that the Datapoint 2200 supported ASCII (7 bit) and EBCDIC (8 bit).
|
||||
|
||||
#### why was the 6-bit byte better for scientific computing?
|
||||
|
||||
@ -90,14 +65,11 @@ I was curious about this comment that the 6-bit byte would be better for scienti
|
||||
> you to lose some of the information more rapidly than you would with binary
|
||||
> shifting
|
||||
|
||||
I don’t understand this comment at all – why does the exponent have to be 8 bits
|
||||
if you use a 32-bit word size? Why couldn’t you use 9 bits or 10 bits if you
|
||||
wanted? But it’s all I could find in a quick search.
|
||||
I don’t understand this comment at all – why does the exponent have to be 8 bits if you use a 32-bit word size? Why couldn’t you use 9 bits or 10 bits if you wanted? But it’s all I could find in a quick search.
|
||||
|
||||
#### why did mainframes use 36 bits?
|
||||
|
||||
Also related to the 6-bit byte: a lot of mainframes used a 36-bit word size. Why? Someone pointed out
|
||||
that there’s a great explanation in the Wikipedia article on [36-bit computing][9]:
|
||||
Also related to the 6-bit byte: a lot of mainframes used a 36-bit word size. Why? Someone pointed out that there’s a great explanation in the Wikipedia article on [36-bit computing][9]:
|
||||
|
||||
> Prior to the introduction of computers, the state of the art in precision
|
||||
> scientific and engineering calculation was the ten-digit, electrically powered,
|
||||
@ -111,23 +83,16 @@ that there’s a great explanation in the Wikipedia article on [36-bit computing
|
||||
|
||||
So this 36 bit thing seems to based on the fact that log_2(20000000000) is 34.2. Huh.
|
||||
|
||||
My guess is that the reason for this is in the 50s, computers were
|
||||
extremely expensive. So if you wanted your computer to support ten decimal
|
||||
digits, you’d design so that it had exactly enough bits to do that, and no
|
||||
more.
|
||||
My guess is that the reason for this is in the 50s, computers were extremely expensive. So if you wanted your computer to support ten decimal
|
||||
digits, you’d design so that it had exactly enough bits to do that, and no more.
|
||||
|
||||
Today computers are way faster and cheaper, so if you want to represent ten
|
||||
decimal digits for some reason you can just use 64 bits – wasting a little bit
|
||||
of space is usually no big deal.
|
||||
Today computers are way faster and cheaper, so if you want to represent ten decimal digits for some reason you can just use 64 bits – wasting a little bit of space is usually no big deal.
|
||||
|
||||
Someone else mentioned that some of these machines with 36-bit word sizes let
|
||||
you choose a byte size – you could use 5 or 6 or 7 or 8-bit bytes, depending
|
||||
on the context.
|
||||
Someone else mentioned that some of these machines with 36-bit word sizes let you choose a byte size – you could use 5 or 6 or 7 or 8-bit bytes, depending on the context.
|
||||
|
||||
#### reason 2: to work well with binary-coded decimal
|
||||
|
||||
In the 60s, there was a popular integer encoding called binary-coded decimal (or [BCD][10] for short) that
|
||||
encoded every decimal digit in 4 bits.
|
||||
In the 60s, there was a popular integer encoding called binary-coded decimal (or [BCD][10] for short) that encoded every decimal digit in 4 bits.
|
||||
|
||||
For example, if you wanted to encode the number 1234, in BCD that would be something like:
|
||||
|
||||
@ -135,49 +100,32 @@ For example, if you wanted to encode the number 1234, in BCD that would be somet
|
||||
0001 0010 0011 0100
|
||||
```
|
||||
|
||||
So if you want to be able to easily work with binary-coded decimal, your byte
|
||||
size should be a multiple of 4 bits, like 8 bits!
|
||||
So if you want to be able to easily work with binary-coded decimal, your byte size should be a multiple of 4 bits, like 8 bits!
|
||||
|
||||
#### why was BCD popular?
|
||||
|
||||
This integer representation seemed really weird to me – why not just use
|
||||
binary, which is a much more efficient way to store integers? Efficiency was really important in early computers!
|
||||
This integer representation seemed really weird to me – why not just use binary, which is a much more efficient way to store integers? Efficiency was really important in early computers!
|
||||
|
||||
My best guess about why is that early computers didn’t have displays the same way we do
|
||||
now, so the contents of a byte were mapped directly to on/off lights.
|
||||
My best guess about why is that early computers didn’t have displays the same way we do now, so the contents of a byte were mapped directly to on/off lights.
|
||||
|
||||
Here’s a [picture from Wikipedia of an IBM 650 with some lights on its display][11] ([CC BY-SA 3.0][12]):
|
||||
|
||||
![][13]
|
||||
|
||||
So if you want people to be relatively able to easily read off a decimal number
|
||||
from its binary representation, this makes a lot more sense. I think today BCD
|
||||
is obsolete because we have displays and our computers can convert numbers
|
||||
represented in binary to decimal for us and display them.
|
||||
So if you want people to be relatively able to easily read off a decimal number from its binary representation, this makes a lot more sense. I think today BCD is obsolete because we have displays and our computers can convert numbers represented in binary to decimal for us and display them.
|
||||
|
||||
Also, I wonder if BCD is where the term “nibble” for 4 bits comes from – in
|
||||
the context of BCD, you end up referring to half bytes a lot (because every
|
||||
digits is 4 bits). So it makes sense to have a word for “4 bits”, and people
|
||||
called 4 bits a nibble. Today “nibble” feels to me like an archaic term though –
|
||||
I’ve definitely never used it except as a fun fact (it’s such a fun word!). The Wikipedia article on [nibbles][14] supports this theory:
|
||||
Also, I wonder if BCD is where the term “nibble” for 4 bits comes from – in the context of BCD, you end up referring to half bytes a lot (because every digits is 4 bits). So it makes sense to have a word for “4 bits”, and people called 4 bits a nibble. Today “nibble” feels to me like an archaic term though – I’ve definitely never used it except as a fun fact (it’s such a fun word!). The Wikipedia article on [nibbles][14] supports this theory:
|
||||
|
||||
> The nibble is used to describe the amount of memory used to store a digit of
|
||||
> a number stored in packed decimal format (BCD) within an IBM mainframe.
|
||||
|
||||
Another reason someone mentioned for BCD was **financial calculations**. Today
|
||||
if you want to store a dollar amount, you’ll typically just use an integer
|
||||
amount of cents, and then divide by 100 if you want the dollar part. This is no
|
||||
big deal, division is fast. But apparently in the 70s dividing an integer
|
||||
represented in binary by 100 was very slow, so it was worth it to redesign how
|
||||
you represent your integers to avoid having to divide by 100.
|
||||
Another reason someone mentioned for BCD was **financial calculations**. Today if you want to store a dollar amount, you’ll typically just use an integer amount of cents, and then divide by 100 if you want the dollar part. This is no big deal, division is fast. But apparently in the 70s dividing an integer represented in binary by 100 was very slow, so it was worth it to redesign how you represent your integers to avoid having to divide by 100.
|
||||
|
||||
Okay, enough about BCD.
|
||||
|
||||
#### reason 3: 8 is a power of 2?
|
||||
|
||||
A bunch of people said it’s important for a CPU’s byte size to be a power of 2.
|
||||
I can’t figure out whether this is true or not though, and I wasn’t satisfied with the explanation that “computers use binary so powers of 2 are good”. That seems very plausible but I wanted to dig deeper.
|
||||
And historically there have definitely been lots of machines that used byte sizes that weren’t powers of 2, for example (from [this retro computing stack exchange thread][15]):
|
||||
A bunch of people said it’s important for a CPU’s byte size to be a power of 2. I can’t figure out whether this is true or not though, and I wasn’t satisfied with the explanation that “computers use binary so powers of 2 are good”. That seems very plausible but I wanted to dig deeper. And historically there have definitely been lots of machines that used byte sizes that weren’t powers of 2, for example (from [this retro computing stack exchange thread][15]):
|
||||
|
||||
- Cyber 180 mainframes used 6-bit bytes
|
||||
- the Univac 1100 / 2200 series used a 36-bit word size
|
||||
@ -190,57 +138,31 @@ Some reasons I heard for why powers of 2 are good that I haven’t understood ye
|
||||
|
||||
Reasons that made more sense to me:
|
||||
|
||||
- it makes it easier to design **clock dividers** that can measure “8 bits were
|
||||
sent on this wire” that work based on halving – you can put 3 halving clock
|
||||
dividers in series. [Graham Sutherland][16] told me about this and made this really cool
|
||||
[simulator of clock dividers][17] showing what these clock dividers look like. That site (Falstad) also has a bunch of other example circuits and it seems like a really cool way to make circuit simulators.
|
||||
- if you have an instruction that zeroes out a specific bit in a byte, then if
|
||||
your byte size is 8 (2^3), you can use just 3 bits of your instruction to
|
||||
indicate which bit. x86 doesn’t seem to do this, but the [Z80’s bit testing instructions][18] do.
|
||||
- someone mentioned that some processors use [Carry-lookahead adders][19], and they work
|
||||
in groups of 4 bits. From some quick Googling it seems like there are a wide
|
||||
variety of adder circuits out there though.
|
||||
- **bitmaps**: Your computer’s memory is organized into pages (usually of size 2^n). It
|
||||
needs to keep track of whether every page is free or not. Operating systems
|
||||
use a bitmap to do this, where each bit corresponds to a page and is 0 or 1
|
||||
depending on whether the page is free. If you had a 9-bit byte, you would
|
||||
need to divide by 9 to find the page you’re looking for in the bitmap.
|
||||
Dividing by 9 is slower than dividing by 8, because dividing by powers of 2
|
||||
is always the fastest thing.
|
||||
- it makes it easier to design **clock dividers** that can measure “8 bits were sent on this wire” that work based on halving – you can put 3 halving clock dividers in series. [Graham Sutherland][16] told me about this and made this really cool [simulator of clock dividers][17] showing what these clock dividers look like. That site (Falstad) also has a bunch of other example circuits and it seems like a really cool way to make circuit simulators.
|
||||
- if you have an instruction that zeroes out a specific bit in a byte, then if your byte size is 8 (2^3), you can use just 3 bits of your instruction to indicate which bit. x86 doesn’t seem to do this, but the [Z80’s bit testing instructions][18] do.
|
||||
- someone mentioned that some processors use [Carry-lookahead adders][19], and they work in groups of 4 bits. From some quick Googling it seems like there are a wide variety of adder circuits out there though.
|
||||
- **bitmaps**: Your computer’s memory is organized into pages (usually of size 2^n). It needs to keep track of whether every page is free or not. Operating systems use a bitmap to do this, where each bit corresponds to a page and is 0 or 1 depending on whether the page is free. If you had a 9-bit byte, you would need to divide by 9 to find the page you’re looking for in the bitmap. Dividing by 9 is slower than dividing by 8, because dividing by powers of 2 is always the fastest thing.
|
||||
|
||||
I probably mangled some of those explanations pretty badly: I’m pretty far out
|
||||
of my comfort zone here. Let’s move on.
|
||||
I probably mangled some of those explanations pretty badly: I’m pretty far out of my comfort zone here. Let’s move on.
|
||||
|
||||
#### reason 4: small byte sizes are good
|
||||
|
||||
You might be wondering – well, if 8-bit bytes were better than 4-bit bytes,
|
||||
why not keep increasing the byte size? We could have 16-bit bytes!
|
||||
You might be wondering – well, if 8-bit bytes were better than 4-bit bytes, why not keep increasing the byte size? We could have 16-bit bytes!
|
||||
|
||||
A couple of reasons to keep byte sizes small:
|
||||
|
||||
- It’s a waste of space – a byte is the minimum unit you can address, and if
|
||||
your computer is storing a lot of ASCII text (which only needs 7 bits), it
|
||||
would be a pretty big waste to dedicate 12 or 16 bits to each character when
|
||||
you could use 8 bits instead.
|
||||
- It’s a waste of space – a byte is the minimum unit you can address, and if your computer is storing a lot of ASCII text (which only needs 7 bits), it would be a pretty big waste to dedicate 12 or 16 bits to each character when you could use 8 bits instead.
|
||||
- As bytes get bigger, your CPU needs to get more complex. For example you need one bus line per bit. So I guess simpler is better.
|
||||
|
||||
My understanding of CPU architecture is extremely shaky so I’ll leave it at
|
||||
that. The “it’s a waste of space” reason feels pretty compelling to me though.
|
||||
My understanding of CPU architecture is extremely shaky so I’ll leave it at that. The “it’s a waste of space” reason feels pretty compelling to me though.
|
||||
|
||||
#### reason 5: compatibility
|
||||
|
||||
The Intel 8008 (from 1972) was the precursor to the 8080 (from 1974), which was the precursor to the
|
||||
8086 (from 1976) – the first x86 processor. It seems like the 8080 and the
|
||||
8086 were really popular and that’s where we get our modern x86 computers.
|
||||
The Intel 8008 (from 1972) was the precursor to the 8080 (from 1974), which was the precursor to the 8086 (from 1976) – the first x86 processor. It seems like the 8080 and the 8086 were really popular and that’s where we get our modern x86 computers.
|
||||
|
||||
I think there’s an “if it ain’t broke don’t fix it” thing going on here – I
|
||||
assume that 8-bit bytes were working well, so Intel saw no need to change the
|
||||
design. If you keep the same 8-bit byte, then you can reuse more of your
|
||||
instruction set.
|
||||
I think there’s an “if it ain’t broke don’t fix it” thing going on here – I assume that 8-bit bytes were working well, so Intel saw no need to change the design. If you keep the same 8-bit byte, then you can reuse more of your instruction set.
|
||||
|
||||
Also around the 80s we start getting network protocols like TCP
|
||||
which use 8-bit bytes (usually called “octets”), and if you’re going to be
|
||||
implementing network protocols, you probably want to be using an 8-bit byte.
|
||||
Also around the 80s we start getting network protocols like TCP which use 8-bit bytes (usually called “octets”), and if you’re going to be implementing network protocols, you probably want to be using an 8-bit byte.
|
||||
|
||||
#### that’s all!
|
||||
|
||||
@ -253,29 +175,15 @@ It seems to me like the main reasons for the 8-bit byte are:
|
||||
- 8 is a better number than 7 (because it’s a power of 2)
|
||||
- once you have popular 8-bit computers that are working well, you want to keep the same design for compatibility
|
||||
|
||||
Someone pointed out that [page 65 of this book from 1962][20]
|
||||
talking about IBM’s reasons to choose an 8-bit byte basically says the same thing:
|
||||
Someone pointed out that [page 65 of this book from 1962][20] talking about IBM’s reasons to choose an 8-bit byte basically says the same thing:
|
||||
|
||||
- Its full capacity of 256 characters was considered to be sufficient for the great majority of applications.
|
||||
- Within the limits of this capacity, a single character is represented by a
|
||||
single byte, so that the length of any particular record is not dependent on
|
||||
the coincidence of characters in that record.
|
||||
- 8-bit bytes are reasonably economical of storage space
|
||||
- For purely numerical work, a decimal digit can be represented by only 4
|
||||
bits, and two such 4-bit bytes can be packed in an 8-bit byte. Although such
|
||||
packing of numerical data is not essential, it is a common practice in
|
||||
order to increase speed and storage efficiency. Strictly speaking, 4-bit
|
||||
bytes belong to a different code, but the simplicity of the 4-and-8-bit
|
||||
scheme, as compared with a combination 4-and-6-bit scheme, for example,
|
||||
leads to simpler machine design and cleaner addressing logic.
|
||||
- Byte sizes of 4 and 8 bits, being powers of 2, permit the computer designer
|
||||
to take advantage of powerful features of binary addressing and indexing to
|
||||
the bit level (see Chaps. 4 and 5 ) .
|
||||
> 1. Its full capacity of 256 characters was considered to be sufficient for the great majority of applications.
|
||||
> 2. Within the limits of this capacity, a single character is represented by a single byte, so that the length of any particular record is not dependent on the coincidence of characters in that record.
|
||||
> 3. 8-bit bytes are reasonably economical of storage space
|
||||
> 4. For purely numerical work, a decimal digit can be represented by only 4 bits, and two such 4-bit bytes can be packed in an 8-bit byte. Although such packing of numerical data is not essential, it is a common practice in order to increase speed and storage efficiency. Strictly speaking, 4-bit bytes belong to a different code, but the simplicity of the 4-and-8-bit scheme, as compared with a combination 4-and-6-bit scheme, for example, leads to simpler machine design and cleaner addressing logic.
|
||||
> 5. Byte sizes of 4 and 8 bits, being powers of 2, permit the computer designer to take advantage of powerful features of binary addressing and indexing to the bit level (see Chaps. 4 and 5 ) .
|
||||
|
||||
>
|
||||
|
||||
Overall this makes me feel like an 8-bit byte is a pretty natural choice if
|
||||
you’re designing a binary computer in an English-speaking country.
|
||||
Overall this makes me feel like an 8-bit byte is a pretty natural choice if you’re designing a binary computer in an English-speaking country.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -0,0 +1,284 @@
|
||||
[#]: subject: "Linux Terminal Basics #8: Move Files and Directories (Cut-Paste Operation)"
|
||||
[#]: via: "https://itsfoss.com/move-files-linux/"
|
||||
[#]: author: "Abhishek Prakash https://itsfoss.com/author/abhishek/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Linux Terminal Basics #8: Move Files and Directories (Cut-Paste Operation)
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Cut, copy and paste are part of everyday computing life.
|
||||
|
||||
In the previous chapter, you learned about [copying files and folders][2] (directories) in the terminal.
|
||||
|
||||
In this part of the Terminal Basics series, you'll learn about the cut-paste operation (moving) in the Linux terminal.
|
||||
|
||||
### Moving or cut-paste?
|
||||
|
||||
Alright! Cut-paste is not the correct technical term here. It is called moving files (and folders).
|
||||
|
||||
Since you are new to the command line, you may find the term 'moving' confusing.
|
||||
|
||||
When you copy a file to another location using the **cp** command, the source file remains in the same location.
|
||||
|
||||
When you move a file to another location **using the mv command**, the source file no longer remains in the origin location.
|
||||
|
||||
This is the same cut-paste operation (Ctrl+X and Ctrl+V) you do in a graphical file explorer.
|
||||
|
||||
> 📋 Basically, moving files in the command line can be thought same as cut-paste in a graphical environment.
|
||||
|
||||
### Moving files
|
||||
|
||||
Linux has a dedicated mv command (short for move) for moving files and directories to other locations.
|
||||
|
||||
And [using the mv command][3] is quite simple:
|
||||
|
||||
```
|
||||
mv source_file destination_directory
|
||||
```
|
||||
|
||||
The role of path comes to play here as well. You can use either the [absolute or relative path][4]. Whichever suits your need.
|
||||
|
||||
Let's see this with an example. **You should practice along with it by replicating the example scenarios on your system**.
|
||||
|
||||
This is the directory structure in the example:
|
||||
|
||||
```
|
||||
[email protected]:~/moving_files$ tree
|
||||
.
|
||||
├── dir1
|
||||
│ ├── file_2
|
||||
│ └── file_3
|
||||
├── dir2
|
||||
│ └── passwd
|
||||
├── dir3
|
||||
├── file_1
|
||||
├── file_2
|
||||
├── file_3
|
||||
├── file_4
|
||||
├── passwd
|
||||
└── services
|
||||
|
||||
3 directories, 9 files
|
||||
```
|
||||
|
||||
Now, let's say I want to move the `file_1` to `dir3`.
|
||||
|
||||
```
|
||||
mv file_1 dir3
|
||||
```
|
||||
|
||||
![Example of moving files in Linux using the mv command][5]
|
||||
|
||||
#### Moving multiple files
|
||||
|
||||
You can move multiple files to another location in the same mv command:
|
||||
|
||||
```
|
||||
mv file1 file2 fileN destination_directory
|
||||
```
|
||||
|
||||
Let's continue our example scenario to move multiple files.
|
||||
|
||||
```
|
||||
mv file_2 file_3 file_4 dir3
|
||||
```
|
||||
|
||||
![Example of moving multiple files in Linux][6]
|
||||
|
||||
> 🖥️ Move the files back to the current directory from
|
||||
|
||||
```
|
||||
dir3
|
||||
```
|
||||
|
||||
. We need them in the next examples.
|
||||
|
||||
#### Moving files with caution
|
||||
|
||||
If the destination already has files with the same name, the destination files will be replaced immediately. At times, you won't want that.
|
||||
|
||||
Like the cp command, the mv command also has an interactive mode with option `-i`.
|
||||
|
||||
And the purpose is the same. Ask for confirmation before replacing the files at the destination.
|
||||
|
||||
```
|
||||
[email protected]:~/moving_files$ mv -i file_3 dir1
|
||||
mv: overwrite 'dir1/file_3'?
|
||||
```
|
||||
|
||||
You can press N to deny replacement and Y or Enter to replace the destination file.
|
||||
|
||||
![Example of moving interactively in Linux][7]
|
||||
|
||||
#### Move but only update
|
||||
|
||||
The mv command comes with some special options. One of them is the update option `-u`.
|
||||
|
||||
With this, the destination file will only be replaced if the file being moved is newer than it.
|
||||
|
||||
```
|
||||
mv -u file_name destination_directory
|
||||
```
|
||||
|
||||
Here's an example. file_2 was modified at 10:39 and file_3 was modified at 10:06.
|
||||
|
||||
```
|
||||
[email protected]:~/moving_files$ ls -l file_2 file_3
|
||||
-rw-rw-r-- 1 abhishek abhishek 0 Apr 4 10:39 file_2
|
||||
-rw-rw-r-- 1 abhishek abhishek 0 Apr 4 10:06 file_3
|
||||
```
|
||||
|
||||
In the destination directory dir1, file_2 was last modified at 10:37 and file_3 was modified at 10:39.
|
||||
|
||||
```
|
||||
[email protected]:~/moving_files$ ls -l dir1
|
||||
total 0
|
||||
-rw-rw-r-- 1 abhishek abhishek 0 Apr 4 10:37 file_2
|
||||
-rw-rw-r-- 1 abhishek abhishek 0 Apr 4 10:39 file_3
|
||||
```
|
||||
|
||||
In other words, in the destination directory, the file_2 is older and file_3 is newer than the ones being moved.
|
||||
|
||||
It also means that file_3 won't me moved while as file_2 will be updated. You can verify it with the timestamps of the files in the destination directory after running the mv command.
|
||||
|
||||
```
|
||||
[email protected]:~/moving_files$ mv -u file_2 file_3 dir1
|
||||
[email protected]:~/moving_files$ ls -l dir1
|
||||
total 0
|
||||
-rw-rw-r-- 1 abhishek abhishek 0 Apr 4 10:39 file_2
|
||||
-rw-rw-r-- 1 abhishek abhishek 0 Apr 4 10:39 file_3
|
||||
[email protected]:~/moving_files$ date
|
||||
Tue Apr 4 10:41:16 AM IST 2023
|
||||
[email protected]:~/moving_files$
|
||||
```
|
||||
|
||||
As you can see, the move command was executed at 10:41 and only the timestamp of file_2 has been changed.
|
||||
|
||||
![Using move command with update option][8]
|
||||
|
||||
> 💡 You can also use the backup option
|
||||
|
||||
```
|
||||
-b
|
||||
```
|
||||
|
||||
. If the destination file is being replaced, it will automatically create a backup with the
|
||||
|
||||
```
|
||||
filename~
|
||||
```
|
||||
|
||||
pattern.
|
||||
|
||||
#### Troubleshoot: Target is not a directory
|
||||
|
||||
If you are moving multiple files, the last argument must be a directory. Otherwise, you'll encounter this error:
|
||||
|
||||
```
|
||||
target is not a directory
|
||||
```
|
||||
|
||||
Here, I create a file which is named `dir`. The name sounds like a directory, but it is a file. And when I try to move multiple files to it, the obvious error is there:
|
||||
|
||||
![Handling target is not a directory error in Linux][9]
|
||||
|
||||
But what if you move a single file to another file? In that case, the target file is replaced by the source file's content while the source file is renamed as the target file. More on this in later sections.
|
||||
|
||||
### Moving directories
|
||||
|
||||
So far, you have seen everything about moving files. How about moving directories?
|
||||
|
||||
The cp and rm commands used recusrive option -r to copy and delete folders respectively.
|
||||
|
||||
However, there is no such requirement for the mv command. You can use the mv command as it is for moving directories.
|
||||
|
||||
```
|
||||
mv dir target_directory
|
||||
```
|
||||
|
||||
Here's an example where I move the `dir2` directory to `dir3`. And as you can see, `dir2` along with its content is moved to `dir3`.
|
||||
|
||||
![Moving folders in Linux command line][10]
|
||||
|
||||
You can move multiple directories the same way.
|
||||
|
||||
### Rename files and directories
|
||||
|
||||
If you want to rename a file or directory, you can use the same mv command.
|
||||
|
||||
```
|
||||
mv filename new_name_in_same_or_new_location
|
||||
```
|
||||
|
||||
Let's say you want to rename a file in the same location. Here's an example where I rename `file_1` to `file_one` in the same directory.
|
||||
|
||||
![Rename files with mv command][11]
|
||||
|
||||
You can also move and rename the files. You just have to provide the directory path and the file name of the destination. Here, I rename `services` file to `my_services` while moving it to `dir3`.
|
||||
|
||||
```
|
||||
[email protected]:~/moving_files$ ls
|
||||
dir dir1 dir3 file_2 file_3 file_one passwd services
|
||||
[email protected]:~/moving_files$ mv services dir3/my_services
|
||||
[email protected]:~/moving_files$ ls dir3
|
||||
dir2 my_services
|
||||
```
|
||||
|
||||
> 📋 You cannot rename multiple files directly with mv command. You have to combine it with other commands like find etc.
|
||||
|
||||
### Test your knowledge
|
||||
|
||||
Time to practice what you just learned.
|
||||
|
||||
Create a new folder to practice the exercise. In here, create a directory structure like this:
|
||||
|
||||
```
|
||||
.
|
||||
├── dir1
|
||||
├── dir2
|
||||
│ ├── dir21
|
||||
│ ├── dir22
|
||||
│ └── dir23
|
||||
└── dir3
|
||||
```
|
||||
|
||||
Copy the file /etc/passwd to the current directory. Now rename it `secrets`.
|
||||
|
||||
Make three new files named `file_1`, `file_2` and `file_3`. Move all the files to `dir22`.
|
||||
|
||||
Now move the `dir22` directory to `dir3`.
|
||||
|
||||
Delete all contents of `dir2` now.
|
||||
|
||||
In the penultimate chapter of the Terminal Basics series, you'll learn about editing files in the terminal. Stay tuned.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/move-files-linux/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://itsfoss.com/content/images/2023/03/linux-mega-packt.webp
|
||||
[2]: https://itsfoss.com/copy-files-directory-linux/
|
||||
[3]: https://linuxhandbook.com/mv-command/?ref=itsfoss.com
|
||||
[4]: https://linuxhandbook.com/absolute-vs-relative-path/?ref=itsfoss.com
|
||||
[5]: https://itsfoss.com/content/images/2023/04/moving-files-linux.png
|
||||
[6]: https://itsfoss.com/content/images/2023/04/moving_multiple_files_linux.png
|
||||
[7]: https://itsfoss.com/content/images/2023/04/move-interactively-linux.png
|
||||
[8]: https://itsfoss.com/content/images/2023/04/move-command-update-option.png
|
||||
[9]: https://itsfoss.com/content/images/2023/04/target-is-not-a-directory-error-linux.png
|
||||
[10]: https://itsfoss.com/content/images/2023/04/moving-directories.png
|
||||
[11]: https://itsfoss.com/content/images/2023/04/rename-file-with-mv-command.png
|
@ -0,0 +1,52 @@
|
||||
[#]: subject: "How to resolve Git merge conflicts"
|
||||
[#]: via: "https://opensource.com/article/23/4/resolve-git-merge-conflicts"
|
||||
[#]: author: "Agil Antony https://opensource.com/users/agantony"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
How to resolve Git merge conflicts
|
||||
======
|
||||
|
||||
Suppose you and I are working on the same file called **index.html**. I make some changes to the file, commit them, and push the changes to the remote Git repository. You also make some changes to the same file, commit them, and start pushing the changes to the same Git repository. However, Git detects a conflict because the changes you made conflict with the changes I made.
|
||||
|
||||
Here's how you can resolve the conflict:
|
||||
|
||||
- Fetch and merge the latest changes from the remote repository:`$ git pull`
|
||||
- Identify the one or more conflicting files:`$ git status`
|
||||
- Open the conflicting file using a text editor:`$ vim index.html`
|
||||
- Resolve the conflict. The conflicting changes are marked by `<<<<<<< HEAD` and `>>>>>>>`. You need to choose which changes to keep and which to discard. Manually edit the file to combine the conflicting changes. Here's an example:`<<<<<<< HEAD
|
||||
<div class="header">
|
||||
<h1>Sample text 1</h1>
|
||||
</div>
|
||||
=======
|
||||
<div class="header">
|
||||
<h1>Sample text 2</h1>
|
||||
</div>
|
||||
>>>>>>> feature-branch`In this example, I changed the website heading to `Sample text 1`, while you have changed the heading to `Sample text 2`. Both changes have been added to the file. You can now decide which heading to keep or edit the file to combine the changes. In either case, remove the markers that indicate the beginning and end of the changes, leaving only the code you want:`<div class="header">
|
||||
<h1>Sample text 2</h1>
|
||||
</div>`
|
||||
- Save all of your changes, and close your editor.
|
||||
- Add the file to the staging area:`$ git add index.html`
|
||||
- Commit the changes:`$ git commit -m "Updated h1 in index.html"`This command commits the changes with the message `Resolved merge conflict`.
|
||||
- Push the changes to the remote repository:`$ git push`
|
||||
|
||||
### Resolution
|
||||
|
||||
Merge conflicts are a good reason to focus your changes on code. The more you change in a file, the greater the potential for conflict. You should make more commits with fewer changes each. You should avoid making a monolithic change that combines multiple feature enhancements or bug fixes into one. Your project manager will thank you, too, because commits with clear intent are easier to track. A Git merge conflict may seem intimidating at first, but now that you know how to do it, you'll see that it's easily resolved.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/23/4/resolve-git-merge-conflicts
|
||||
|
||||
作者:[Agil Antony][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/agantony
|
||||
[b]: https://github.com/lkxed/
|
@ -0,0 +1,182 @@
|
||||
[#]: subject: "BASIC vs. FORTRAN 77: Comparing programming blasts from the past"
|
||||
[#]: via: "https://opensource.com/article/23/4/basic-vs-fortran-77"
|
||||
[#]: author: "Jim Hall https://opensource.com/users/jim-hall"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
BASIC vs. FORTRAN 77: Comparing programming blasts from the past
|
||||
======
|
||||
|
||||
If you grew up with computers in the 1970s and 1980s, as I did, you probably learned a common programming language for personal computers called BASIC, or the Beginner's All-purpose Symbolic Instruction Code. You could find BASIC implementations on every personal computer of the era, including the TRS-80, Apple II, and the IBM PC. Back then, I was a self-taught BASIC programmer, experimenting with AppleSoft BASIC on the Apple II before moving to GW-BASIC on the IBM PC and, later, to QuickBASIC on DOS.
|
||||
|
||||
But once upon a time, a popular language for scientific programming was FORTRAN, short for FORmula TRANslation. Although since the 1990 specification of the language, the name is more commonly stylized as "Fortran."
|
||||
|
||||
When I studied physics as a university undergraduate student in the early 1990s, I leveraged my experience in BASIC to learn FORTRAN 77. That was when I realized that BASIC derived many of its concepts from FORTRAN. To be clear, FORTRAN and BASIC differ in lots of other ways, but I found that knowing a little BASIC helped me to learn FORTRAN programming quickly.
|
||||
|
||||
I want to show some similarities between the two languages by writing the same program in both. I'll explore the `FOR` loop in BASIC and FORTRAN 77 by writing a sample program to add a list of numbers from 1 to 10.
|
||||
|
||||
### Bywater BASIC
|
||||
|
||||
BASIC came in many flavors, depending on your computer, but the overall language remained the same. One version of BASIC that I like is [Bywater BASIC][1], an open source implementation of BASIC available for different platforms, including Linux and DOS.
|
||||
|
||||
To use Bywater BASIC on FreeDOS, you must first [install the package][2] from the FreeDOS 1.3 Bonus CD. To run it, go into the C: directory and type `bwbasic`. This command starts the BASIC interpreter. You can enter your program from this prompt:
|
||||
|
||||
```
|
||||
bwBASIC:
|
||||
```
|
||||
|
||||
Bywater BASIC uses an older BASIC programming standard that requires you to write every program instruction with a line number. Think of a line number like an index. You can easily refer to any instruction in the program with line numbers. As you type the program into the Bywater BASIC interpreter, add the line number before each instruction:
|
||||
|
||||
```
|
||||
bwBASIC: 10 print "Add the numbers from 1 to 10 ..."
|
||||
bwBASIC: 20 sum = 0
|
||||
bwBASIC: 30 for i = 1 to 10
|
||||
bwBASIC: 40 sum = sum + i
|
||||
bwBASIC: 50 next i
|
||||
bwBASIC: 60 print sum
|
||||
bwBASIC: 70 end
|
||||
```
|
||||
|
||||
Use the `list` command to view the program you have entered into the interpreter:
|
||||
|
||||
```
|
||||
bwBASIC: list
|
||||
10 print "Add the numbers from 1 to 10 ..."
|
||||
20 sum = 0
|
||||
30 for i = 1 to 10
|
||||
40 sum = sum + i
|
||||
50 next i
|
||||
60 print sum
|
||||
70 end
|
||||
```
|
||||
|
||||
This short program demonstrates the `FOR` loop in BASIC. `FOR` is the most fundamental loop construct in any programming language, allowing you to iterate over a set of values. The general syntax of the `FOR` loop in Bywater BASIC looks like this:
|
||||
|
||||
```
|
||||
FOR var = start TO end
|
||||
```
|
||||
|
||||
In this example program, the instruction `for i = 1 to 10` starts a loop that iterates through the values 1 to 10. At each pass through the loop, the variable `i` is set to the new value.
|
||||
|
||||
In BASIC, all instructions up to the next instruction are executed as part of the `FOR` loop. Because you can put one `FOR` loop inside another, Bywater BASIC uses the syntax `NEXT variable` to specify which loop variable to iterate.
|
||||
|
||||
Type `run` at the prompt to execute the program:
|
||||
|
||||
```
|
||||
bwBASIC: run
|
||||
Add the numbers from 1 to 10 ...
|
||||
55
|
||||
```
|
||||
|
||||
Bywater BASIC is called a BASIC interpreter because you can only run the program from inside the Bywater BASIC environment. This means the interpreter does all the hard work of interacting with the operating system, so your program doesn't need to do that on its own, with the trade-off that the program runs a little slower in the interpreted environment than it might if it were a compiled program.
|
||||
|
||||
### FreeBASIC
|
||||
|
||||
Another popular implementation of BASIC is [FreeBASIC][3], an open source BASIC compiler for several platforms, including Linux and DOS. To use FreeBASIC, you'll need to install the FreeBASIC package from the FreeDOS 1.3 Bonus CD, then change into the C: directory where you'll find the FreeBASIC programs.
|
||||
|
||||
FreeBASIC is a compiler, so you first create a source file with your program instructions, then run the compiler with the source code to create a program you can run. I wrote a similar version of the "add the numbers from 1 to 10" program as this BASIC file, which I saved as `sum.bas`:
|
||||
|
||||
```
|
||||
dim sum as integer
|
||||
dim i as integer
|
||||
print "Add the numbers from 1 to 10 ..."
|
||||
sum = 0
|
||||
for i = 1 to 10
|
||||
sum = sum + i
|
||||
next
|
||||
print sum
|
||||
end
|
||||
```
|
||||
|
||||
If you compare this code to the Bywater BASIC version of the program, you may notice that FreeBASIC doesn't require line numbers. FreeBASIC implements a more modern version of BASIC that makes it easier to write programs without keeping track of line numbers.
|
||||
|
||||
Another key difference is that you must define or declare your variables in your source code. Use the `DIM` instruction to declare a variable in FreeBASIC, such as `dim sum as integer`, to define an integer variable called `sum`.
|
||||
|
||||
Now you can compile the BASIC program using `fbc` on the command line:
|
||||
|
||||
```
|
||||
C:\DEVEL\FBC> fbc sum.bas
|
||||
```
|
||||
|
||||
If your code doesn't have any errors in it, the compiler generates a program that you can run. For example, my program is now called `sum`. Running my program adds up the numbers from 1 to 10:
|
||||
|
||||
```
|
||||
C:\DEVEL\FBC> sum
|
||||
Add the numbers from 1 to 10 ...
|
||||
55
|
||||
```
|
||||
|
||||
### FORTRAN 77
|
||||
|
||||
The FORTRAN programming language is like a hybrid between old-style and modern BASIC. FORTRAN came before BASIC, and BASIC clearly took inspiration from FORTRAN, just as later versions of FORTRAN took cues from BASIC. You write FORTRAN programs as source code in a file but you don't use line numbers everywhere. However, FORTRAN 77 does use line numbers (called labels) for certain instructions, including the `FOR` loop. Although in FORTRAN 77, the `FOR` is actually called a `DO` loop, it does the same thing and has almost the same usage.
|
||||
|
||||
In FORTRAN 77, the `DO` loop syntax looks like this:
|
||||
|
||||
```
|
||||
DO label var = start, end
|
||||
```
|
||||
|
||||
This situation is one of the instances where you need a line number to indicate where the `DO` loop ends. You used a `NEXT` instruction in BASIC, but FORTRAN requires a line label instead. Typically, that line is a `CONTINUE` instruction.
|
||||
|
||||
Look at this sample FORTRAN program to see how to use `DO` to loop over a set of numbers. I saved this source file as `sum.f`:
|
||||
|
||||
```
|
||||
PROGRAM MAIN
|
||||
INTEGER SUM,I
|
||||
PRINT *, 'ADD THE NUMBERS FROM 1 TO 10 ...'
|
||||
SUM = 0
|
||||
DO 10 I = 1, 10
|
||||
SUM = SUM + I
|
||||
10 CONTINUE
|
||||
PRINT *, SUM
|
||||
END
|
||||
```
|
||||
|
||||
In FORTRAN, every program needs to start with the PROGRAM instruction, with a name for the program. You might name this program `SUM`, but then you cannot use the variable `SUM` later in the program. When I learned FORTRAN, I borrowed from C programming and started all of my FORTRAN programs with `PROGRAM MAIN`, like the `main()` function in C programs, because I was unlikely to use a variable called `MAIN`.
|
||||
|
||||
The `DO` loop in FORTRAN is similar to the `FOR` loop in BASIC. It iterates over values from 1 to 10. The variable `I` gets the new value at each pass over the loop. This allows you to add each number from 1 to 10 and print the sum when you're done.
|
||||
|
||||
You can find FORTRAN compilers for every platform, including Linux and DOS. FreeDOS 1.3 includes the OpenWatcom FORTRAN compiler on the Bonus CD. On Linux, you may need to install a package to install GNU Fortran support in the GNU Compiler Collection (GCC). On Fedora Linux, you use the following command to add GNU Fortran support:
|
||||
|
||||
```
|
||||
$ sudo dnf install gcc-gfortran
|
||||
```
|
||||
|
||||
Then you can compile `sum.f` and run the program with these commands:
|
||||
|
||||
```
|
||||
$ gfortran -o sum sum.f
|
||||
$ ./sum
|
||||
ADD THE NUMBERS FROM 1 TO 10 ...
|
||||
55
|
||||
```
|
||||
|
||||
### A few differences
|
||||
|
||||
I find that FORTRAN and BASIC are very similar, but with some differences. The core languages are different, but if you know a little of BASIC, you can learn FORTRAN. And if you know some FORTRAN, you can learn BASIC.
|
||||
|
||||
If you want to explore both of these languages, here are a few things to keep in mind:
|
||||
|
||||
- **FORTRAN 77 uses all uppercase,** but later versions of FORTRAN allow mixed cases as long as you use the same capitalization for variables, functions, and subroutines. Most implementations of BASIC are case-insensitive, meaning you can freely mix uppercase and lowercase letters.
|
||||
- **There are many different versions of BASIC,** but they usually do the same thing. If you learn one BASIC implementation, you can easily learn how to use a different one. Watch for warnings or error messages from the BASIC interpreter or compiler, and explore the manual to find the differences.
|
||||
- **Some BASIC implementations require line numbers**, such as Bywater BASIC and GW-BASIC. More modern BASIC versions allow you to write programs without line numbers. FreeBASIC requires the `-lang` deprecated option to compile programs with line numbers.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/23/4/basic-vs-fortran-77
|
||||
|
||||
作者:[Jim Hall][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jim-hall
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://sourceforge.net/projects/bwbasic/
|
||||
[2]: https://opensource.com/article/21/6/freedos-package-manager
|
||||
[3]: https://www.freebasic.net/
|
@ -0,0 +1,149 @@
|
||||
[#]: subject: "Our favorite fonts for the Linux terminal"
|
||||
[#]: via: "https://opensource.com/article/23/4/linux-terminal-fonts"
|
||||
[#]: author: "Jim Hall https://opensource.com/users/jim-hall"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Our favorite fonts for the Linux terminal
|
||||
======
|
||||
|
||||
Terminal emulators came up as a topic for me recently, and it got me thinking: What's everyone's favorite terminal font?
|
||||
|
||||
So I asked Opensource.com contributors to share what font they like to use. Here are their answers.
|
||||
|
||||
### VT323
|
||||
|
||||
I like to use a different font ([VT323][1]) in my GNOME Terminal than the font I use (Source Code Pro) in my programming editors or other apps that use a monospace font. I just like the look of the classic VT-style font.
|
||||
|
||||
Sometimes, I switch to the original IBM EGA font, because to my eye it looks really nice. But I associate EGA with DOS, and I associate VT323 with classic Unix terminals, so I use VT323 most of the time. Here's my screenshot of GNOME Terminal using VT323 as the monospace font:
|
||||
|
||||
![gnome-terminal1108×926 output][2]
|
||||
|
||||
I set up the terminal using VT323 at 24 pt, which gives a nice big window. If I'm going to bring up a terminal window, I want to really use it to do real work, not just do one thing and exit. I'm probably going to stay in that terminal window for a while, so it should be big and easy to see. I also prefer 80x25, because I'm an old DOS command line guy and 25 lines looks "right" to my eyes:
|
||||
|
||||
![preference profile screen - text appearance][3]
|
||||
|
||||
**—[Jim Hall][4]**
|
||||
|
||||
### Monospaced fonts
|
||||
|
||||
I don't know that I have a specific font that I use. I usually use either [DejaVu][5] or [Liberation][6] Mono. I like monospaced fonts because they're easier to read. Even then, I don't want the letters to be too close together. The main thing is being able to tell a small "L" from the number 1, Q from O, and so on. It's also nice to have all special characters stand out clearly.
|
||||
|
||||
I also like a good contrast between the font and background, so I set the background to black and characters to white.
|
||||
|
||||
**—[Greg Pittman][7]**
|
||||
|
||||
### Hack
|
||||
|
||||
I like to use monospaced fonts, particularly for the terminal and coding because they're easier to read. I've been using the [Hack][8] font family for years. It provides a nice monospace font combined with additional glyphs and Powerline characters that I can use to display status on the command line.
|
||||
|
||||
![Command line][9]
|
||||
|
||||
Here's the font preview generated with [Fontpreview tool][10].
|
||||
|
||||
![Display of font A-Z and the numbers][11]
|
||||
|
||||
**—[Ricardo Gerardi][12]**
|
||||
|
||||
### Victor Mono
|
||||
|
||||
I've been using [Victor Mono][13] for both my terminal and IDE for a few years. It's perhaps a bit of an acquired taste, because the italic characters are in a monospace cursive script. I like this because code comments have a distinct look that's noticeably different from the rest of the code. Here's what it looks like in a terminal:
|
||||
|
||||
![terminal font is italic characters in a monospace cursive script][14]
|
||||
|
||||
I also use the Solarized Dark color scheme for both my IDE and the terminal. If I could, anything work or code related would all use the same color scheme, and the same fonts, so it all looks uniform. I've gone through various fonts over the years, but I always try to use the same thing in my terminal as in my IDE.
|
||||
|
||||
**—[Chris Reynolds][15]**
|
||||
|
||||
### Monaco
|
||||
|
||||
At the moment, Monaco regular at 16 pt (so much better than 12 or 14 pt fonts).
|
||||
|
||||
**—[Jenny Panighetti][16]**
|
||||
|
||||
### Keeping it pretty in pink
|
||||
|
||||
This reminds me of the time I pranked the other devs on my team by making all of their terminals pink (I was the only woman). It was like a baby pink background with hot pink text.
|
||||
|
||||
I like to make my personal terminals pretty, but for work I leave it white on black and just make the font around 16 pt because I appreciate the larger font size.
|
||||
|
||||
I just leave my terminal set to SF Mono font, but I make it bolder and bump it up a few points so I can see it better. My favorite colors to use are pink or a nice aqua blue color. I also like the default style the terminal has, called "Novel".
|
||||
|
||||
![Two open terminals, one pink and one aqua blue][17]
|
||||
|
||||
**—[RachieVee][18]**
|
||||
|
||||
### Let's ask ChatGPT
|
||||
|
||||
For the heck of it, I decided to ask [ChatGPT][19]. Here was its response:
|
||||
|
||||
> As a language model, I do not have personal preferences or the ability to have a favorite font. However, some popular terminal fonts include Monospace, Consolas, and Inconsolata. These fonts are popular because they are easy to read, have clear distinctions between different characters, and are highly legible even at small font sizes. Additionally, they are designed to work well with a variety of different operating systems and terminals, making them versatile choices for developers and users alike.
|
||||
|
||||
I generated this text in part with GPT-3, OpenAI's large-scale language-generation model. Upon generating draft language, the author reviewed, edited, and revised the language to my own liking and takes ultimate responsibility for the content of this publication.
|
||||
|
||||
**—[Stephanie Brinley][20]**
|
||||
|
||||
### Fantasque Sans Mono
|
||||
|
||||
```
|
||||
$ grep font ~/.Xdefaults
|
||||
URxvt*font: xft:FantasqueSansMono-Regular:pixelsize=12:antialias=true
|
||||
```
|
||||
|
||||
I don't remember when I settled on [Fantasque Sans Mono][21], but I guess it's been my default for the past 8 years now, both in [Rxvt][22] and Konsole. I don't know what font I'm using in my GNOME terminal. Probably whatever the default is on GNOME.
|
||||
|
||||
**—[Seth Kenlon][23]**
|
||||
|
||||
### Jetbrains Mono
|
||||
|
||||
Lately, I have Tilix set as my default terminal. My Tilix config has similar settings to what Jim Hall uses. The few differences are:
|
||||
|
||||
- Cursor shape is underline instead of a block
|
||||
- Font is [Jetbrains Mono][24] Nerd Font Mono Medium 14
|
||||
|
||||
![Black terminal with blue text][25]
|
||||
|
||||
**—[Alan Formy-Duval][26]**
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/23/4/linux-terminal-fonts
|
||||
|
||||
作者:[Jim Hall][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jim-hall
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://fontsource.org/fonts/vt323
|
||||
[2]: https://opensource.com/sites/default/files/2023-01/gnome-terminal1108%C3%97926.png
|
||||
[3]: https://opensource.com/sites/default/files/2023-01/gnome-terminal-vt323848%C3%97661.png
|
||||
[4]: https://opensource.com/users/jim-hall
|
||||
[5]: https://fontsource.org/fonts/dejavu-mono
|
||||
[6]: https://github.com/liberationfonts
|
||||
[7]: https://opensource.com/users/greg-p
|
||||
[8]: https://sourcefoundry.org/hack/
|
||||
[9]: https://opensource.com/sites/default/files/2023-01/Geradi%201.png
|
||||
[10]: https://github.com/sdushantha/fontpreview
|
||||
[11]: https://opensource.com/sites/default/files/2023-01/fontpreview_default.png
|
||||
[12]: https://opensource.com/users/rgerardi
|
||||
[13]: https://rubjo.github.io/victor-mono/
|
||||
[14]: https://opensource.com/sites/default/files/2023-01/reynolds1.png
|
||||
[15]: https://opensource.com/users/jazzsequence
|
||||
[16]: https://twitter.com/elvenjen
|
||||
[17]: https://opensource.com/sites/default/files/2023-01/pink-blue.webp
|
||||
[18]: https://opensource.com/users/rachievee
|
||||
[19]: https://opensource.com/article/23/2/chatgpt-vs-community
|
||||
[20]: https://opensource.com/users/sbrinley
|
||||
[21]: https://github.com/belluzj/fantasque-sans
|
||||
[22]: https://opensource.com/article/19/10/why-use-rxvt-terminal
|
||||
[23]: https://opensource.com/users/seth
|
||||
[24]: https://www.jetbrains.com/lp/mono/
|
||||
[25]: https://opensource.com/sites/default/files/2023-01/alan.png
|
||||
[26]: https://opensource.com/users/alanfdoss
|
@ -0,0 +1,216 @@
|
||||
[#]: subject: "Make a web-safe color guide with Bash"
|
||||
[#]: via: "https://opensource.com/article/23/4/web-safe-color-guide-bash"
|
||||
[#]: author: "Jim Hall https://opensource.com/users/jim-hall"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Make a web-safe color guide with Bash
|
||||
======
|
||||
|
||||
When computer displays had a limited color palette, web designers often used a set of [web-safe colors][1] to create websites. While modern websites displaying on newer devices can display many more colors than the original web-safe color palette, I sometimes like to refer to the web-safe colors when I create web pages. This way I know my pages look good anywhere.
|
||||
|
||||
You can find web-safe color palettes on the web, but I wanted to have my own copy for easy reference. And you can make one too, using the `for` loop in Bash.
|
||||
|
||||
### Bash for loop
|
||||
|
||||
The syntax of a [for loop in Bash][2] looks like this:
|
||||
|
||||
> for _variable_ in _set_ ; do _statements_ ; done
|
||||
|
||||
As an example, say you want to print all numbers from 1 to 3. You can write a quick `for` loop on the Bash command line to do that for you:
|
||||
|
||||
```
|
||||
$ for n in 1 2 3 ; do echo $n ; done
|
||||
1
|
||||
2
|
||||
3
|
||||
```
|
||||
|
||||
The semicolons are a standard Bash statement separator. They let you write multiple commands on a single line. If you were to include this `for` loop in a Bash script file, you might instead replace the semicolons with line breaks and write out the `for` loop like this:
|
||||
|
||||
```
|
||||
for n in 1 2 3
|
||||
do
|
||||
echo $n
|
||||
done
|
||||
```
|
||||
|
||||
I like to include the `do` on the same line as the `for` so it's easier for me to read:
|
||||
|
||||
```
|
||||
for n in 1 2 3 ; do
|
||||
echo $n
|
||||
done
|
||||
```
|
||||
|
||||
### More than one for loop at a time
|
||||
|
||||
You can put one loop inside another. That can help you to iterate over several variables, to do more than one thing at a time. Let's say you wanted to print out all combinations of the letters A, B, and C with the numbers 1, 2, and 3. You can do that with two `for` loops in Bash, like this:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
for number in 1 2 3 ; do
|
||||
for letter in A B C ; do
|
||||
echo $letter$number
|
||||
done
|
||||
done
|
||||
```
|
||||
|
||||
If you put these lines in a Bash script file called `for.bash` and run it, you see nine lines showing the combinations of all the letters paired with each of the numbers:
|
||||
|
||||
```
|
||||
$ bash for.bash
|
||||
A1
|
||||
B1
|
||||
C1
|
||||
A2
|
||||
B2
|
||||
C2
|
||||
A3
|
||||
B3
|
||||
C3
|
||||
```
|
||||
|
||||
### Looping through the web-safe colors
|
||||
|
||||
The web-safe colors are all colors from hexadecimal color `#000` (black, where the red, green, and blue values are all zero) to `#fff` (white, where the red, green, and blue colors are all at their full intensities), stepping through each hexadecimal value as 0, 3, 6, 9, c, and f.
|
||||
|
||||
You can generate a list of all combinations of the web-safe colors using three `for` loops in Bash, where the loops iterate over the red, green, and blue values.
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
for r in 0 3 6 9 c f ; do
|
||||
for g in 0 3 6 9 c f ; do
|
||||
for b in 0 3 6 9 c f ; do
|
||||
echo "#$r$g$b"
|
||||
done
|
||||
done
|
||||
done
|
||||
```
|
||||
|
||||
If you save this in a new Bash script called `websafe.bash` and run it, you see an iteration of all the web safe colors as hexadecimal values:
|
||||
|
||||
```
|
||||
$ bash websafe.bash | head
|
||||
#000
|
||||
#003
|
||||
#006
|
||||
#009
|
||||
#00c
|
||||
#00f
|
||||
#030
|
||||
#033
|
||||
#036
|
||||
#039
|
||||
```
|
||||
|
||||
To make an HTML page that you can use as a reference for web-safe colors, you need to make each entry a separate HTML element. Put each color in a `<div>` element, and set the background to the web-safe color. To make the hexadecimal value easier to read, put it inside a separate `<code>` element. Update the Bash script to look like this:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
for r in 0 3 6 9 c f ; do
|
||||
for g in 0 3 6 9 c f ; do
|
||||
for b in 0 3 6 9 c f ; do
|
||||
echo "<div style='background-color:#$r$g$b'><code>#$r$g$b</code></div>"
|
||||
done
|
||||
done
|
||||
done
|
||||
```
|
||||
|
||||
When you run the new Bash script and save the results to an HTML file, you can view the output in a web browser to all the web-safe colors:
|
||||
|
||||
```
|
||||
$ bash websafe.bash > websafe.html
|
||||
```
|
||||
|
||||
![Colour gradient.][3]
|
||||
|
||||
The web page isn't very nice to look at. The black text on a dark background is impossible to read. I like to apply some HTML styling to ensure the hexadecimal values are displayed with white text on a black background inside the color rectangle. To make the page look really nice, I also use HTML grid styles to arrange the boxes with six per row and some space between each box.
|
||||
|
||||
To add this extra styling, you need to include the other HTML elements before and after the for loops. The HTML code at the top defines the styles and the HTML code at the bottom closes all the open HTML tags:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
cat<<EOF
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Web-safe colors</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<style>
|
||||
div {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media only screen and (min-width:600px) {
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6,1fr);
|
||||
column-gap: 1em;
|
||||
row-gap: 1em;
|
||||
}
|
||||
|
||||
div {
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</body>
|
||||
EOF
|
||||
|
||||
for r in 0 3 6 9 c f ; do
|
||||
for g in 0 3 6 9 c f ; do
|
||||
for b in 0 3 6 9 c f ; do
|
||||
|
||||
echo "<div
|
||||
style='background-color:#$r$g$b'><code>#$r$g$b</code></div>"
|
||||
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
cat<<EOF
|
||||
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
```
|
||||
|
||||
This finished Bash script generates a web-safe color guide in HTML. Whenever you need to refer to the web-safe colors, run the script and save the results to an HTML page. Now you can see a representation of the web-safe colors in your browser as an easy-reference guide for your next web project:
|
||||
|
||||
```
|
||||
$ bash websafe.bash > websafe.html
|
||||
```
|
||||
|
||||
![Web colors.][4]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/23/4/web-safe-color-guide-bash
|
||||
|
||||
作者:[Jim Hall][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jim-hall
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://en.wikipedia.org/wiki/Web_colors#Web-safe_colors
|
||||
[2]: https://opensource.com/article/19/6/how-write-loop-bash
|
||||
[3]: https://opensource.com/sites/default/files/2023-03/10000001000002620000013685924861376B1AB6.webp
|
||||
[4]: https://opensource.com/sites/default/files/2023-03/10000001000002620000013633233DC8DC56C891.webp
|
@ -0,0 +1,213 @@
|
||||
[#]: subject: "A Quick Guide to Install and Play GOG Games on Linux"
|
||||
[#]: via: "https://itsfoss.com/play-gog-games-linux/"
|
||||
[#]: author: "Ankush Das https://itsfoss.com/author/ankush/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
A Quick Guide to Install and Play GOG Games on Linux
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
[Gaming on Linux][2] is no longer a problem. You can play plenty of AAA titles, indie games, and Windows-exclusive games on Linux. Several games from GOG, Steam, Epic Games, Origin, and Ubisoft Connect should work flawlessly.
|
||||
|
||||
Unfortunately, GOG does not offer a client for Linux that you can use.
|
||||
|
||||
So, in this guide, I will be focusing on **installing and playing GOG games on Linux**.
|
||||
|
||||
If you have been following us, you may have come across on our ultimate guide to the [Epic Games Store on Linux][3]. It is more or less the same thing, but for a different store.
|
||||
|
||||
> 💡 GOG.com is popular for offering DRM-free games. Furthermore, if you make a purchase on GOG, usually, the developer gets a good cut of it compared to other stores.
|
||||
|
||||
### 3 Ways to Install GOG Games on Linux
|
||||
|
||||
You have a couple of options when it comes to installing and running a game from the GOG store.
|
||||
|
||||
You can use any of the following game clients on Linux:
|
||||
|
||||
- **Lutris**
|
||||
- **Heroic Games Launcher**
|
||||
- **Bottles**
|
||||
|
||||
I found Lutris to be the easiest, and quickest to be able to run a Windows-exclusive GOG game on Linux. So, let me start with it.
|
||||
|
||||
#### Method 1. Install and Play GOG Games Using Lutris
|
||||
|
||||
1. To get started, you need to **install Lutris on Linux**.
|
||||
|
||||
You can install it using [Flathub][4], PPA for Ubuntu-based distros, DEB package or from the software center of distros like Pop!_OS, Solus.
|
||||
|
||||
Head to its [official download page][5] and install Lutris.
|
||||
|
||||
[Install Lutris][5]
|
||||
|
||||
2. Once you are done installing Lutris, launch it and click on “**GOG**” among the sources listed on its left sidebar, as shown in the image:
|
||||
|
||||
![lutris game client gog source][6]
|
||||
|
||||
Do you see a **user avatar icon**, right next to it? Click on it to log in to your GOG account.
|
||||
|
||||
![gog sign in through lutris][7]
|
||||
|
||||
You can access the library of games associated with your account after signing in through Lutris' native user interface.
|
||||
|
||||
![][8]
|
||||
|
||||
3. Pick any game you want, and click on it to find "**Install**" button.
|
||||
|
||||
As you proceed, Lutris will prompt you to **install Wine**, which would enable you to run the Windows game on Linux.
|
||||
|
||||
![][9]
|
||||
|
||||
**(Optional)** You can separately install Wine under the "**Runners**" menu, and have multiple versions of it ready before installing the game if you prefer.
|
||||
|
||||
![lutris wine manager][10]
|
||||
|
||||
4. But, if you do not want any hassle, just go with the installation process, and it will automatically install Wine and then prompt you to download the game
|
||||
|
||||
![][11]
|
||||
|
||||
![][12]
|
||||
|
||||
5. Continue with the process, and it will set up the installer for you and launch it.
|
||||
|
||||
![][13]
|
||||
|
||||
![][14]
|
||||
|
||||
Now, you have to follow the on-screen instructions for any game you want to install and then complete it.
|
||||
|
||||
> 📋 Not every game will work seamlessly. For some, you may have to install it using a specific Wine version and some may not work at all. So, you should do some research on the particular game running on Linux before trying to install it via GOG.
|
||||
|
||||
It is done! The game should launch right after successful setup.
|
||||
|
||||
![Playing a GOG Windows game on Linux][15]
|
||||
|
||||
#### Method 2. Install and Play GOG Games on Linux Using Heroic Games Launcher
|
||||
|
||||
Heroic Games Launcher is a nice option with several features to run GOG games on Linux.
|
||||
|
||||
You can use an **AppImage** file available, or install its Flatpak via **Flathub** or get RPM/DEB packages from its GitHub.
|
||||
|
||||
[Install Heroic Games Launcher][16]
|
||||
|
||||
> 🚧 You get similar functionalities but unlike Lutris, but you need to first install Wine manually using its
|
||||
|
||||
**Wine Manager**
|
||||
|
||||
. Heroic Games Launcher does not automatically install Wine for you. It will download the game for you, even if you do not have Wine installed.
|
||||
|
||||
It can be confusing for new users. But, if you have a bit of experience and want to choose the Wine/Proton version, you can head to its **Wine Manager** and preferably download the latest available version to get started.
|
||||
|
||||
![wine manager listing several versions of wine/proton on heroic games launcher][17]
|
||||
|
||||
No additional fiddling, **just click on the download icon** as shown in the screenshot above, and it will automatically install it.
|
||||
|
||||
Once done with it, here are the steps to install a GOG game using Heroic:
|
||||
|
||||
1. Log in to your GOG account. You can find the GOG menu right after you launch it and head to the **Login section**.
|
||||
|
||||
![][18]
|
||||
|
||||
![][19]
|
||||
|
||||
2. After logging in, head to the **Library** to find the games you have.
|
||||
|
||||
![gog game library on heroic games launcher][20]
|
||||
|
||||
3. Click on the download icon to proceed. Now, you should get a prompt to decide the **installation path, Wine version**, and a couple of other options.
|
||||
|
||||
![gog game installation screen on heroic games launcher selecting the install path, wine, and more][21]
|
||||
|
||||
You can select "**Use Default Wine Settings**" if you want to automatically select the Wine version you have installed.
|
||||
|
||||
Or, you can use the **drop-down arrow** to pick among the Wine/Proton version available on your system.
|
||||
|
||||
For the ease of use, you can go with the default settings. And, if the game fails to work, you can go to its settings later and try other Wine versions.
|
||||
|
||||
4. Wait for the download to complete and then launch/run the game.
|
||||
|
||||
![][22]
|
||||
|
||||
![][23]
|
||||
|
||||
#### Method 3: Install and Play GOG Games on Linux Using Bottles
|
||||
|
||||
Bottles is an impressive platform. However, it does not let you install games through it.
|
||||
|
||||
> 🚧 This is not a recommended method. But, if you want to try the GOG Galaxy game client on Linux, Bottles is the way to go.
|
||||
|
||||
Instead, it will help you install the GOG client (which you find for Windows) and make it work on Linux. Bottles is one of the best [ways to install a Windows program on Linux][24].
|
||||
|
||||
It is recommended to install Bottles as Flatpak, at the time of writing this. So, to get started, you need to get it installed from [Flathub][25]. Additionally, you can explore other download options, if available.
|
||||
|
||||
[Download Bottles][26]
|
||||
|
||||
Once you get it installed, you have to create a new Bottle for Gaming. And, inside it, you will have to search for GOG Galaxy v1 or legacy and install the program to use GOG on Linux.
|
||||
|
||||
![][27]
|
||||
|
||||
![][28]
|
||||
|
||||
In my tests, GOG Galaxy client did not launch. And, when it did, it was too slow/unresponsive. But, at least, it is something you can explore when nothing else works for you. It may or may not work, of course.
|
||||
|
||||
If this is something that interests you, feel free to give it a try.
|
||||
|
||||
### Wrapping Up
|
||||
|
||||
The installers or gaming clients on Linux are making things convenient every day.
|
||||
|
||||
For some games, it can end up as a one-click installation experience, while for others, it might need a little tweaking.
|
||||
|
||||
If you struggle with it, feel free to join our **[It's FOSS forums][29]** for help. And, if you are new to the gaming scene on Linux, I suggest you read our guide on it:
|
||||
|
||||
There is also a handy utility called GameHub that can be utilized for keeping games from different platforms in one UI.
|
||||
|
||||
And of course, GOG is not the only place for [getting Linux games][30].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/play-gog-games-linux/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://itsfoss.com/content/images/2023/03/linux-mega-packt.webp
|
||||
[2]: https://itsfoss.com/linux-gaming-guide/
|
||||
[3]: https://itsfoss.com/epic-games-linux/#3-use-bottles-to-access-epic-games-store
|
||||
[4]: https://flathub.org/apps/details/net.lutris.Lutris?ref=itsfoss.com
|
||||
[5]: https://lutris.net/downloads?ref=itsfoss.com
|
||||
[6]: https://itsfoss.com/content/images/2023/04/lutris-gog.png
|
||||
[7]: https://itsfoss.com/content/images/2023/04/lutris-gog-login.png
|
||||
[8]: https://itsfoss.com/content/images/2023/04/lutris-game-install.png
|
||||
[9]: https://itsfoss.com/content/images/2023/04/install-wine-lutris.png
|
||||
[10]: https://itsfoss.com/content/images/2023/04/lutris-wine-manager.png
|
||||
[11]: https://itsfoss.com/content/images/2023/04/lutris-wine-download.png
|
||||
[12]: https://itsfoss.com/content/images/2023/04/lutris-download-game.png
|
||||
[13]: https://itsfoss.com/content/images/2023/04/lutris-game-installer.png
|
||||
[14]: https://itsfoss.com/content/images/2023/04/lutris-game-installation-1.png
|
||||
[15]: https://itsfoss.com/content/images/2023/04/lutris-game-working.jpg
|
||||
[16]: https://heroicgameslauncher.com/downloads?ref=itsfoss.com
|
||||
[17]: https://itsfoss.com/content/images/2023/04/heroic-games-launcher-wine.png
|
||||
[18]: https://itsfoss.com/content/images/2023/04/heroic-games-gog-login.png
|
||||
[19]: https://itsfoss.com/content/images/2023/04/heroic-gog-login-page.png
|
||||
[20]: https://itsfoss.com/content/images/2023/04/heroic-gog-games-library.png
|
||||
[21]: https://itsfoss.com/content/images/2023/04/gog-wine-path-install.png
|
||||
[22]: https://itsfoss.com/content/images/2023/04/gog-game-download-heroic.png
|
||||
[23]: https://itsfoss.com/content/images/2023/04/gog-game-heroic-game-play.png
|
||||
[24]: https://itsfoss.com/use-windows-applications-linux/
|
||||
[25]: https://flathub.org/apps/details/com.usebottles.bottles?ref=itsfoss.com
|
||||
[26]: https://usebottles.com/download/?ref=itsfoss.com
|
||||
[27]: https://itsfoss.com/content/images/2023/04/gog-installer-1.png
|
||||
[28]: https://itsfoss.com/content/images/2023/04/install-gog-client-1.png
|
||||
[29]: https://itsfoss.community/?ref=itsfoss.com
|
||||
[30]: https://itsfoss.com/download-linux-games/
|
@ -0,0 +1,60 @@
|
||||
[#]: subject: "5 best practices for PatternFly, an open source design system"
|
||||
[#]: via: "https://opensource.com/article/23/4/open-source-design-system-patternfly"
|
||||
[#]: author: "Abigael Donahue https://opensource.com/users/abigaeljamie"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
5 best practices for PatternFly, an open source design system
|
||||
======
|
||||
|
||||
Have you ever admired the facets of a gemstone? The angles and slants are a thing of beauty. You can see that a multi-faceted gemstone shines brighter than a flat one. You may also see this kind of beauty when analyzing a multi-faceted design system. A design system is a collection of guidelines, standards, and resources for creating consistent and unified user interfaces (UI). Like the facets of a diamond, an open source design system rich with diverse contributions and community engagement ultimately leads to better product experiences.
|
||||
|
||||
The [PatternFly][1] project is an open source design system for Red Hat products. But open source doesn't end with PatternFly's code. Behind PatternFly is a team of people who create designs completely in the open. From designers and developers to researchers and writers, we work together to operate as an open source community.
|
||||
|
||||
Our secret? We don't have one — we work in the open, remember? However, we use these five best practices. I'll share them here so that you too can power your own design system with open source.
|
||||
|
||||
### 1. Contribute collectively
|
||||
|
||||
We have a core PatternFly design team to design, maintain, and evolve the design system. But we encourage and welcome contributions from everyone. If you have a passion for collaboration and a knack for user experience (UX), [PatternFly wants to hear from you][2].
|
||||
|
||||
### 2. Build community
|
||||
|
||||
Nothing created in a silo makes its way to PatternFly. We believe design is better in the open. This is why we include the community in all updates, changes, and additions. We collect feedback on contributions from people across design and development so that everyone has a say in what gets implemented. We also seek input and collaboration from people across multiple [design disciplines][3]. This is done to break free from any bias or assumption. This kind of open design makes our design system stronger. It also strengthens our blossoming community of people who engage with or contribute to PatternFly (we lovingly refer to them as Flyers).
|
||||
|
||||
### 3. Loop in everyone
|
||||
|
||||
If you find that brainstorming ideas with others results in solutions better than any one person would have dreamed of, then you already think like a Flyer. We have regular design meetings where contributors present their ideas and discuss design approaches in a group setting. This enables us to keep our ideas collaborative and consider designs from all angles. Additionally, we host monthly community meetings so that we can connect with Flyers from across the globe and share the latest updates. You can catch all of our past meeting recordings on our [PatternFly YouTube channel][4].
|
||||
|
||||
### 4. Listen to users
|
||||
|
||||
As a community, we aim to have all PatternFly contributions lead to functional and beautiful product experiences across different contexts. To make that a reality, we hold ourselves accountable to break out of our own bubbles and engage with users. We work with UX researchers to test updates, changes, and additions with users — such as visual themes and interactions — to ensure that we're creating designs, resources, and experiences that solve for everyone, not just people like us.
|
||||
|
||||
### 5. Create connections
|
||||
|
||||
PatternFly is the thread of consistency through products across Red Hat's portfolio. Everyone has the creative freedom to build what best serves their users. But we work as a team to connect product groups through the design system for a more unified user experience. PatternFly resources are easy to access and open to all. This helps us create connections and squash silos.
|
||||
|
||||
### Come design in the open with us
|
||||
|
||||
Whether you're a team of 1 or 100, or whether your design system is open source or not — there's always room for a little collaboration and community in everything we do. Tell us how things turn out for you by connecting with the [PatternFly community][5]. We can't wait to hear from you.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/23/4/open-source-design-system-patternfly
|
||||
|
||||
作者:[Abigael Donahue][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/abigaeljamie
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://www.patternfly.org/v4/
|
||||
[2]: https://www.patternfly.org/v4/contribute/about
|
||||
[3]: https://design.redhat.com/?intcmp=7013a000002qLH8AAM
|
||||
[4]: https://www.youtube.com/channel/UCqLT0IEvYmb8z__9IFLSVyQ
|
||||
[5]: https://www.patternfly.org/v4/community
|
@ -0,0 +1,118 @@
|
||||
[#]: subject: "CachyOS: Arch-based Distro for Speed and Ease of Use"
|
||||
[#]: via: "https://news.itsfoss.com/cachyos/"
|
||||
[#]: author: "Ankush Das https://news.itsfoss.com/author/ankush/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
CachyOS: Arch-based Distro for Speed and Ease of Use
|
||||
======
|
||||
|
||||
A performance-focused Arch-based distro for newbies and experts.
|
||||
|
||||
![cachyOS][1]
|
||||
|
||||
![][2]
|
||||
|
||||
Arch Linux is suitable for advanced users looking for a challenge to use Linux on their system.
|
||||
|
||||
However, many [Arch-based distributions][3] have made it possible for new users to get into the distribution family by making things easier. Options like Garuda Linux, Manjaro Linux, and others make it convenient for new users.
|
||||
|
||||
And one of the exciting options among them is **CachyOS**.
|
||||
|
||||
Well, you might already know about [blendOS][4] (which is also an Arch-based distro, still in the works). It is not remotely similar, but if you are exploring Arch-based distros, you can check it out.
|
||||
|
||||
### CachyOS: Overview
|
||||
|
||||
![cachyos home with cachyos theme][5]
|
||||
|
||||
CachyOS is tailored for all users, whether you are an expert or just starting out. It is available as a stable release even though it is fairly new.
|
||||
|
||||
It aims to provide a blazing-fast experience and offer customizability and stability simultaneously.
|
||||
|
||||
All of this keeping security in mind.
|
||||
|
||||
Some of the critical highlights of CachyOS include the following:
|
||||
|
||||
- **Desktop****packages compiled with****LTO and x86-64-v3 optimization**.
|
||||
- **Choice of desktop environments** with online installation process (including i3, bspwm, and Openbox window managers)
|
||||
- **Offline and Online installation** options
|
||||
- **GUI and CLI-based installation** options
|
||||
- **Optimized Linux Kernel** with advanced BORE Scheduler for enhanced performance and reliability
|
||||
|
||||
### Initial Impression
|
||||
|
||||
CachyOS looks like a well-polished distribution. When I used the ISO to spin up a virtual machine, I noticed that it does support NVIDIA cards, which is a nice touch.
|
||||
|
||||
![][6]
|
||||
|
||||
And then, the option to use an offline or online installation process was helpful. With the online installation process, you can install desktop environments or window managers as per your preference.
|
||||
|
||||
Once done, the welcome screen provided all the essential abilities from the get-go. So, good points for that as well.
|
||||
|
||||
![cachyos welcome screen][7]
|
||||
|
||||
You can install packages, enable system-specific settings, and tweak application/kernel stuff from the welcome screen. Of course, a newbie should not do anything they do not know, but it is good to have everything accessible.
|
||||
|
||||
![cachyos hello welcome screen tweak options][8]
|
||||
|
||||
I tried the KDE edition of CachyOS, which looks pretty good.
|
||||
|
||||
For some reason, the theme was KDE's default Breeze Dark. I expected it to have CachyOS's customized theme out of the box.
|
||||
|
||||
![cachyos homescreen with file manager using kde breeze dark theme][9]
|
||||
|
||||
So, I had to head to the theme manager settings and apply the CachyOS theme for it to look unique.
|
||||
|
||||
![][10]
|
||||
|
||||
It utilizes the fish shell, making the terminal look and feel excellent out of the box.
|
||||
|
||||
![cachyos fish shell][11]
|
||||
|
||||
Performance and security enhancements are at their core. So, if you are unsure about the claims, you will have to compare things closely with another distro. However, per a couple of Reddit threads, some users mention a 10-20% performance uplift.
|
||||
|
||||
You can read [Phoronix's performance analysis][12] of CachyOS for additional insights.
|
||||
|
||||
Unlike other distributions, it features its own web browser, a fork of Firefox, with modifications/enhancements for privacy and security. However, it missed out on a default video player, which should be given to cater to new users.
|
||||
|
||||
**Overall**: It feels like a well-thought-out distribution out of the box. To add a cherry on top, its [documentation][13] is on point and incredibly useful for beginners.
|
||||
|
||||
### Download CachyOS
|
||||
|
||||
You can find KDE and GNOME editions of CachyOS on its [official website][14]. An XFCE edition is in the works. Of course, you can install anything else using its online installation process.
|
||||
|
||||
[CachyOS][14]
|
||||
|
||||
Additionally, you can explore its [GitHub page][15] if you are curious about the customizations they make under the hood.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://news.itsfoss.com/cachyos/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://news.itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://news.itsfoss.com/content/images/size/w1304/2023/04/CachyOS.jpg
|
||||
[2]: https://news.itsfoss.com/content/images/2023/03/linux-mega-packt.webp
|
||||
[3]: https://itsfoss.com/arch-based-linux-distros/?ref=news.itsfoss.com
|
||||
[4]: https://news.itsfoss.com/blendos/
|
||||
[5]: https://news.itsfoss.com/content/images/2023/04/cachy-os-home.jpg
|
||||
[6]: https://news.itsfoss.com/content/images/2023/04/cachy-os-installer.jpg
|
||||
[7]: https://news.itsfoss.com/content/images/2023/04/cachy-os-hello.jpg
|
||||
[8]: https://news.itsfoss.com/content/images/2023/04/cachyos-app-tweaks.jpg
|
||||
[9]: https://news.itsfoss.com/content/images/2023/04/cachyos-home.jpg
|
||||
[10]: https://news.itsfoss.com/content/images/2023/04/cachyos-nord-theme.jpg
|
||||
[11]: https://news.itsfoss.com/content/images/2023/04/cachy-os-fish-shell.jpg
|
||||
[12]: https://www.phoronix.com/review/cachyos-linux-perf?ref=news.itsfoss.com
|
||||
[13]: https://wiki.cachyos.org/?ref=news.itsfoss.com
|
||||
[14]: https://cachyos.org/?ref=news.itsfoss.com
|
||||
[15]: https://github.com/cachyos?ref=news.itsfoss.com
|
@ -1,93 +0,0 @@
|
||||
[#]: subject: "Open Source Software: Is There an Easy Path to Success?"
|
||||
[#]: via: "https://www.opensourceforu.com/2022/07/open-source-software-is-there-an-easy-path-to-success/"
|
||||
[#]: author: "Jules Graybill https://www.opensourceforu.com/author/jules-graybill/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "CanYellow"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
开源软件:存在成功的捷径吗?
|
||||
======
|
||||
|
||||
开发开源软件背后的工作是相当庞大的。那么我们如何保证开源项目的成功呢?存在捷径吗?本文认为是没有的。
|
||||
|
||||
![团队合作][1]
|
||||
|
||||
今天,开源已经风靡世界。很多大型企业在快速成功的诱惑下被推向开源。但真实情况是世界上并不存在成功的捷径。你无法做到通过一次努力就能让所有的开源项目正常运行。
|
||||
|
||||
事实上,上述公司早期遇到的许多挑战都不是技术上的,而是人员与文化上的。
|
||||
|
||||
开发一个能够在市场上获得成功的开源项目需要同不同层级(的开源社区人员)一同工作。维护这样的项目也是一个持续的过程。所有这一切的关键在于找到以下这个非常基础的问题的正确答案:开源究竟是什么。
|
||||
|
||||
### 开源是代码
|
||||
|
||||
对于很多没有深刻认识到构成开源的不同层级(的开源社区人员)用户而言,答案相当简单:开源就是软件!这当然没有错,毕竟这就是我们多数人如何使用它的。不过,相比仅仅被视作软件而言,开源远不止这些。
|
||||
|
||||
任何开源项目的实质仍然是代码本身。代码是使一个开源项目有别于其他项目并使其对用户有益的根本。当你在开源中工作的时候,代码和软件一样都是产品自身的一部分。
|
||||
|
||||
从零开始开发一个(开源)项目或者复刻(fork)一个现有项目的分支,即便是在处理如此庞大而复杂的代码库时,也需要编写成千上万行代码。尤其是在创新一个现有项目的分支的情况下,在移除任何在先的许可证、宣传材料或者其他任何可能已经失去作用的文件时必须小心翼翼。终究是一个项目的功能吸引了它的用户群并维持项目的持续发展。当终端用户在考虑是否使用开源软件的时候,他们会阅读项目的源代码,而他们在其中所看到的应当是那些能够建立他们的信心的内容。
|
||||
|
||||
### 开源是社区
|
||||
|
||||
如何参与到社区中也是产品构建项目的一部分。创建一个社区并维护一个健康的社区关系是开源的核心之一,但对于大部分的领导者而言也往往是最坚难的任务,很少有人能很好地维护它。你可以尝试建立基金会或者提供赞助,但是最终还是人们自行决定是否想要加入社区。
|
||||
|
||||
维护一定程度的社区透明度并不断保持也是重要的。社区可以随心所欲地参与项目。除了需要秘密进行的工作之外,诸如安全设置、签发证书、注册商标等,尽可能多的将你所做的工作展示给社区是相当重要的,这有助于取得社区信任。你终究需要对社区负责,你的项目成也社区,败也社区。这可能会导致你的项目开发更谨慎、更缓慢并且向社区公开,不过项目最终会进展顺利。
|
||||
|
||||
如此地公开你正在进行的工作似乎有些令人生怯,尤其是当你担心更新推迟或者是出现漏洞的影响的时候。不过,让社区成员知悉你的进展不仅有助帮助你建立与社区之间的信任关系,而且能够让社区成员感到被认可。
|
||||
|
||||
另一方面,公开你的工作流也可以获得来自社区成员的监督,他们经常有自己的见解并向你反馈。记录这些反馈是很重要的,这使得你的开源项目如实地反映社区需求。他们是项目的末端用户,而他们的反馈则反映了他们如何看待你的项目的长期发展以及你的项目最终将有多么成功或者主流。
|
||||
|
||||
举例而言,当我们在考虑一个新功能的时候,我们在征求意见文档(RFC, Request for Comments)中发布一个征集意见的请求,我们会收到大量的反馈,我们必须认真思考应当如何吸收这些反馈。
|
||||
|
||||
因为开源是一个大型的合作项目,社区在支持开源项目成为可能的万里挑一的项目上享有主动权。并非所有的问题都要解决,但只要你有在倾听社区的呼声,社区就会有参与感。
|
||||
|
||||
参与到社区中也存在一些隐患。社区内部、项目维护与社区之间均可能存在不同意见,尤其是在涉及管理问题上。管理问题对于一个开源项目来说是相当重要的。这也就是为什么拥有一份清晰的文档化的管理条例对于项目以及社区均是如此重要。
|
||||
|
||||
社区管理是一个关键的而又难啃的骨头。社区授权本身需要相当大的信任。对于一个拥有成千上万行代码的项目,在社区中寻找能够有效领导社区的人物是不容易的。不过开源项目经常是由更小的子项目组成的,这些子项目最好由社区中的某个人进行管理。这有助于社区更紧密地参与到项目中。
|
||||
|
||||
| - |
|
||||
| :- |
|
||||
| 建立社区的过程不是一帆风顺的。让我列举一一些有助于维持社区与我的团队之间平衡的技巧。
|
||||
|
||||
**声明你的原则:**尤其是在开源项目的早期,在项目代码仍在完善,很多事情还不完美的时候,项目之外的人员很难真正理解你所做的决定。向他们说明你做出决定所依据的原则有助于你在思考过程上保持坦率,从而让社区不会错误地干扰你的事务。
|
||||
|
||||
这一经验非常有效,在你做出决定时坚持遵循其中一项原则并展示出来是非常重要的。
|
||||
|
||||
*确定如何进行协作:*你可以通过Discord、Slack或者邮件等途径完成这一工作。但是如果你试图同时使用他们,你将毫不意外的分散项目社区。社区人员将在所有这些途径上互相交流。选择一到两种沟通工具,投身于他们来保证社区的信息同步。
|
||||
|
||||
*珍惜反馈意见:*倾听来自社区的反馈并付诸行动。即使需要你作出艰难的决定,你也应当向社区展示你是重视社区话语的。
|
||||
|
||||
**维护一套行为准则:**如果你与社区打交道,你需要定义什么行为是可以接受的。一套落地的行为准则有助于在人们越过红线时警示他们。如果你可以提前制定这些你可以避免很多麻烦。
|
||||
|
||||
*考虑如何分发你的项目:*存在这样的情况,因为你还没有准备好某一个组件,或者是因为存在一些项目功能你不希望所有人都能够访问,所以你可能并不希望将你的项目完全向公众公开。关键是制定符合你的要求而不是向用户妥协的分发条款,如此,需要某种功能的用户可以获取所需项目的同时不需要该功能的用户也不需要做出妥协而开始使用该项目。
|
||||
|
||||
*尽可能地避免投票:*这是因为部分成员经常会赞成与大部分成员的意见相左的选项,这会使这些人产生一定程度的失望,并让他们觉得被项目所孤立。反之,尽量尝试询问他们想要解决什么问题,并尝试创造一个不需要付出代价的解决方案。
|
||||
|
||||
### 开源是许可
|
||||
|
||||
开源是给予你的用户如何使用你的软件的自由,而许可能够做到这一点。一个开源项目许可是极好的,它保证了不论你作为维护者做了什么,你的所有终端用户以及利益相关方总是可以维护一系列的项目复刻版本,这些都是重要的项目复刻版。
|
||||
|
||||
许可提供了人们可选择性,如果他们认为有必要,他们可以将项目复制到不同的路径中。他们拥有创建副本的权利,这使得许多优秀的软件能够被开发出来。维护者有责任倾听他们的社区成员的声音并以一个对项目的社区成员有利的方式运营项目。
|
||||
|
||||
我们推荐使用现有的许多可用的许可证而不是独立制作你自己的许可条款,仅仅只是因为用户以及利益相关方通常都很熟悉公共许可证,因此你不需要再花费时间在解释许可条款上。这将帮助你将你的精力集中在项目的其他部分上。
|
||||
|
||||
### 最后,开源是一项运动
|
||||
|
||||
开源包括了很多维度,也包含了很多人员。最重要的是,它是有关理解人们想要什么的,也是有关创建一个鼓励协作与透明的环境的。开源也是有关创建有利于开源项目走自己想走的道路的社区的。维护者创造越多的机会让社区自由发挥,开源产品就越好,也越发成功。
|
||||
|
||||
开源是以上这些方面,而你的视野越宽阔,你就能越好的利用它。请考虑你如何能够在开源的每一个维度上出类拔萃,因为时至今日,开源的成功之路并无捷径。
|
||||
|
||||
|
||||
via: https://www.opensourceforu.com/2022/07/open-source-software-is-there-an-easy-path-to-success/
|
||||
|
||||
作者:[Jules Graybill][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[CanYellow](https://github.com/CanYellow)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.opensourceforu.com/author/jules-graybill/
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://www.opensourceforu.com/wp-content/uploads/2022/07/team-work-working-together-1.jpg
|
@ -1,99 +0,0 @@
|
||||
[#]: subject: "5 open source alternatives to Microsoft Exchange"
|
||||
[#]: via: "https://opensource.com/article/21/11/open-source-alternatives-microsoft-exchange"
|
||||
[#]: author: "Heike Jurzik https://opensource.com/users/hej"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "XiaotingHuang22"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
可以替代 Microsoft Exchange 的5个开源软件
|
||||
======
|
||||
不再将就于 Microsoft Exchange 这一专有软件,试一试这些基于 Linux 系统的电子邮件和群件服务吧。
|
||||
![在团队中工作,忙碌的工作生活][1]
|
||||
|
||||
几十年来,Microsoft Exchange 一直统治着电子邮件和群件服务市场。 作为领头羊,它主宰着企业界,无处不在的 Outlook 邮件客户端已成为群件同行里的事实标准。 由于 Exchange 与 Microsoft 的 Office 产品紧密联系,无论是桌面客户端还是移动客户端,微软用户都可以轻松使用各种生产力软件和功能。
|
||||
|
||||
然而,许多公司对于将数据存储在 Microsoft 云中也心存疑虑。 在本文中,我将介绍一些开源替代产品及其优势。 这不仅有关如何不再受供应商控制和降低成本,更关乎使用具有开放标准和不同安全级别的软件——用于组件服务器本身及其背后的操作系统。
|
||||
|
||||
本文中介绍的这五个替代产品都是基于 Linux 的。 虽然 grommunio、Kopano、Nextcloud、ownCloud 和 OX App Suite 在功能上差异很大,吸引到的企业类型各不相同,但它们都提供免费版本,并可选择购买付费支持服务和附加组件。 所有产品都可以在本地或云端运行。 最重要的是,所有供应商都为其软件提供 SaaS (Software as a Service,软件即服务) 解决方案。
|
||||
|
||||
### grommunio
|
||||
|
||||
[grommunio][2],以前被称为 grammm,在 AGPLv3 许可下发布的。 它由奥地利同名公司开发和支持。 与 Exchange 不同,grommunio 提供符合标准的邮件服务器以及功能齐全的群件解决方案,具有电子邮件、联系人、日历、任务、文件共享等功能。 grommunio 适用于各种开源和专有邮件客户端,如 Windows Mail、Outlook、Android、Apple Mail/iOS、Thunderbird 等,并支持旧的 RPC over HTTP 协议和 Outlook 标准协议 MAPI over HTTP。 除此之外还包含:用于移动设备的 Exchange ActiveSync 和各种标准协议,如 CalDAV(日历)、CardDAV(地址簿)、IMAP、POP3、SMTP 和 LDAP,以及 Active Directory(用于同步用户帐户)。
|
||||
|
||||
外部开源应用程序还提供了一些 Microsoft 的 API 或协议不支持的功能。 例如,开发人员合并了 [Jitsi][3](视频和音频电话软件)、[Mattermost][4](聊天软件)以及文件共享和同步([ownCloud][5])。除此之外,grommunio 还配备了基本的移动设备管理软件 (MDM)。
|
||||
|
||||
grommunio 的设计面向大量不同的用户,并且与 Exchange 一样,它支持数据库分片(数据库在多个主机之间的水平分布)。 灵活的存储后端允许管理员通过添加其他服务器或云帐户来扩展他们的设置。 grommunio 仅将 MySQL 数据库用于元数据,而所有“内容”(例如邮件和群件对象)都存储在每个用户的 SQLite 数据库中。 有关底层架构的更多信息,请查看[制造商的网站][6]。
|
||||
|
||||
社区版是免费的,其中包括所有的 grommunio 功能并支持最多五个用户帐户。
|
||||
|
||||
### Kopano
|
||||
|
||||
来自德国和荷兰的软件制造商 Kopano 出品的 [Kopano][7] 也获得了 AGPLv3 许可并基于 Zarafa 软件堆栈。 与其前身不同,Kopano 的目标不只是成为 Exchange 的替代品。 相反,它提供一个完整的群件解决方案,除了电子邮件、联系人、日历、任务、笔记和文档编辑这些标准功能外,它还包括实时通信。 Kopano 与[许多其他平台][8]、应用程序和服务交互,其中一些通过插件就能轻松实现。 对于视频会议,Kopano 团队基于 WebRTC 开发了自己的开源解决方案:Kopano Meet 提供端到端加密,在 Windows、macOS、Linux、Android 和 iOS 客户端都适用。
|
||||
|
||||
Outlook 客户端通过 ActiveSync(Z-Push 库)或 [Kopano OL Extension][9] (KOE) 来同步移动数据,KOE 是已经包含的 ActiveSync 的加强版。 Kopano 提供本机 Web 客户端 (WebApp)、移动设备客户端 (Mobility) 以及支持 Windows、Linux 和 macOS 的桌面版本 (DeskApp)。 它可以通过 IMAP、CalDAV 和 CardDAV 连接其他客户端。 所有直接连接到 Kopano 服务器的应用程序都使用 SOAP(简单对象访问协议)中的 MAPI。
|
||||
|
||||
Kopano Groupware 和 Kopano ONE(Kopano Groupware 的特别版)都提供免费的社区版本。 Kopano Meet 还可以作为应用程序或容器下载。
|
||||
|
||||
### Nextcloud
|
||||
|
||||
[Nextcloud][10] 在斯图加特和柏林(德国)都有办事处,已获得 AGPLv3 许可。 与 ownCloud 或 Dropbox 一样,用户可以通过桌面(Windows、Linux 和 macOS)、网络浏览器或本地应用程序(Android 和 iOS)访问软件套件。 从 18 版本开始,Nextcloud 除了拥有 Nextcloud Files(文件同步和共享)还包括了 Nextcloud Talk(通话、聊天和网络会议)和 Nextcloud Groupware(日历、联系人和邮件),并更名为 Nextcloud Hub。
|
||||
|
||||
用户和群组管理通过 OpenID 或 LDAP 进行。 Nextcloud 支持各种存储后端,例如 FTP、S3 和 Dropbox。 Nextcloud 可与多种数据库管理系统配合使用,包括 PostgreSQL、MariaDB、SQLite 和 Oracle 数据库。 管理员可以通过 [Nextcloud 应用程序商店][11] 中的 200 多个应用程序扩展功能,其中包括实时通信、音频和视频聊天、任务管理、邮件等等。
|
||||
|
||||
Nextcloud 是完全免费的。 最重要的是,该公司提供了 Nextcloud Enterprise 版本(针对企业部署进行了预配置、优化和强化)
|
||||
|
||||
### ownCloud
|
||||
|
||||
[ownCloud][12] 是由位于德国纽伦堡的 ownCloud GmbH 公司开发和维护的具有文件同步、共享和内容协作功能的软件。 它的客户端-服务器软件的核心和许多社区应用程序都是在 AGPLv3 下发布的。 一些扩展功能的企业应用程序获得 ownCloud 商业许可证 (OCL) 许可。
|
||||
|
||||
ownCloud主要是一款内容协作软件,包括在线办公文档编辑、日历、联系人同步等功能。移动客户端支持 Android 和 iOS,桌面应用可以和 Windows、macOS 和 Linux 的原生文件管理器结合使用。它允许访问 Web 界面,无需安装专用客户端软件。 ownCloud 支持 WebDAV、CalDAV 和 CardDAV 协定。 LDAP 协定也包含其中,但 ownCloud 同时连接到支持 OpenID Connect 身份验证标准的其他身份提供者。
|
||||
|
||||
ownCloud 可以整合 Microsoft Office Online Server、Office 365 和 Microsoft Teams,同时为Microsoft Outlook 和 eM Client 提供可用插件。 如有必要,外部存储功能可连接到不同的存储提供商,例如 Amazon S3、Dropbox、Microsoft SharePoint、Google Drive、Windows 网络驱动器 (SMB) 和 FTP。 该供应商还为企业客户提供额外的功能,如端到端加密、勒索软件和防病毒保护等(请参阅[完整功能列表][13])。
|
||||
|
||||
社区版免费且 100% 开源。
|
||||
|
||||
### OX 应用套件
|
||||
|
||||
[Open-Xchange][14] 成立于 2005 年,总部位于德国奥尔佩和纽伦堡。 今天,OX 在多个欧洲国家、美国和日本设有办事处。 [OX App Suite][15] 是一个模块化的电子邮件、通信和协作平台,主要为电信公司、托管公司和其他提供基于云的服务的提供商而设计。
|
||||
|
||||
OX 后端在 GPLv2 协议下发布,前端(UI)在 AGPLv3 下发布。 用户可以通过他们喜欢的浏览器(完全个性化的门户)或移动应用程序(Android 和 iOS)访问应用程序套件。 或者,本机客户端(移动设备和台式机)可用于 OX Mail 和 OX Drive。 得益于 CardDAV 和 CalDAV 扩展、Exchange Active Sync 和适用于 Android 的 OX Sync App,联系人、日历和任务得以同步。
|
||||
|
||||
OX 应用套件包含用于电子邮件、联系人、日历和任务的应用程序。 还有其他工具和扩展可用,其中一些是开源的,一些功能则要付费,包括 OX Documents(文本文档、电子表格、演示文稿)、OX Drive(管理、共享和同步文件)、OX Guard(电子邮件和文件加密)等等。 如需完整列表,请访问 OX 网站的[一般条款和条件][16]。
|
||||
|
||||
该应用免费提供有限功能的社区版。
|
||||
|
||||
### 开源电子邮件和群件
|
||||
|
||||
电子邮件和群件服务并不是必须花费(大量)金钱才可获得,更没有必要将就自己,(为了免费的服务而)依靠一个私人专有的、属于别人的服务器。 如果你不太热衷于管理职责,那么上述的这五个 Exchange 开源替代品都可以作为 SaaS 解决方案使用。 或者,所有供应商都提供专业技术支持,您可以在本地运行软件——一切尽在你的掌握中,但你却不会感觉自己孤军无援。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/11/open-source-alternatives-microsoft-exchange
|
||||
|
||||
作者:[Heike Jurzik][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[XiaotingHuang22](https://github.com/XiaotingHuang22)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/hej
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/team_dev_email_chat_video_work_wfm_desk_520.png?itok=6YtME4Hj (Working on a team, busy worklife)
|
||||
[2]: https://grommunio.com/
|
||||
[3]: https://opensource.com/article/20/5/open-source-video-conferencing
|
||||
[4]: https://opensource.com/article/20/7/mattermost
|
||||
[5]: https://opensource.com/article/21/7/owncloud-windows-files
|
||||
[6]: https://grommunio.com/features/architecture/
|
||||
[7]: https://kopano.com/
|
||||
[8]: https://kopano.com/products/interoperability/
|
||||
[9]: https://kb.kopano.io/display/WIKI/Setting+up+the+Kopano+OL+Extension
|
||||
[10]: https://nextcloud.com/
|
||||
[11]: https://apps.nextcloud.com/
|
||||
[12]: https://owncloud.com/
|
||||
[13]: https://owncloud.com/features/
|
||||
[14]: https://www.open-xchange.com/
|
||||
[15]: https://www.open-xchange.com/products/ox-app-suite/
|
||||
[16]: https://www.open-xchange.com/terms-and-conditions/
|
Loading…
Reference in New Issue
Block a user