开源之家
  • 首页
  • 精品培训视频
  • 计算机电子书
  • 软件工具
  • 知识库
    • Linux入门教程
    • Shell脚本学习指南
    • Nginx入门教程
    • Docker 入门实战
    • Kubernetes(k8s)手册
    • 快乐的Linux命令行
  • Linux命令手册
登录

Linux入门教程

01 Linux简介
  • 1.0 Linux学习路线
  • 1.1 操作系统概述
  • 1.2 Linux是什么,有哪些特点?
  • 1.3 Linux和UNIX的关系及区别(详解版)
  • 1.4 类UNIX系统是什么鬼?
  • 1.5 Linux中大量使用脚本语言,而不是C语言!
  • 1.6 为什么要学Linux,它比Windows好在哪里?
  • 1.7 Linux系统的优缺点
  • 1.8 常见Linux发行版本有哪些?
  • 1.9 初学者应选择哪个Linux发行版?
  • 1.10 Linux桌面系统大比拼|附带优缺点
  • 1.11 Linux的主要应用领域有哪些?
  • 1.12 Linux已经霸占了服务器领域!
  • 1.13 开源软件是什么?有哪些?
  • 1.14 开源协议是什么?有哪些?如何选择?
  • 1.15 开源就等于免费吗?用事实来说话
  • 1.16 Linux该如何学习(新手入门必看)
  • 1.17 想学好Linux,这些习惯必须养成(初学者必读)
02 Linux安装
  • 2.1 安装Linux系统对硬件有什么要求?
  • 2.2 虚拟机是什么
  • 2.3 为什么建议使用虚拟机来安装Linux?
  • 2.4 小白必看:零基础安装Linux系统(超级详细)
  • 2.5 使用U盘安装Linux系统
  • 2.6 使用dd命令安装Linux系统
  • 2.7 使用LiveCD从光盘直接运行Linux,无需安装
  • 2.8 忘记root账户密码怎么办?
  • 2.9 使用系统光盘修复Linux系统
  • 2.10 破解密码这么简单,Linux是安全的操作系统吗?
  • 2.11 Linux四种远程管理协议
  • 2.12 Linux两种远程管理工具(PuTTY和SecureCRT)
  • 2.13 新手必读的Linux使用注意事项
  • 2.14 Linux文件目录结构一览表
  • 2.15 Linux中一切皆文件[包含优缺点]
  • 2.16 Linux挂载详解
  • 2.17 新手必看的Linux服务器管理和维护注意事项
03 Linux文件和目录管理
  • 3.1 Linux文件系统的层次结构
  • 3.2 Linux文件系统到底有什么用处?
  • 3.3 Linux绝对路径和相对路径详解
  • 3.4 Linux文件(目录)命名规则
  • 3.5 Linux命令行下如何识别文件类型?
  • 3.6 Linux命令基本格式
  • 3.7 Linux切换目录(cd命令)
  • 3.8 Linux显示当前工作路径(pwd命令)
  • 3.9 Linux查看目录中的文件(ls命令)
  • 3.10 Linux创建目录(mkdir命令)
  • 3.11 Linux删除空目录(rmdir命令)
  • 3.12 Linux创建文件及修改文件时间戳(touch命令)
  • 3.13 Linux建立软硬链接文件(ln命令)
  • 3.14 深度剖析Linux硬链接和软链接,直击它们的本质!
  • 3.15 Linux复制文件和目录(cp命令)
  • 3.16 Linux删除文件或目录(rm命令)
  • 3.17 Linux移动或重命名文件和目录(mv命令)
  • 3.18 Linux懒人神器:命令自动补全功能!
  • 3.19 Linux命令的执行过程是怎样的?(新手必读)
  • 3.20 什么是环境变量,Linux环境变量有哪些?
  • 3.21 Linux PATH环境变量及作用(初学者必读)
04 Linux打包和压缩详解
  • 4.1 打包和压缩的概念和区别
  • 4.2 Linux tar打包命令详解
  • 4.3 Linux压缩文件或目录为.zip格式(zip命令)
  • 4.4 Linux解压.zip格式的文件(unzip命令)
  • 4.5 Linux压缩文件或目录中文件为.gz格式(gzip命令)
  • 4.6 Linux解压.gz格式的文件(gunzip命令)
  • 4.7 Linux压缩文件或目录中文件为.bz2格式(bzip2命令)
  • 4.8 Linux解压.bz2格式的文件(bunzip2命令)
05 Vim文本编辑器
  • 5.1 Vim及其安装
  • 5.2 Vi和Vim之间到底有什么关系?
  • 5.3 Linux Vim三种工作模式
  • 5.4 Linux Vim基本操作
  • 5.5 Vim移动光标命令汇总
  • 5.6 Linux Vim撤销和恢复撤销快捷键
  • 5.7 Linux Vim可视化模式
  • 5.8 Vim多窗口编辑模式
  • 5.9 Linux Vim批量注释和自定义注释
  • 5.10 Vim显示行号
  • 5.11 Vim配置文件(.vimrc)详解
06 Linux文本处理
  • 6.1 Linux连接合并文件内容(cat命令)
  • 6.2 Linux分屏显示文件内容(more命令)
  • 6.3 Linux显示文件开头内容(head命令)
  • 6.4 Linux查看文件内容(less命令)
  • 6.5 Linux显示文件结尾内容(tail命令)
  • 6.6 Linux重定向(输入输出重定向)
  • 6.7 Linux grep(Linux三剑客之一)
  • 6.8 Linux sed(Linux三剑客之一)
  • 6.9 Linux sed命令的高级玩法
  • 6.10 Linux awk(Linux三剑客之一)
  • 6.11 Linux awk命令的高级玩法
07 Linux系统软件安装
  • 7.1 Linux软件包
  • 7.2 Linux RPM包统一命名规则
  • 7.3 Linux RPM包安装、卸载和升级
  • 7.4 Linux rpm命令查询软件包
  • 7.5 Linux RPM包验证和数字证书
  • 7.6 Linux提取RPM包
  • 7.7 Linux SRPM源码包安装
  • 7.8 Linux重建RPM数据库(修复损坏的RPM数据库)
  • 7.9 RPM包的依赖性及其解决方案
  • 7.10 Linux yum源及配置
  • 7.11 Linux yum命令
  • 7.12 Linux yum管理软件组
  • 7.13 Linux源码包安装和卸载
  • 7.14 Linux源码包升级
  • 7.15 RPM包和源码包,究竟应该选择哪种安装方式?
  • 7.16 Linux函数库(静态函数库和动态函数库)及其安装过程
  • 7.17 Linux脚本程序包及安装方法详解(以webmin为例)
08 Linux用户和用户组管理
  • 8.1 Linux用户和用户组
  • 8.2 Linux UID和GID
  • 8.3 Linux /etc/passwd
  • 8.4 Linux /etc/shadow
  • 8.5 Linux /etc/group
  • 8.6 Linux /etc/gshadow
  • 8.7 Linux初始组和附加组
  • 8.8 Linux /etc/login.defs
  • 8.9 Linux系统添加新用户(useradd命令)
  • 8.10 Linux修改系统用户密码(passwd命令)
  • 8.11 Linux修改系统用户信息(usermod命令)
  • 8.12 Linux强制系统用户登陆时修改密码(chage命令)
  • 8.13 Linux删除系统用户(userdel命令)
  • 8.14 Linux查看用户的UID和GID(id命令)
  • 8.15 Linux临时切换用户身份(su命令)
  • 8.16 Linux whoami和who am i命令
  • 8.17 Linux添加用户组(groupadd命令)
  • 8.18 Linux修改用户组(groupmod命令)
  • 8.19 Linux删除用户组(groupdel命令)
  • 8.20 Linux将系统用户加入或移除群组(gpasswd命令)
  • 8.21 Linux切换用户的有效群组(newgrp命令)
09 Linux权限管理
  • 9.1 Linux权限管理的重要性
  • 9.2 Linux修改文件和目录的所属组(chgrp命令)
  • 9.3 Linux修改文件和目录的所有者和所属组(chown命令)
  • 9.4 Linux文件权限到底是如何设定的?
  • 9.5 Linux读写执行权限(-r、-w、-x)的真正含义是什么?
  • 9.6 Linux修改文件或目录权限(chmod命令)
  • 9.7 Linux默认权限的设定和修改(umask)
  • 9.8 Linux ACL访问控制权限
  • 9.9 Linux ACL权限设置(setfacl和getfacl)
  • 9.10 Linux mask有效权限有什么用,如何修改?
  • 9.11 Linux SetUID(SUID)特殊权限
  • 9.12 不要轻易设置SetUID(SUID)权限,否则会带来重大安全隐患!
  • 9.13 Linux SetGID(SGID)特殊权限
  • 9.14 Linux Stick BIT(SBIT)特殊权限
  • 9.15 Linux文件特殊权限(SUID、SGID和SBIT)的设置
  • 9.16 Linux修改文件或目录的隐藏属性(chattr命令)
  • 9.17 Linux查看文件或目录的隐藏属性(lsattr命令)
  • 9.18 Linux sudo命令(包含和su命令的对比)
  • 9.19 Linux权限对指令执行的影响
10 Linux文件系统管理
  • 10.1 硬盘结构(机械硬盘和固态硬盘)详解
  • 10.2 Linux文件系统详解
  • 10.3 Linux系统是如何识别硬盘设备和硬盘分区的?
  • 10.4 Linux df用法详解:查看文件系统硬盘使用情况
  • 10.5 Linux du命令:统计目录或文件所占磁盘空间大小
  • 10.6 Linux mount命令详解:挂载Linux系统外的文件
  • 10.7 Linux挂载光盘(使用mount命令)
  • 10.8 Linux挂载U盘(使用mount命令)
  • 10.9 Linux开机自动挂载硬件设备(配置/etc/fatab文件)
  • 10.10 修改/etc/fstab文件出错导致Linux不能启动,该怎么办?
  • 10.11 Linux umount命令:卸载文件系统
  • 10.12 Linux fsck命令:检测和修复文件系统
  • 10.13 Linux dumpe2fs命令:查看文件系统信息
  • 10.14 Linux fdisk命令详解:给硬盘分区
  • 10.15 Linux fdisk创建分区(主分区、扩展分区和逻辑分区)过程详解
  • 10.16 Linux parted命令用法详解:创建分区
  • 10.17 Linux mkfs命令详解格式化分区(为分区写入文件系统)
  • 10.18 Linux mke2fs命令格式化硬盘(给硬盘写入文件系统)
  • 10.19 Linux虚拟内存和物理内存
  • 10.20 Linux swap分区及作用详解
11 Linux高级文件系统管理
  • 11.1 磁盘配额是什么
  • 11.2 磁盘配额启动前的准备工作
  • 11.3 Linux扫描文件系统并建立磁盘配额记录文件(quotacheck命令)
  • 11.4 Linux开启磁盘配额限制(quotaon命令)
  • 11.5 Linux关闭磁盘配额限制(quotaoff命令)
  • 11.6 Linux修改用户(群组)的磁盘配额(edquota命令)
  • 11.7 Linux非交互式设置磁盘配额(setquota命令)
  • 11.8 Linux查询已建立好的磁盘配额(quota和repquota命令)
  • 11.9 Linux磁盘配额测试过程完全攻略
  • 11.10 Linux LVM逻辑卷管理机制
  • 11.11 Linux PV物理卷
  • 11.12 Linux VG卷组
  • 11.13 Linux LV逻辑卷
  • 11.14 Linux LVM(逻辑卷管理)的删除
  • 11.15 Linux RAID(磁盘阵列)完全攻略
  • 11.16 使用图形界面来配置RAID
  • 11.17 使用mdadm命令来配置RAID
12 Linux系统管理
  • 12.1 Linux进程管理
  • 12.2 Linux进程启动的方式有几种?
  • 12.3 Linux如何查看正在运行的进程(ps命令)
  • 12.4 Linux实时监听进程运行状态(top命令)
  • 12.5 Linux查看进程树(pstree命令)
  • 12.6 Linux列出进程调用或打开的文件信息(lsof命令)
  • 12.7 Linux进程优先级
  • 12.8 Linux调整进程的优先级(nice和renice命令)
  • 12.9 Linux常用信号(进程间通信)
  • 12.10 Linux终止进程(kill命令)
  • 10.11 Linux umount命令:卸载文件系统
  • 10.12 Linux fsck命令:检测和修复文件系统
  • 10.13 Linux dumpe2fs命令:查看文件系统信息
  • 10.14 Linux fdisk命令详解:给硬盘分区
  • 10.15 Linux fdisk创建分区(主分区、扩展分区和逻辑分区)过程详解
  • 10.16 Linux parted命令用法详解:创建分区
  • 10.17 Linux mkfs命令详解格式化分区(为分区写入文件系统)
  • 10.18 Linux mke2fs命令格式化硬盘(给硬盘写入文件系统)
  • 10.19 Linux虚拟内存和物理内存
  • 10.20 Linux swap分区及作用详解
13 Linux数据备份与恢复
  • 13.1 Linux备份的重要性
  • 13.2 Linux中的哪些数据需要备份?
  • 13.3 Linux数据备份介质的选择
  • 13.4 Linux备份策略
  • 13.5 Linux tar命令备份数据
  • 13.6 Linux dump
  • 13.7 Linux restore
  • 13.8 Linux dd
  • 13.9 Linux rsync命令:支持本地备份和远程备份
14 Linux系统服务管理
  • 14.1 Linux系统服务
  • 14.2 Linux端口
  • 14.3 Linux独立服务管理
  • 14.4 Linux基于xinetd服务的管理
  • 14.5 Linux源码包服务管理
  • 14.6 Linux常见服务类别及功能
  • 14.7 影响Linux系统性能的因素有哪些?
  • 14.8 Linux分析系统性能(sar命令)
  • 14.9 Linux如何查看CPU运行状态?
  • 14.10 Linux如何查看内存的使用情况?
  • 14.11 Linux如何查看硬盘的读写性能?
15 Linux系统日志管理
  • 15.1 Linux rsyslogd服务
  • 15.2 Linux日志文件及功能
  • 15.3 Linux日志文件的格式分析
  • 15.4 rsyslogd配置文件格式及其内容
  • 15.5 Linux日志服务器设置
  • 15.6 Linux日志轮替(日志转储)
  • 15.7 Linux logrotate命令
  • 15.8 Linux日志分析工具
16 Linux系统启动管理
  • 16.1 Linux系统启动流程
  • 16.2 BIOS开机自检
  • 16.3 主引导目录(MBR)结构及作用
  • 16.4 Linux内核(内核模块)的加载
  • 16.5 Linux /sbin/init
  • 16.6 Linux /etc/inittab
  • 16.7 Linux /etc/rc.d/rc.local配置文件
  • 16.8 Linux启动引导程序加载内核
  • 16.9 Linux /boot/grub/目录
  • 16.10 Linux GRUB磁盘分区表示法
  • 16.11 Linux GRUB配置文件
  • 16.12 Linux多系统并存的GRUB配置文件
  • 16.13 Linux GRUB手动安装
  • 16.14 Linux GRUB加密
  • 16.15 Linux字符界面调整分辨率
  • 16.16 Linux内核模块管理
  • 16.17 Linux NTFS文件系统安装
17 LAMP环境搭建和LNMP环境搭建
  • 17.1 Linux LAMP环境搭建的前期准备
  • 17.2 Linux libxml2安装
  • 17.3 Linux libmcrypt安装
  • 17.4 Linux mhash和mcrypt安装
  • 17.5 Linux zlib和libpng安装
  • 17.6 Linux jpeg6安装
  • 17.7 Linux freetype安装
  • 17.8 Linux gd库安装
  • 17.9 Linux Apache安装
  • 17.10 Linux ncurses安装
  • 17.11 Linux MySQL安装
  • 17.12 Linux PHP安装
  • 17.13 Linux memcache安装和配置
  • 17.14 Linux phpmyadmin安装及配置
  • 17.15 LNMP安装的前期准备
  • 17.16 LNMP一键安装
  • 17.17 LNMP安装php扩展模块
18 SELinux管理
  • 18.1 SELinux是什么
  • 18.2 SELinux的主要作用
  • 18.3 SELinux 的3种工作模式
  • 18.4 SELinux配置文件(/etc/selinux/config)
  • 18.5 SELinux工作模式设置
  • 18.6 SELinux安全上下文查看
  • 18.7 SELinux安全上下文的修改和设置
  • 18.8 SELinux默认安全上下文的查询和修改
  • 18.9 SELinux auditd日志系统的安装与启动
  • 18.10 SELinux auditd日志使用方法
  • 18.11 SELinux的3种策略类型
  • 18.12 SELinux策略规则查看的方法
  • 18.13 SELinux策略规则的开启和关闭

Shell脚本学习指南

01 Shell基础
  • 1.1 Shell是什么
  • 1.2 Shell是运维人员必须掌握的技能
  • 1.3 常用的Shell有哪些?
  • 1.4 进入Shell的两种方式
  • 1.5 Shell命令的基本格式
  • 1.6 Shell命令的本质到底是什么?如何自己实现一个命令?
  • 1.7 Shell命令的选项和参数在本质上是什么
  • 1.8 Shell命令提示符
  • 1.9 修改命令提示符
  • 1.10 第一个Shell脚本
  • 1.11 执行Shell脚本
  • 1.12 Shell四种运行方式
  • 1.13 Shell配置文件的加载
  • 1.14 如何编写自己的Shell配置文件?
02 Shell编程
  • 2.1 Shell变量
  • 2.2 Linux中的文件描述符到底是什么?
  • 2.3 Shell命令替换
  • 2.4 Shell位置参数
  • 2.5 Shell特殊变量
  • 2.6 Shell $*和$@之间的区别
  • 2.7 Shell $?
  • 2.8 Shell字符串详解
  • 2.9 Shell字符串拼接
  • 2.10 Shell字符串截取
  • 2.11 Shell数组
  • 2.12 Shell获取数组长度
  • 2.13 Shell数组拼接
  • 2.14 Shell删除数组元素
  • 2.15 Shell关联数组
  • 2.16 Shell内建命令
  • 2.17 Shell alias命令
  • 2.18 Shell echo命令
  • 2.19 Shell read命令
  • 2.20 Shell exit命令
  • 2.21 Shell declare和typeset命令
  • 2.22 Shell数学计算
  • 2.23 Shell (())
  • 2.24 Shell let命令
  • 2.25 Shell $[]
  • 2.26 Shell expr命令
  • 2.27 Linux bc命令
  • 2.28 Shell declare -i
  • 2.29 Shell if else
  • 2.30 Shell退出状态
  • 2.31 Shell test命令
  • 2.32 Shell [[]]
  • 2.33 Shell case in
  • 2.34 Shell while
  • 2.35 Shell until
  • 2.36 Shell for
  • 2.37 Shell select in
  • 2.38 Shell break和continue
  • 2.39 Shell函数
  • 2.40 Shell函数参数
  • 2.41 Shell函数返回值精讲
03 Shell高级教程
  • 3.1 Shell重定向
  • 3.2 Linux中的文件描述符到底是什么?
  • 3.3 结合文件描述符谈重定向,彻底理解重定向的本质!
  • 3.4 使用exec命令操作文件描述符
  • 3.5 使用exec命令操作文件描述符
  • 3.6 Shell Here Document
  • 3.7 Shell Here String
  • 3.8 Shell组命令
  • 3.9 Shell进程替换
  • 3.10 Linux管道
  • 3.11 Shell过滤器
  • 3.12 子Shell和子进程到底有什么区别?
  • 3.13 如何检测子Shell和子进程?
  • 3.14 Linux中的信号是什么
  • 3.15 Bash Shell中的信号
  • 3.16 Linux进程简明教程
  • 3.17 使用什么命令查看进程
  • 3.18 Shell向进程发送信号
  • 3.19 使用trap命令获取信号
  • 3.20 trap命令捕获信号实例演示
  • 3.21 移除(重置)信号捕获
  • 3.22 关于进程、信号和捕获的总结
  • 3.23 Shell模块化
04 Bash Shell快捷键
  • 4.1 Bash Shell快捷键大全
  • 4.2 Bash Shell命令自动补全功能
  • 4.3 Bash Shell历史命令

Nginx入门教程

01 Nginx基础
  • 1.1 前言
  • 1.2 Nginx 简介
  • 1.3 Nginx 源码架构分析
  • 1.4 Nginx 编译安装
  • 1.5 Tengine 编译安装
  • 1.6 OpenResty 编译安装
  • 1.7 Nginx 配置简述
  • 1.8 Nginx Docker 容器化配置
02 Nginx核心配置
  • 2.1 Nginx 核心配置指令
  • 2.2 Nginx 配置文件
  • 2.3 Nginx 进程配置指令
  • 2.4 Nginx 端口监听
  • 2.5 Nginx 主机名
  • 2.6 Nginx 处理HTTP请求
  • 2.7 Nginx 路由匹配规则
  • 2.8 Nginx 重定向配置
  • 2.9 Nginx 根目录配置
  • 2.10 Nginx 访问路径别名
  • 2.11 Nginx 文件判断
  • 2.12 Nginx 零复制
  • 2.13 Nginx 日志记录配置
03 Nginx HTTP模块
  • 3.1 Nginx 镜像模块
  • 3.2 Nginx 请求头控制模块
  • 3.3 Nginx IP访问控制模块
  • 3.4 Nginx 用户cookie模块
  • 3.5 Nginx 并发连接数限制模块
  • 3.6 Nginx 首页处理模块
  • 3.7 Nginx 请求频率限制模块
  • 3.8 Nginx 页面缓存时间配置
  • 3.9 Nginx gzip压缩及相关配置
04 Nginx Web服务
  • 4.1 Nginx 静态资源服务器搭建
  • 4.2 Nginx 文件下载服务器搭建
  • 4.3 Nginx 伪动态SSI服务器
  • 4.4 Nginx HTTPS服务器搭建
  • 4.5 Nginx FastCGI模块配置简述
  • 4.6 Nginx PHP服务器环境搭建
  • 4.7 Nginx 基于FastCGI负载均衡
  • 4.8 Nginx CGI网关接口
  • 4.9 Nginx uWSGI模块配置
  • 4.10 Nginx Python项目部署
  • 4.11 Nginx 伪流媒体服务器搭建
  • 4.12 Nginx HTTP2模块配置简述
  • 4.13 Nginx WebDAV模块配置简述
05 Nginx 代理服务器
  • 5.1 Nginx HTTP代理服务器
  • 5.2 Nginx stream模块简述
  • 5.3 Nginx TCPUDP代理简述
  • 5.4 Nginx 基于SSL的TCP代理服务器
  • 5.5 Nginx gRPC代理服务器
06 Nginx 缓存
  • 6.1 Nginx Web缓存配置
  • 6.2 Nginx 代理缓存配置
  • 6.3 Nginx Memcached 缓存模块
  • 6.4 Nginx 反向代理缓存服务器配置
  • 6.5 Nginx 客户端缓存控制
07 Nginx 负载均衡
  • 7.6 Nginx TCPUDP负载均衡
  • 7.5 Nginx upstream动态更新
  • 7.4 Nginx upstream容错机制
  • 7.3 Nginx 长连接负载均衡
  • 7.2 Nginx 负载均衡策略
  • 7.1 Nginx 负载均衡模块
08 Nginx 日志管理监控
  • 8.1 Nginx 日志分析简述
  • 8.2 Nginx 访问日志配置
  • 8.3 Nginx 错误日志配置
  • 8.4 Nginx 日志归档配置
  • 8.5 Nginx 日志分析工具 ELK
  • 8.6 Nginx 监控工具 Prometheus
09 Nginx 集群
  • 9.1 LVS简介
  • 9.2 Keepalived 配置简述
  • 9.3 Nginx 集群负载搭建
  • 9.4 Nginx 集群配置管理规划
  • 9.5 Nginx 配置归档工具GitLab
  • 9.6 Nginx 配置修改工具Ansible
  • 9.7 Jenkins 安装与配置简述
  • 9.8 Nginx 集群配置管理实例
10 Nginx 在 k8s 的应用
  • 10.1 Kubernetes(k8s)系统简述
  • 10.2 Kubernetes(k8s)集群部署
  • 10.3 Kubernetes(k8s)网络通信
  • 10.4 Nginx Ingress 简介
  • 10.5 Nginx Ingress 安装部署
  • 10.6 Nginx Ingress 配置映射
  • 10.7 Nginx Ingress 注解

Docker 入门实战

01 Docker基础
  • 1.1 前言
  • 1.2 Docker 架构
02 Docker安装
  • 2.1 CentOS Docker 安装
  • 2.2 Ubuntu Docker 安装
  • 2.3 Debian Docker 安装
  • 2.4 Windows Docker 安装
  • 2.5 MacOS Docker 安装
  • 2.6 Docker 镜像加速
03 Docker 使用
  • 3.1 Docker Hello World
  • 3.2 Docker 容器使用
  • 3.3 Docker 镜像使用
  • 3.4 Docker 容器连接
  • 3.5 Docker 仓库管理
  • 3.6 Docker Dockerfile
  • 07 Docker Compose
  • 08 Docker Machine
  • 09 Swarm 集群管理
04 Docker 实例
  • 4.1 Docker 安装 Ubuntu
  • 4.2 Docker 安装 CentOS
  • 4.3 Docker 安装 Nginx
  • 4.4 Docker 安装 Node.js
  • 4.5 Docker 安装 PHP
  • 4.6 Docker 安装 MySQL
  • 4.7 Docker 安装 Tomcat
  • 4.8 Docker 安装 Python
  • 4.9 Docker 安装 Redis
  • 4.10 Docker 安装 MongoDB
  • 4.11 Docker 安装 Apache
05 Docker 参考手册
  • 5.1 Docker 常用命令
  • 5.2 Docker 备忘单

Kubernetes(k8s)手册

01 kubernetes 概述
  • 01 kubernetes 简介
  • 02 Kubernetes 组件
  • 03 Kubernetes API
02 Kubernetes 安装
  • 01 Kubernetes Linux安装

快乐的Linux命令行

  • 第1章:引言
  • 第2章:什么是 shell
  • 第3章:文件系统中跳转
  • 第4章:探究操作系统
  • 第5章:操作文件和目录
  • 第6章:使用命令
  • 第7章:重定向
  • 第8章:从Shell眼中看世界
  • 第9章:键盘高级操作技巧
  • 第10章:权限
  • 第11章:进程
  • 第12章:Shell环境
  • 第13章:vi简介
  • 第14章:自定制Shell提示符
  • 第15章:软件包管理
  • 第16章:存储媒介
  • 第17章:网络系统
  • 第18章:查找文件
  • 第19章:归档和备份
  • 第20章:正则表达式
  • 第21章:文本处理
  • 第22章:格式化输出
  • 第23章:打印
  • 第24章:编译程序
  • 第25章:编写第一个Shell脚本
  • 第26章:启动一个项目
  • 第27章 : 自顶向下设计
  • 第28章 : 流程控制:if 分支结构
  • 第29章 : 读取键盘输入
  • 第30章 : 流程控制:while/until 循环
  • 第31章 : 疑难排解
  • 第32章 : 流程控制:case 分支
  • 第33章 : 位置参数
  • 第34章 : 流程控制:for 循环
  • 第35章 : 字符串和数字
  • 第36章 : 数组
  • 第37章 : 奇珍异宝

第35章 : 字符串和数字

文章目录
  • 35.1 参数展开
    • 35.1.1 基本参数
    • 35.1.2 管理空变量的展开
  • 35.2 返回变量名的参数展开
    • 35.2.1 字符串展开
    • 35.2.2 大小写转换
  • 35.3 算术求值和展开
    • 35.3.1 数基
    • 35.3.2 一元运算符
    • 35.3.3 简单算术
    • 35.3.4 赋值运算符
    • 35.3.5 位运算符
    • 35.3.6 逻辑运算符
  • 35.4 bc - 一种高精度计算器语言
    • 35.4.1 使用 bc
    • 35.4.2 一个脚本实例
  • 35.5 总结
  • 35.6 额外加分

第35章 : 字符串和数字

所有的计算机程序都是用来和数据打交道的。在过去的章节中,我们专注于处理文件级别的数据。然而,许多编程问题需要使用更小的数据单位来解决,比方说字符串和数字。

在这一章中,我们将查看几个用来操作字符串和数字的 shell 功能。shell 提供了各种执行字符串操作的参数展开功能。除了算术展开(在第七章中接触过),还有一个常见的命令行程序叫做 bc,能执行更高级别的数学运算。

35.1 参数展开

尽管参数展开在第七章中出现过,但我们并没有详尽地介绍它,因为大多数的参数展开会用在脚本中,而不是命令行中。我们已经使用了一些形式的参数展开;例如,shell 变量。shell 提供了更多方式。

35.1.1 基本参数

最简单的参数展开形式反映在平常使用的变量上。

例如:

$a

当 $a 展开后,会变成变量 a 所包含的值。简单参数也可能用花括号引起来:

${a}

虽然这对展开没有影响,但若该变量 a 与其它的文本相邻,可能会把 shell 搞糊涂了。在这个例子中,我们试图创建一个文件名,通过把字符串 “_file” 附加到变量 a 的值的后面。

[me@linuxbox ~]$ a="foo"
[me@linuxbox ~]$ echo "$a_file"

如果我们执行这个序列,没有任何输出结果,因为 shell 会试着展开一个称为 a_file 的变量,而不是 a。通过添加花括号可以解决这个问题:

[me@linuxbox ~]$ echo "${a}_file"
foo_file

我们已经知道通过把数字包裹在花括号中,可以访问大于9的位置参数。例如,访问第十一个位置参数,我们可以这样做:

${11}

35.1.2 管理空变量的展开

几种用来处理不存在和空变量的参数展开形式。这些展开形式对于解决丢失的位置参数和给参数指定默认值的情况很方便。

${parameter:-word}

若 parameter 没有设置(例如,不存在)或者为空,展开结果是 word 的值。若 parameter 不为空,则展开结果是 parameter 的值。

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo:-"substitute value if unset"}
if unset
substitute value
[me@linuxbox ~]$ echo $foo
[me@linuxbox ~]$ foo=bar
[me@linuxbox ~]$ echo ${foo:-"substitute value if unset"}
bar
[me@linuxbox ~]$ echo $foo
bar

${parameter:=word}

若 parameter 没有设置或为空,展开结果是 word 的值。另外,word 的值会赋值给 parameter。若 parameter 不为空,展开结果是 parameter 的值。

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo:="default value if unset"}
default value if unset
[me@linuxbox ~]$ echo $foo
default value if unset
[me@linuxbox ~]$ foo=bar
[me@linuxbox ~]$ echo ${foo:="default value if unset"}
bar
[me@linuxbox ~]$ echo $foo
bar

注意: 位置参数或其它的特殊参数不能以这种方式赋值。


${parameter:?word}

若 parameter 没有设置或为空,这种展开导致脚本带有错误退出,并且 word 的内容会发送到标准错误。若 parameter 不为空,展开结果是 parameter 的值。

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo:?"parameter is empty"}
bash: foo: parameter is empty
[me@linuxbox ~]$ echo $?
1
[me@linuxbox ~]$ foo=bar
[me@linuxbox ~]$ echo ${foo:?"parameter is empty"}
bar
[me@linuxbox ~]$ echo $?
0

${parameter:+word}

若 parameter 没有设置或为空,展开结果为空。若 parameter 不为空,展开结果是 word 的值会替换掉 parameter 的值;然而,parameter 的值不会改变。

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo:+"substitute value if set"}

[me@linuxbox ~]$ foo=bar
[me@linuxbox ~]$ echo ${foo:+"substitute value if set"}
substitute value if set

35.2 返回变量名的参数展开

shell 具有返回变量名的能力。这会用在一些相当独特的情况下。

${!prefix*}

${!prefix@}

这种展开会返回以 prefix 开头的已有变量名。根据 bash 文档,这两种展开形式的执行结果相同。这里,我们列出了所有以 BASH 开头的环境变量名:

[me@linuxbox ~]$ echo ${!BASH*}
BASH BASH_ARGC BASH_ARGV BASH_COMMAND BASH_COMPLETION
BASH_COMPLETION_DIR BASH_LINENO BASH_SOURCE BASH_SUBSHELL
BASH_VERSINFO BASH_VERSION

35.2.1 字符串展开

有大量的展开形式可用于操作字符串。其中许多展开形式尤其适用于路径名的展开。

${#parameter}

展开成由 parameter 所包含的字符串的长度。通常,parameter 是一个字符串;然而,如果 parameter 是 @ 或者是 * 的话,则展开结果是位置参数的个数。

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo "'$foo' is ${#foo} characters long."
'This string is long.' is 20 characters long.

${parameter:offset}

${parameter:offset:length}

这些展开用来从 parameter 所包含的字符串中提取一部分字符。提取的字符始于第 offset 个字符(从字符串开头算起)直到字符串的末尾,除非指定提取的长度。

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo ${foo:5}
string is long.
[me@linuxbox ~]$ echo ${foo:5:6}
string

若 offset 的值为负数,则认为 offset 值是从字符串的末尾开始算起,而不是从开头。注意负数前面必须有一个空格,为防止与 ${parameter:-word} 展开形式混淆。length,若出现,则必须不能小于零。

如果 parameter 是 @,展开结果是 length 个位置参数,从第 offset 个位置参数开始。

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo ${foo: -5}
long.
[me@linuxbox ~]$ echo ${foo: -5:2}
lo

${parameter#pattern}

${parameter##pattern}

这些展开会从 paramter 所包含的字符串中清除开头一部分文本,这些字符要匹配定义的 pattern。pattern 是通配符模式,就如那些用在路径名展开中的模式。这两种形式的差异之处是该 # 形式清除最短的匹配结果,
而该 ## 模式清除最长的匹配结果。

[me@linuxbox ~]$ foo=file.txt.zip
[me@linuxbox ~]$ echo ${foo#*.}
txt.zip
[me@linuxbox ~]$ echo ${foo##*.}
zip

${parameter%pattern}

${parameter%%pattern}

这些展开和上面的 # 和 ## 展开一样,除了它们清除的文本从 parameter 所包含字符串的末尾开始,而不是开头。

[me@linuxbox ~]$ foo=file.txt.zip
[me@linuxbox ~]$ echo ${foo%.*}
file.txt
[me@linuxbox ~]$ echo ${foo%%.*}
file

${parameter/pattern/string}

${parameter//pattern/string}

${parameter/#pattern/string}

${parameter/%pattern/string}

这种形式的展开对 parameter 的内容执行查找和替换操作。如果找到了匹配通配符 pattern 的文本,则用 string 的内容替换它。在正常形式下,只有第一个匹配项会被替换掉。在该 // 形式下,所有的匹配项都会被替换掉。该 /# 要求匹配项出现在字符串的开头,而 /% 要求匹配项出现在字符串的末尾。/string 可能会省略掉,这样会导致删除匹配的文本。

[me@linuxbox~]$ foo=JPG.JPG
[me@linuxbox ~]$ echo ${foo/JPG/jpg}
jpg.JPG
[me@linuxbox~]$ echo ${foo//JPG/jpg}
jpg.jpg
[me@linuxbox~]$ echo ${foo/#JPG/jpg}
jpg.JPG
[me@linuxbox~]$ echo ${foo/%JPG/jpg}
JPG.jpg

知道参数展开是件很好的事情。字符串操作展开可以用来替换其它常见命令比方说 sed 和 cut。通过减少使用外部程序,展开提高了脚本的效率。举例说明,我们将修改在之前章节中讨论的 longest-word 程序,用参数展开 {#j} 取代命令 j | wc -c) 及其 subshell ,像这样:

#!/bin/bash
# longest-word3 : find longest string in a file
for i; do
    if [[ -r $i ]]; then
        max_word=
        max_len=
        for j in $(strings $i); do
            len=${#j}
            if (( len > max_len )); then
                max_len=$len
                max_word=$j
            fi
        done
        echo "$i: '$max_word' ($max_len characters)"
    fi
    shift
done

下一步,我们将使用 time 命令来比较这两个脚本版本的效率:

[me@linuxbox ~]$ time longest-word2 dirlist-usr-bin.txt
dirlist-usr-bin.txt: 'scrollkeeper-get-extended-content-list' (38
characters)
real 0m3.618s
user 0m1.544s
sys 0m1.768s
[me@linuxbox ~]$ time longest-word3 dirlist-usr-bin.txt
dirlist-usr-bin.txt: 'scrollkeeper-get-extended-content-list' (38
characters)
real 0m0.060s
user 0m0.056s
sys 0m0.008s

原来的脚本扫描整个文本文件需耗时3.168秒,而该新版本,使用参数展开,仅仅花费了0.06秒 一个非常巨大的提高。

35.2.2 大小写转换

最新的 bash 版本已经支持字符串的大小写转换了。bash 有四个参数展开和 declare 命令的两个选项来支持大小写转换。

那么大小写转换对什么有好处呢? 除了明显的审美价值,它在编程领域还有一个重要的角色。让我们考虑一个数据库查询的案例。假设一个用户已经敲写了一个字符串到数据输入框中,而我们想要在一个数据库中查找这个字符串。该用户输入的字符串有可能全是大写字母或全是小写或是两者的结合。我们当然不希望把每个可能的大小写拼写排列填充到我们的数据库中。那怎么办?

解决这个问题的常见方法是规范化用户输入。也就是,在我们试图查询数据库之前,把用户的输入转换成标准化。我们能做到这一点,通过把用户输入的字符全部转换成小写字母或大写字母,并且确保数据库中的条目按同样的方式规范化。

这个 declare 命令可以用来把字符串规范成大写或小写字符。使用 declare 命令,我们能强制一个变量总是包含所需的格式,无论如何赋值给它。

#!/bin/bash
# ul-declare: demonstrate case conversion via declare
declare -u upper
declare -l lower
if [[ $1 ]]; then
    upper="$1"
    lower="$1"
    echo $upper
    echo $lower
fi

在上面的脚本中,我们使用 declare 命令来创建两个变量,upper 和 lower。我们把第一个命令行参数的值(位置参数1)赋给每一个变量,然后把变量值在屏幕上显示出来:

[me@linuxbox ~]$ ul-declare aBc
ABC
abc

正如我们所看到的,命令行参数(“aBc”)已经规范化了。

有四个参数展开,可以执行大小写转换操作:

表 35-1: 大小写转换参数展开
格式 结果
${parameter,,} 把 parameter 的值全部展开成小写字母。
${parameter,} 仅仅把 parameter 的第一个字符展开成小写字母。
${parameter^^} 把 parameter 的值全部转换成大写字母。
${parameter^} 仅仅把 parameter 的第一个字符转换成大写字母(首字母大写)。

这里是一个脚本,演示了这些展开格式:

#!/bin/bash
# ul-param - demonstrate case conversion via parameter expansion
if [[ $1 ]]; then
    echo ${1,,}
    echo ${1,}
    echo ${1^^}
    echo ${1^}
fi

这里是脚本运行后的结果:

[me@linuxbox ~]$ ul-param aBc
abc
aBc
ABC
ABc

再次,我们处理了第一个命令行参数,输出了由参数展开支持的四种变体。尽管这个脚本使用了第一个位置参数,但参数可以是任意字符串,变量,或字符串表达式。

35.3 算术求值和展开

我们在第七章中已经接触过算术展开了。它被用来对整数执行各种算术运算。它的基本格式是:

$((expression))

这里的 expression 是一个有效的算术表达式。

这个与复合命令 (( )) 有关,此命令用做算术求值(真测试),我们在第27章中遇到过。

在之前的章节中,我们看到过一些类型的表达式和运算符。这里,我们将看到一个更完整的列表。

35.3.1 数基

回到第9章,我们看过八进制(以8为底)和十六进制(以16为底)的数字。在算术表达式中,shell 支持任意进制的整型常量。

表 35-2: 指定不同的数基
表示法 描述
number 默认情况下,没有任何表示法的数字被看做是十进制数(以10为底)。
0number 在算术表达式中,以零开头的数字被认为是八进制数。
0xnumber 十六进制表示法
base#number number 以 base 为底

一些例子:

[me@linuxbox ~]$ echo $((0xff))
255
[me@linuxbox ~]$ echo $((2#11111111))
255

在上面的示例中,我们打印出十六进制数 ff(最大的两位数)的值和最大的八位二进制数(以2为底)。

35.3.2 一元运算符

有两个一元运算符,+ 和 -,它们被分别用来表示一个数字是正数还是负数。例如,-5。

35.3.3 简单算术

下表中列出了普通算术运算符:

表 35-3: 算术运算符
运算符 描述
+ 加
- 减
* 乘
/ 整除
** 乘方
% 取模(余数)

其中大部分运算符是不言自明的,但是整除和取模运算符需要进一步解释一下。

因为 shell 算术只操作整型,所以除法运算的结果总是整数:

[me@linuxbox ~]$ echo $(( 5 / 2 ))
2

这使得确定除法运算的余数更为重要:

[me@linuxbox ~]$ echo $(( 5 % 2 ))
1

通过使用除法和取模运算符,我们能够确定5除以2得数是2,余数是1。

在循环中计算余数是很有用处的。在循环执行期间,它允许某一个操作在指定的间隔内执行。在下面的例子中,我们显示一行数字,并高亮显示5的倍数:

#!/bin/bash
# modulo : demonstrate the modulo operator
for ((i = 0; i <= 20; i = i + 1)); do
    remainder=$((i % 5))
    if (( remainder == 0 )); then
        printf "<%d> " $i
    else
        printf "%d " $i
    fi
done
printf "\n"

当脚本执行后,输出结果看起来像这样:

[me@linuxbox ~]$ modulo
<0> 1 2 3 4 <5> 6 7 8 9 <10> 11 12 13 14 <15> 16 17 18 19 <20>

35.3.4 赋值运算符

尽管它的使用不是那么明显,算术表达式可能执行赋值运算。虽然在不同的上下文中,我们已经执行了许多次赋值运算。每次我们给变量一个值,我们就执行了一次赋值运算。我们也能在算术表达式中执行赋值运算:

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo $foo
[me@linuxbox ~]$ if (( foo = 5 ));then echo "It is true."; fi
It is true.
[me@linuxbox ~]$ echo $foo
5

在上面的例子中,首先我们给变量 foo 赋了一个空值,然后验证 foo 的确为空。下一步,我们执行一个 if 复合命令 (( foo = 5 ))。这个过程完成两件有意思的事情:1)它把5赋值给变量 foo,2)它计算测试条件为真,因为 foo 的值非零。


注意: 记住上面表达式中 = 符号的真正含义非常重要。单个 = 运算符执行赋值运算。foo = 5 是说“使得 foo 等于5”,而 == 运算符计算等价性。foo == 5 是说“是否 foo 等于5?”。这会让人感到非常迷惑,因为 test 命令接受单个 = 运算符来测试字符串等价性。这也是使用更现代的 [[ ]] 和 (( )) 复合命令来代替 test 命令的另一个原因。


除了 = 运算符,shell 也提供了其它一些表示法,来执行一些非常有用的赋值运算:

表35-4: 赋值运算符
表示法 描述
parameter = value 简单赋值。给 parameter 赋值。
parameter += value 加。等价于 parameter = parameter + value。
parameter -= value 减。等价于 parameter = parameter – value。
parameter *= value 乘。等价于 parameter = parameter * value。
parameter /= value 整除。等价于 parameter = parameter / value。
parameter %= value 取模。等价于 parameter = parameter % value。
parameter++ 后缀自增变量。等价于 parameter = parameter + 1 (但,要看下面的讨论)。
parameter-- 后缀自减变量。等价于 parameter = parameter - 1。
++parameter 前缀自增变量。等价于 parameter = parameter + 1。
--parameter 前缀自减变量。等价于 parameter = parameter - 1。

这些赋值运算符为许多常见算术任务提供了快捷方式。特别关注一下自增(++)和自减(--)运算符,它们会把它们的参数值加1或减1。这种风格的表示法取自C 编程语言并且被其它几种编程语言吸收,包括 bash。

自增和自减运算符可能会出现在参数的前面或者后面。然而它们都是把参数值加1或减1,这两个位置有个微小的差异。若运算符放置在参数的前面,参数值会在参数返回之前增加(或减少)。若放置在后面,则运算会在参数返回之后执行。这相当奇怪,但这是它预期的行为。这里是个演示的例子:

[me@linuxbox ~]$ foo=1
[me@linuxbox ~]$ echo $((foo++))
1
[me@linuxbox ~]$ echo $foo
2

如果我们把1赋值给变量 foo,然后通过把自增运算符 ++ 放到参数名 foo 之后来增加它,foo 返回1。然而,如果我们第二次查看变量 foo 的值,我们看到它的值增加了1。若我们把 ++ 运算符放到参数 foo 之前,我们得到更期望的行为:

[me@linuxbox ~]$ foo=1
[me@linuxbox ~]$ echo $((++foo))
2
[me@linuxbox ~]$ echo $foo
2

对于大多数 shell 应用来说,前缀运算符最有用。

自增 ++ 和 自减 -- 运算符经常和循环操作结合使用。我们将改进我们的 modulo 脚本,让代码更紧凑些:

#!/bin/bash
# modulo2 : demonstrate the modulo operator
for ((i = 0; i <= 20; ++i )); do
    if (((i % 5) == 0 )); then
        printf "<%d> " $i
    else
        printf "%d " $i
    fi
done
printf "\n"

35.3.5 位运算符

位运算符是一类以不寻常的方式操作数字的运算符。这些运算符工作在位级别的数字。它们被用在某类底层的任务中,经常涉及到设置或读取位标志。

表35-5: 位运算符
运算符 描述
~ 按位取反。对一个数字所有位取反。
<< 位左移. 把一个数字的所有位向左移动。
>> 位右移. 把一个数字的所有位向右移动。
& 位与。对两个数字的所有位执行一个 AND 操作。
| 位或。对两个数字的所有位执行一个 OR 操作。
^ 位异或。对两个数字的所有位执行一个异或操作。

注意除了按位取反运算符之外,其它所有位运算符都有相对应的赋值运算符(例如,<<=)。

这里我们将演示产生2的幂列表的操作,使用位左移运算符:

[me@linuxbox ~]$ for ((i=0;i<8;++i)); do echo $((1<<i)); done
1
2
4
8
16
32
64
128

35.3.6 逻辑运算符

正如我们在第27章中所看到的,复合命令 (( )) 支持各种各样的比较运算符。还有一些可以用来计算逻辑运算。这里是比较运算符的完整列表:

表35-6: 比较运算符
运算符 描述
<= 小于或相等
>= 大于或相等
< 小于
> 大于
== 相等
!= 不相等
&& 逻辑与
|| 逻辑或
expr1?expr2:expr3 条件(三元)运算符。若表达式 expr1 的计算结果为非零值(算术真),则
执行表达式 expr2,否则执行表达式 expr3。

当表达式用于逻辑运算时,表达式遵循算术逻辑规则;也就是,表达式的计算结果是零,则认为假,而非零表达式认为真。该 (( )) 复合命令把结果映射成 shell 正常的退出码:

[me@linuxbox ~]$ if ((1)); then echo "true"; else echo "false"; fi
true
[me@linuxbox ~]$ if ((0)); then echo "true"; else echo "false"; fi
false

最陌生的逻辑运算符就是这个三元运算符了。这个运算符(仿照于 C 编程语言里的三元运算符)执行一个单独的逻辑测试。它用起来类似于 if/then/else 语句。它操作三个算术表达式(字符串不会起作用),并且若第一个表达式为真(或非零),则执行第二个表达式。否则,执行第三个表达式。我们可以在命令行中实验一下:

[me@linuxbox~]$ a=0
[me@linuxbox~]$ ((a<1?++a:--a))
[me@linuxbox~]$ echo $a
1
[me@linuxbox~]$ ((a<1?++a:--a))
[me@linuxbox~]$ echo $a
0

这里我们看到一个实际使用的三元运算符。这个例子实现了一个切换。每次运算符执行的时候,变量 a 的值从零变为1,或反之亦然。

请注意在表达式内执行赋值却并非易事。

当企图这样做时,bash 会声明一个错误:

[me@linuxbox ~]$ a=0
[me@linuxbox ~]$ ((a<1?a+=1:a-=1))
bash: ((: a<1?a+=1:a-=1: attempted assignment to non-variable (error token is "-=1")

通过把赋值表达式用括号括起来,可以解决这个错误:

[me@linuxbox ~]$ ((a<1?(a+=1):(a-=1)))

下一步,我们看一个使用算术运算符更完备的例子,该示例产生一个简单的数字表格:

#!/bin/bash
# arith-loop: script to demonstrate arithmetic operators
finished=0
a=0
printf "a\ta**2\ta**3\n"
printf "=\t====\t====\n"
until ((finished)); do
    b=$((a**2))
    c=$((a**3))
    printf "%d\t%d\t%d\n" $a $b $c
    ((a<10?++a:(finished=1)))
done

在这个脚本中,我们基于变量 finished 的值实现了一个 until 循环。首先,把变量 finished 的值设为零(算术假),继续执行循环之道它的值变为非零。在循环体内,我们计算计数器 a 的平方和立方。在循环末尾,计算计数器变量 a 的值。若它小于10(最大迭代次数),则 a 的值加1,否则给变量 finished 赋值为1,使得变量 finished 算术为真,从而终止循环。运行该脚本得到这样的结果:

[me@linuxbox ~]$ arith-loop
a    a**2     a**3
=    ====     ====
0    0        0
1    1        1
2    4        8
3    9        27
4    16       64
5    25       125
6    36       216
7    49       343
8    64       512
9    81       729
10   100      1000

35.4 bc - 一种高精度计算器语言

我们已经看到 shell 是可以处理所有类型的整型算术的,但是如果我们需要执行更高级的数学运算或仅使用浮点数,该怎么办?答案是,我们不能这样做。至少不能直接用 shell 完成此类运算。为此,我们需要使用外部程序。有几种途径可供我们采用。嵌入的 Perl 或者 AWK 程序是一种可能的方案,但是不幸的是,超出了本书的内容大纲。另一种方式就是使用一种专业的计算器程序。这样一个程序叫做 bc,在大多数 Linux 系统中都可以找到。

该 bc 程序读取一个用它自己的类似于 C 语言的语法编写的脚本文件。一个 bc 脚本可能是一个分离的文件或者是从标准输入读入。bc 语言支持相当少的功能,包括变量,循环和程序员定义的函数。这里我们不会讨论整个 bc 语言,仅仅体验一下。查看 bc 的手册页,其文档整理得非常好。

让我们从一个简单的例子开始。我们将编写一个 bc 脚本来执行2加2运算:

/* A very simple bc script */
2 + 2

脚本的第一行是一行注释。bc 使用和 C 编程语言一样的注释语法。注释,可能会跨越多行,开始于 "/* "结束于 "*/"。

35.4.1 使用 bc

如果我们把上面的 bc 脚本保存为 foo.bc,然后我们就能这样运行它:

[me@linuxbox ~]$ bc foo.bc
bc 1.06.94
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software
Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
4

如果我们仔细观察,我们看到算术结果在最底部,版权信息之后。可以通过 -q(quiet)选项禁止这些版权信息。bc 也能够交互使用:

[me@linuxbox ~]$ bc -q
2 + 2
4
quit

当使用 bc 交互模式时,我们简单地输入我们希望执行的运算,结果就立即显示出来。bc 的 quit 命令结束交互会话。

也可能通过标准输入把一个脚本传递给 bc 程序:

[me@linuxbox ~]$ bc < foo.bc
4

这种接受标准输入的能力,意味着我们可以使用 here 文档,here字符串,和管道来传递脚本。这里是一个使用 here 字符串的例子:

[me@linuxbox ~]$ bc <<< "2+2"
4

35.4.2 一个脚本实例

作为一个真实世界的例子,我们将构建一个脚本,用于计算每月的还贷金额。在下面的脚本中,我们使用了 here 文档把一个脚本传递给 bc:

#!/bin/bash
# loan-calc : script to calculate monthly loan payments
PROGNAME=$(basename $0)
usage () {
    cat <<- EOF
    Usage: $PROGNAME PRINCIPAL INTEREST MONTHS
    Where:
    PRINCIPAL is the amount of the loan.
    INTEREST is the APR as a number (7% = 0.07).
    MONTHS is the length of the loan's term.
    EOF
}
if (($# != 3)); then
    usage
    exit 1
fi
principal=$1
interest=$2
months=$3
bc <<- EOF
    scale = 10
    i = $interest / 12
    p = $principal
    n = $months
    a = p * ((i * ((1 + i) ^ n)) / (((1 + i) ^ n) - 1))
    print a, "\n"
EOF

当脚本执行后,输出结果像这样:

[me@linuxbox ~]$ loan-calc 135000 0.0775 180
475
1270.7222490000

若贷款 135,000 美金,年利率为 7.75%,借贷180个月(15年),这个例子计算出每月需要还贷的金额。注意这个答案的精确度。这是由脚本中变量 scale 的值决定的。bc 的手册页提供了对 bc 脚本语言的详尽描述。虽然 bc 的数学符号与 shell 的略有差异(bc 与 C 更相近),但是基于目前我们所学的内容,大多数符号是我们相当熟悉的。

35.5 总结

在这一章中,我们学习了很多小东西,在脚本中这些小零碎可以完成“真正的工作”。随着我们编写脚本经验的增加,能够有效地操作字符串和数字的能力将具有极为重要的价值。我们的 loan-calc 脚本表明,甚至可以创建简单的脚本来完成一些真正有用的事情。

35.6 额外加分

虽然该 loan-calc 脚本的基本功能已经很到位了,但脚本还远远不够完善。为了额外加分,试着给脚本 loan-calc 添加以下功能:

  • 完整的命令行参数验证

  • 用一个命令行选项来实现“交互”模式,提示用户输入本金、利率和贷款期限

  • 输出格式美化

更新于 2022年10月2日
第34章 : 流程控制:for 循环第36章 : 数组

发表评论 取消回复

您需要登录后才可以发表评论...
登录... 后才能评论
文章目录
  • 35.1 参数展开
    • 35.1.1 基本参数
    • 35.1.2 管理空变量的展开
  • 35.2 返回变量名的参数展开
    • 35.2.1 字符串展开
    • 35.2.2 大小写转换
  • 35.3 算术求值和展开
    • 35.3.1 数基
    • 35.3.2 一元运算符
    • 35.3.3 简单算术
    • 35.3.4 赋值运算符
    • 35.3.5 位运算符
    • 35.3.6 逻辑运算符
  • 35.4 bc - 一种高精度计算器语言
    • 35.4.1 使用 bc
    • 35.4.2 一个脚本实例
  • 35.5 总结
  • 35.6 额外加分

Copyright © 2015-2023 开源之家

  • 首页
  • 每日签到
  • 加入VIP
  • 顶部
AI&大数据 Java Java Linux Linux Python 前端 办公软件 办公软件 培训视频 娱乐休闲 小程序开发 数据库 系统相关 网络 英语 设计创意 软件测试