开源之家
  • 首页
  • 精品培训视频
  • 计算机电子书
  • 软件工具
  • 知识库
    • 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章 : 奇珍异宝

第31章 : 疑难排解

文章目录
  • 31.1 语法错误
    • 31.1.1 丢失引号
    • 31.1.2 丢失或意外的标记
    • 31.1.3 预料不到的展开
  • 31.2 逻辑错误
    • 31.2.1 防错编程
    • 31.2.2 验证输入
  • 31.3 测试
    • 31.3.1 测试案例
  • 31.4 调试
    • 31.4.1 找到问题区域
    • 31.4.2 追踪
    • 31.4.3 执行时检查数值
  • 31.5 总结

第31章 : 疑难排解

随着我们的脚本变得越来越复杂,当脚本运行错误,执行结果出人意料的时候, 我们就应该查看一下原因了。在这一章中,我们将会看一些脚本中出现地常见错误类型,同时还会介绍几个可以跟踪和消除问题的有用技巧。

31.1 语法错误

一个普通的错误类型是语法。语法错误涉及到一些 shell 语法元素的拼写错误。大多数情况下,这类错误会导致 shell 拒绝执行此脚本。

在以下讨论中,我们将使用下面这个脚本,来说明常见的错误类型:

#!/bin/bash
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ]; then
    echo "Number is equal to 1."
else
    echo "Number is not equal to 1."
fi

参看脚本内容,我们知道这个脚本执行成功了:

[me@linuxbox ~]$ trouble
Number is equal to 1.

31.1.1 丢失引号

如果我们编辑我们的脚本,并从跟随第一个 echo 命令的参数中,删除其末尾的双引号:

#!/bin/bash
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ]; then
    echo "Number is equal to 1.
else
    echo "Number is not equal to 1."
fi

观察发生了什么:

[me@linuxbox ~]$ trouble
/home/me/bin/trouble: line 10: unexpected EOF while looking for
matching `"'
/home/me/bin/trouble: line 13: syntax error: unexpected end of file

这个脚本产生了两个错误。有趣地是,所报告的行号不是引号被删除的地方,而是程序中后面的文本行。我们能知道为什么,如果我们跟随丢失引号文本行之后的程序。bash 会继续寻找右引号,直到它找到一个,其就是这个紧随第二个 echo 命令之后的引号。找到这个引号之后,bash 变得很困惑,并且 if 命令的语法被破坏了,因为现在这个 fi 语句在一个用引号引起来的(但是开放的)字符串里面。

在冗长的脚本中,此类错误很难找到。使用带有语法高亮的编辑器将会帮助查找错误。如果安装了 vim 的完整版,通过输入下面的命令,可以使语法高亮生效:

:syntax on

31.1.2 丢失或意外的标记

另一个常见错误是忘记补全一个复合命令,比如说 if 或者是 while。让我们看一下,如果我们删除 if 命令中测试之后的分号,会出现什么情况:

#!/bin/bash
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ] then
    echo "Number is equal to 1."
else
    echo "Number is not equal to 1."
fi

结果是这样的:

[me@linuxbox ~]$ trouble
/home/me/bin/trouble: line 9: syntax error near unexpected token
`else'
/home/me/bin/trouble: line 9: `else'

再次,错误信息指向一个错误,其出现的位置在实际问题所在的文本行的后面。所发生的事情真是相当有意思。我们记得,if 能够接受一系列命令,并且会计算列表中最后一个命令的退出代码。在我们的程序中,我们打算这个列表由单个命令组成,即 [,测试的同义词。这个 [ 命令把它后面的东西看作是一个参数列表。在我们这种情况下,有三个参数: $number,=,和 ]。由于删除了分号,单词 then 被添加到参数列表中,从语法上讲,这是合法的。随后的 echo 命令也是合法的。它被解释为命令列表中的另一个命令,if将会计算命令的 退出代码。接下来遇到单词 else,但是它出局了,因为 shell 把它认定为一个保留字(对于 shell 有特殊含义的单词),而不是一个命令名,因此报告错误信息。

31.1.3 预料不到的展开

可能有这样的错误,它们仅会间歇性地出现在一个脚本中。有时候这个脚本执行正常,其它时间会失败,这是因为展开结果造成的。如果我们归还我们丢掉的分号,并把 number 的数值更改为一个空变量,我们可以示范一下:

#!/bin/bash
# trouble: script to demonstrate common errors
number=
if [ $number = 1 ]; then
    echo "Number is equal to 1."
else
    echo "Number is not equal to 1."
fi

运行这个做了修改的脚本,得到以下输出:

[me@linuxbox ~]$ trouble
/home/me/bin/trouble: line 7: [: =: unary operator expected
Number is not equal to 1.

我们得到一个相当神秘的错误信息,其后是第二个 echo 命令的输出结果。这问题是由于 test 命令中 number 变量的展开结果造成的。当此命令:

[ $number = 1 ]

经过展开之后,number 变为空值,结果就是这样:

[  = 1 ]

这是无效的,所以就产生了错误。这个 = 操作符是一个二元操作符(它要求每边都有一个数值),但是第一个数值是缺失的,这样 test 命令就期望用一个一元操作符(比如 -z)来代替。进一步说,因为 test 命令运行失败了(由于错误),这个 if 命令接收到一个非零退出代码,因此执行第二个 echo 命令。

通过为 test 命令中的第一个参数添加双引号,可以更正这个问题:

[ "$number" = 1 ]

然后当展开操作发生地时候,执行结果将会是这样:

[ "" = 1 ]

其得到了正确的参数个数。除了代表空字符串之外,引号应该被用于这样的场合,一个要展开成多单词字符串的数值,及其包含嵌入式空格的文件名。

31.2 逻辑错误

不同于语法错误,逻辑错误不会阻止脚本执行。虽然脚本会正常运行,但是它不会产生期望的结果,归咎于脚本的逻辑问题。虽然有不计其数的可能的逻辑错误,但下面是一些在脚本中找到的最常见的逻辑错误类型:

  1. 不正确的条件表达式。很容易编写一个错误的 if/then/else 语句,并且执行错误的逻辑。有时候逻辑会被颠倒,或者是逻辑结构不完整。

  2. “超出一个值”错误。当编写带有计数器的循环语句的时候,为了计数在恰当的点结束,循环语句可能要求从 0 开始计数,而不是从 1 开始,这有可能会被忽视。这些类型的错误要不导致循环计数太多,而“超出范围”,要不就是过早的结束了一次迭代,从而错过了最后一次迭代循环。

  3. 意外情况。大多数逻辑错误来自于程序碰到了程序员没有预见到的数据或者情况。这也可以包括出乎意料的展开,比如说一个包含嵌入式空格的文件名展开成多个命令参数而不是单个的文件名。

31.2.1 防错编程

当编程的时候,验证假设非常重要。这意味着要仔细地计算脚本所使用的程序和命令的退出状态。这里有个基于一个真实的故事的实例。为了在一台重要的服务器中执行维护任务,一位不幸的系统管理员写了一个脚本。这个脚本包含下面两行代码:

cd $dir_name
rm *

从本质上来说,这两行代码没有任何问题,只要是变量 dir_name中存储的目录名字存在就可以。但是如果不是这样会发生什么事情呢?在那种情况下,cd 命令会运行失败,脚本会继续执行下一行代码,将会删除当前工作目录中的所有文件。完成不是期望的结果!由于这种设计策略,这个倒霉的管理员销毁了服务器中的一个重要部分。

让我们看一些能够提高这个设计的方法。首先,在 cd 命令执行成功之后,再运行 rm 命令,可能是明智的选择。

cd $dir_name && rm *

这样,如果 cd 命令运行失败后,rm 命令将不会执行。这样比较好,但是仍然有可能未设置变量 dir_name 或其变量值为空,从而导致删除了用户家目录下面的所有文件。这个问题也能够避免,通过检验变量 dir_name 中包含的目录名是否真正地存在:

[[ -d $dir_name ]] && cd $dir_name && rm *

通常,当某种情况(比如上述问题)发生的时候,最好是终止脚本执行,并对这种情况提示错误信息:

if [[ -d $dir_name ]]; then
    if cd $dir_name; then
        rm *
    else
        echo "cannot cd to '$dir_name'" >&2
        exit 1
    fi
else
    echo "no such directory: '$dir_name'" >&2
    exit 1
fi

这里,我们检验了两种情况,一个名字,看看它是否为一个真正存在的目录,另一个是 cd 命令是否执行成功。如果任一种情况失败,就会发送一个错误说明信息到标准错误,然后脚本终止执行,并用退出状态 1 表明脚本执行失败。

31.2.2 验证输入

一个良好的编程习惯是如果一个程序可以接受输入数据,那么这个程序必须能够应对它所接受的任意数据。这通常意味着必须非常仔细地筛选输入数据,以确保只有有效的输入数据才能被程序用来做进一步地处理。在前面章节中我们学习 read 命令的时候,我们遇到过一个这样的例子。一个脚本中包含了下面一条测试语句,用来验证一个选择菜单:

[[ $REPLY =~ ^[0-3]$ ]]

这条测试语句非常明确。只有当用户输入是一个位于 0 到 3 范围内(包括 0 和 3)的数字的时候,这条语句才返回一个 0 退出状态。而其它任何输入概不接受。有时候编写这类测试条件非常具有挑战性,但是为了能产出一个高质量的脚本,付出还是必要的。

设计是时间的函数

当我还是一名大学生,在学习工业设计的时候,一位明智的教授说过一个项目的设计程度是由给定设计师的时间量来决定的。如果给你五分钟来设计一款能够 “杀死苍蝇”的产品,你会设计出一个苍蝇拍。如果给你五个月的时间,你可能会制作出激光制导的“反苍蝇系统”。

同样的原理适用于编程。有时候一个 “快速但粗糙” 的脚本就可以解决问题,但这个脚本只能被其作者使用一次。这类脚本很常见,为了节省气力也应该被快速地开发出来。所以这些脚本不需要太多的注释和防错检查。相反,如果一个脚本打算用于生产使用,也就是说,某个重要任务或者多个客户会不断地用到它,此时这个脚本就需要非常谨慎小心地开发了。

31.3 测试

在各类软件开发中(包括脚本),测试是一个重要的环节。在开源世界中有一句谚语,“早发布,常发布”,这句谚语就反映出这个事实(测试的重要性)。通过提早和经常发布,软件能够得到更多曝光去使用和测试。经验表明如果在开发周期的早期发现 bug,那么这些 bug 就越容易定位,而且越能低成本的修复。

在之前的讨论中,我们知道了如何使用 stubs 来验证程序流程。在脚本开发的最初阶段,它们是一项有价值的技术来检测我们的工作进度。

让我们看一下上面的文件删除问题,为了轻松测试,看看如何修改这些代码。测试原本那个代码片段将是危险的,因为它的目的是要删除文件,但是我们可以修改代码,让测试安全:

if [[ -d $dir_name ]]; then
    if cd $dir_name; then
        echo rm * # TESTING
    else
        echo "cannot cd to '$dir_name'" >&2
        exit 1
    fi
else
    echo "no such directory: '$dir_name'" >&2
    exit 1
fi
exit # TESTING

因为在满足出错条件的情况下代码可以打印出有用信息,所以我们没有必要再添加任何额外信息了。最重要的改动是仅在 rm 命令之前放置了一个 echo 命令,为的是把 rm 命令及其展开的参数列表打印出来,而不是执行实际的 rm 命令语句。这个改动可以安全的执行代码。在这段代码的末尾,我们放置了一个 exit 命令来结束测试,从而防止执行脚本其它部分的代码。这个需求会因脚本的设计不同而变化。

我们也在代码中添加了一些注释,用来标记与测试相关的改动。当测试完成之后,这些注释可以帮助我们找到并删除所有的更改。

31.3.1 测试案例

为了执行有用的测试,开发和使用好的测试案例是很重要的。这个要求可以通过谨慎地选择输入数据或者运行边缘案例和极端案例来完成。在我们的代码片段中(是非常简单的代码),我们想要知道在下面的三种具体情况下这段代码是怎样执行的:

  1. dir_name 包含一个已经存在的目录的名字

  2. dir_name 包含一个不存在的目录的名字

  3. dir_name 为空

通过执行以上每一个测试条件,就达到了一个良好的测试覆盖率。

正如设计,测试也是一个时间的函数。不是每一个脚本功能都需要做大量的测试。问题关键是确定什么功能是最重要的。因为测试若发生故障会存在如此潜在的破坏性,所以我们的代码片在设计和测试段期间都应值得仔细推敲。

31.4 调试

如果测试暴露了脚本中的一个问题,那下一步就是调试了。“一个问题”通常意味着在某种情况下,这个脚本的执行结果不是程序员所期望的结果。若是这种情况,我们需要仔细确认这个脚本实际到底要完成什么任务,和为什么要这样做。有时候查找 bug 要牵涉到许多监测工作。一个设计良好的脚本会对查找错误有帮助。设计良好的脚本应该具备防卫能力,能够监测异常条件,并能为用户提供有用的反馈信息。然而有时候,出现的问题相当稀奇,出人意料,这时候就需要更多的调试技巧了。

31.4.1 找到问题区域

在一些脚本中,尤其是一些代码比较长的脚本,有时候隔离脚本中与出现的问题相关的代码区域对查找问题很有帮助。隔离的代码区域并不总是真正的错误所在,但是隔离往往可以深入了解实际的错误原因。可以用来隔离代码的一项技巧是“添加注释”。例如,我们的文件删除代码可以修改成这样,从而决定注释掉的这部分代码是否导致了一个错误:

if [[ -d $dir_name ]]; then
    if cd $dir_name; then
        rm *
    else
        echo "cannot cd to '$dir_name'" >&2
        exit 1
    fi
# else
# echo "no such directory: '$dir_name'" >&2
# exit 1
fi

通过给脚本中的一个逻辑区块内的每条语句的开头添加一个注释符号,我们就阻止了这部分代码的执行。然后可以再次执行测试,来看看清除的代码是否影响了错误的行为。

31.4.2 追踪

在一个脚本中,错误往往是由意想不到的逻辑流导致的。也就是说,脚本中的一部分代码或者从未执行,或是以错误的顺序,或在错误的时间给执行了。为了查看真实的程序流,我们使用一项叫做追踪(tracing)的技术。

一种追踪方法涉及到在脚本中添加可以显示程序执行位置的提示性信息。我们可以添加提示信息到我们的代码片段中:

echo "preparing to delete files" >&2
if [[ -d $dir_name ]]; then
    if cd $dir_name; then
echo "deleting files" >&2
        rm *
    else
        echo "cannot cd to '$dir_name'" >&2
        exit 1
    fi
else
    echo "no such directory: '$dir_name'" >&2
    exit 1
fi
echo "file deletion complete" >&2

我们把提示信息输出到标准错误输出,让其从标准输出中分离出来。我们也没有缩进包含提示信息的语句,这样想要删除它们的时候,能比较容易找到它们。

当这个脚本执行的时候,就可能看到文件删除操作已经完成了:

[me@linuxbox ~]$ deletion-script
preparing to delete files
deleting files
file deletion complete
[me@linuxbox ~]$

bash 还提供了一种名为追踪的方法,这种方法可通过 -x 选项和 set 命令加上 -x 选项两种途径实现。拿我们之前的 trouble 脚本为例,给该脚本的第一行语句添加 -x 选项,我们就能追踪整个脚本。

#!/bin/bash -x
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ]; then
    echo "Number is equal to 1."
else
    echo "Number is not equal to 1."
fi

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

[me@linuxbox ~]$ trouble
+ number=1
+ '[' 1 = 1 ']'
+ echo 'Number is equal to 1.'
Number is equal to 1.

追踪生效后,我们看到脚本命令展开后才执行。行首的加号表明追踪的迹象,使其与常规输出结果区分开来。加号是追踪输出的默认字符。它包含在 PS4(提示符4)shell 变量中。可以调整这个变量值让提示信息更有意义。这里,我们修改该变量的内容,让其包含脚本中追踪执行到的当前行的行号。注意这里必须使用单引号是为了防止变量展开,直到提示符真正使用的时候,就不需要了。

[me@linuxbox ~]$ export PS4='$LINENO + '
[me@linuxbox ~]$ trouble
5 + number=1
7 + '[' 1 = 1 ']'
8 + echo 'Number is equal to 1.'
Number is equal to 1.

我们可以使用 set 命令加上 -x 选项,为脚本中的一块选择区域,而不是整个脚本启用追踪。

#!/bin/bash
# trouble: script to demonstrate common errors
number=1
set -x # Turn on tracing
if [ $number = 1 ]; then
    echo "Number is equal to 1."
else
    echo "Number is not equal to 1."
fi
set +x # Turn off tracing

我们使用 set 命令加上 -x 选项来启动追踪,+x 选项关闭追踪。这种技术可以用来检查一个有错误的脚本的多个部分。

31.4.3 执行时检查数值

伴随着追踪,在脚本执行的时候显示变量的内容,以此知道脚本内部的工作状态,往往是很用的。使用额外的 echo 语句通常会奏效。

#!/bin/bash
# trouble: script to demonstrate common errors
number=1
echo "number=$number" # DEBUG
set -x # Turn on tracing
if [ $number = 1 ]; then
    echo "Number is equal to 1."
else
    echo "Number is not equal to 1."
fi
set +x # Turn off tracing

在这个简单的示例中,我们只是显示变量 number 的数值,并为其添加注释,随后利于其识别和清除。当查看脚本中的循环和算术语句的时候,这种技术特别有用。

31.5 总结

在这一章中,我们仅仅看了几个在脚本开发期间会出现的问题。当然,还有很多。这章中描述的技术对查找大多数的常见错误是有效的。调试是一种艺术,可以通过开发经验,在知道如何避免错误(整个开发过程中不断测试)
以及在查找 bug(有效利用追踪)两方面都会得到提升。

更新于 2022年10月2日
第30章 : 流程控制:while/until 循环第32章 : 流程控制:case 分支

发表评论 取消回复

您需要登录后才可以发表评论...
登录... 后才能评论
文章目录
  • 31.1 语法错误
    • 31.1.1 丢失引号
    • 31.1.2 丢失或意外的标记
    • 31.1.3 预料不到的展开
  • 31.2 逻辑错误
    • 31.2.1 防错编程
    • 31.2.2 验证输入
  • 31.3 测试
    • 31.3.1 测试案例
  • 31.4 调试
    • 31.4.1 找到问题区域
    • 31.4.2 追踪
    • 31.4.3 执行时检查数值
  • 31.5 总结

Copyright © 2015-2023 开源之家

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