网安
  • Develop
    • JAVA学习
      • 字节码
      • API开发
      • Web开发
      • 工程结构推荐
      • 创建第一个项目
      • 权限管控
      • 配置文件
      • 日志管理
      • 数据访问
      • 性能监控
      • IoC容器
      • Spring
      • Filter与Listener
      • jsp
      • MVC
      • servlet-1
      • servlet-2
      • servlet-3
      • servlet-4
      • FreeMarker
      • Thymeleaf
      • EL
      • SpEL
      • JSTL
      • 部署
      • JDBC
      • 数据库连接池
      • fastjson
      • jackson
      • XML
      • JSON
      • 序列化
      • Maven
      • 安装与使用
      • 工具
      • 爬虫
    • GO学习
      • GO
        • flag 包
        • goland 远程调试
        • GoReleaser
        • OS 包
        • time 包
        • 格式化输出
    • Lua学习
      • Lua
      • 基础语法
      • LuaJIT
      • 与系统交互
    • Pyhon
      • 基础
      • Django
      • CLI
      • miniforge
      • MockingBird
      • pdb
      • pyc
      • 装的我脑血栓要犯了
      • Python101
      • 反序列化
      • 爬虫
      • Pillow
      • 图像识别
      • flask
    • Speed-Ruby
      • 入门1
      • 入门2 对象
      • 入门3 创建命令
      • Encoding类
      • File类与Dir类
      • IO
      • Proc类
      • Time类与Date类
      • 正则
      • 错误处理与异常
      • 对象、变量和常量
      • 方法
      • 数值
      • 数组
      • 条件判断
      • 循环
      • 运算符
      • Socket编程
      • 字符串
      • 并发与线程
      • 块
      • 类和模块
      • 散列
    • Web
      • HTTP
        • Connection
        • HTTP 报文
        • Vary
      • 笔记
        • 跳转
        • 认证 & 授权
        • 同源策略(SOP)
        • 文件
    • Git 学习笔记
    • JSON
      • JSON 学习笔记
    • HTML
      • Speed-HTML
      • 语法学习
      • HTML字符实体
    • XML
      • XML 学习笔记
    • 计算机基础
      • 操作系统
      • 计算机组成
      • 算法
      • 内存
      • 字符编码
    • gnuplot 学习笔记
    • regex
  • Integrated
    • Linux
      • God-Linux
      • Secure-Linux
      • Power-Linux
      • IO模型
      • Speed-Linux
      • 发行版
      • 工具
      • 启动过程
      • 进程
      • 认证
      • 日志
      • 守护进程
      • 文件
      • 信息
      • VSFTP 配置案例
      • auditd
      • containerd
      • DNS 配置案例
      • Docker
      • Docker-Compose
      • firewalld 实验
      • gpg
      • Iptables
      • httpd
      • LAMP
      • mysql
      • nfs 配置案例
      • openssl
      • PAM
      • samba 配置案例
      • terraform
      • ufw
      • VSFTP 配置案例
    • Network
      • Speed-Net
      • Power-Net
      • SDN 笔记
      • DNS
      • TLS
    • Windows
      • Secure-Win
      • Speed-Win
      • ACL
      • LDAP
      • IPC$(Internet Process Connection)
      • PDB符号文件
      • 工作组
      • WinRM
      • 角色权限
      • 凭据
      • 签名
      • 日志
      • 认证
      • 协议
      • 信息
      • 应用
      • 组策略
      • 域
      • asp站点搭建
      • Exchange 搭建
      • Windows 故障转移集群
      • Windows 基础服务搭建
      • Windows 域搭建
      • 本地抓包
      • PowerShell 笔记
    • 容器
      • Docker
    • 数据库
      • Speed-SQL
      • Power-SQL
      • MSSQL
      • MySQL
      • Postgresql
      • Redis
      • MySQL大小写问题
      • 主键和外键
      • MySQL快速入门
      • 虚拟化
        • ESXi
        • vCenter
  • Plan
    • Mac-Plan
    • Misc-Plan
    • Team-Plan
    • Thinking-Plan
    • VM-Plan
  • Sercurity
    • Power-PenTest
    • BlueTeam
      • 安全建设
      • 分析
      • 加固
      • 取证
      • 应急
      • USB取证
      • 磁盘取证
      • 内存取证
      • ClamAV 部署
      • yara 实验
      • 安防设施搭建使用
      • ZIP明文攻击
      • 流量分析
    • Crypto
      • Crypto
        • 2020 9 G60攻防大赛
        • CTF
        • 2020 9 中能融合杯工控CTF
        • 2020 10 全国工业互联网安全技术技能大赛江苏省选拔赛
        • 2020 10 全国网络与信息安全管理职业技能大赛江苏场
        • 2020 11 I²S峰会暨工业互联网安全大赛
        • 2021 6 第二届I²S峰会暨工业互联网安全大赛
        • 2021-9-第七届工控信息安全攻防竞赛
        • 2021 9 第七届全国职工职业技能大赛某市县选拔赛
        • 2021 9 全国网络与信息安全管理职业技能大赛江苏场
        • 2021-10-G60攻防大赛
    • CTF
      • CTF
      • writeup
        • 2020 9 中能融合杯工控CTF
        • 2020 9 G60攻防大赛
        • 2020 10 全国工业互联网安全技术技能大赛江苏省选拔赛
        • 2020 10 全国网络与信息安全管理职业技能大赛江苏场
        • 2020 11 I²S峰会暨工业互联网安全大赛
        • 2021 6 第二届I²S峰会暨工业互联网安全大赛
        • 2021-9-第七届工控信息安全攻防竞赛
        • 2021 9 第七届全国职工职业技能大赛某市县选拔赛
        • 2021 9 全国网络与信息安全管理职业技能大赛江苏场
        • 2021-10-G60攻防大赛
    • ICS
      • PLC攻击
      • S7comm 相关
      • 工控协议
      • 上位机安全
      • Modbus 仿真环境搭建
      • siemens 仿真搭建实验
      • S7-300 启停实验
    • IOT
      • 无线电安全
        • RFID复制卡
        • RFID基础知识
        • WiFikiller
      • 硬件安全
        • DIY键盘嵌入指纹识别模块实验记录
        • Device-Exploits
        • HID-Digispark
        • HID-KeyboardLogger
        • HID-USBHarpoon
        • HID-USBKeyLogger
      • 固件安全
        • 固件安全
        • Dlink_DWR-932B 路由器固件分析
    • Mobile sec
      • 小程序安全
      • Android安全
    • PWN
      • SLMail溢出案例
      • PWN
    • Red Team
      • OS安全
        • Linux 安全
        • Exploits
        • NTLM中继
        • Windows 安全
        • Responder欺骗
        • Windows-LOL
      • Web_Generic
        • Top 10
          • RCE
          • Fileread
          • SQLi
          • SSRF
          • SSTI
          • Web Generic
          • XSS
          • XXE
      • Web_Tricks
        • JWT 安全
        • HTTP_request_smuggling
        • OOB
        • 绕过访问
      • 靶场
        • Hello-Java-Sec 学习
        • DVWA-WalkThrough
        • pikachu-WalkThrough
        • upload-labs-WalkThrough
        • XVWA-WalkThrough
        • XSS挑战-WalkThrough
      • 实验
        • flask
        • fastjson
        • Log4j
        • nodejs
        • Shiro
        • Spring
        • Weblogic
      • 前端攻防
      • IDOR
    • 安防设备
      • Exploits
      • Bypass 技巧
    • 后渗透
      • 权限提升
      • 后渗透
      • 权限维持
      • 实验
        • C2 实验
        • Exchange
        • 端口转发实验
        • 代理实验
        • 免杀实验
        • 隧道实验
    • 软件服务安全
      • Exploits
      • CS Exploits
      • 实验
        • Docker
        • Kubernetes
        • Mysql
        • Oracle
        • PostgreSQL
        • Redis
        • vCenter
    • 协议安全
      • Exploits
    • 信息收集
      • 端口安全
      • 空间测绘
      • 信息收集
    • 语言安全
      • 语言安全
        • 语言安全
      • GO安全
        • GO安全
        • Go代码审计
      • JAVA安全
        • JAVA安全
        • JAVA代码审计
        • JAVA反序列化
        • SpEL 注入
      • PHP安全
        • PHP安全
        • bypass_disable_function
        • bypass_open_basedir
        • phpinfo
        • PHP代码审计
        • PHP反序列化
        • PHP回调函数
        • 变量覆盖
        • POP
        • 弱类型
        • 伪协议
        • 无字母数字Webshell
      • Python安全
        • pyc反编译
        • Python安全
        • Python 代码审计
        • 沙箱逃逸
      • dotnet安全
      • JS安全
    • 云安全
      • 公有云安全
    • Reverse
      • Reverse
      • FILE
        • ELF
        • BMP
        • JPG
        • PE
        • PNG
        • ZIP
        • 文件头
      • 实验
        • PYAble
          • 2-逆运算
          • 1-基本分析
          • 3-异或
          • 4-Base64
          • 5-Base64换表
          • 6-动态调试
        • Windows
          • condrv.sys 内存损坏漏洞
    • 工具
      • Aircrack
      • BloodHound
      • Burp Suite
      • frp
      • CobaltStrike
      • Ghidra
      • fscan
      • Hashcat
      • IDA
      • merlin
      • Kali
      • Metasploit
      • Mimikatz
      • ModSecurity
      • Nmap
      • nps
      • nuclei
      • pupy
      • RedGuard
      • SET
      • sliver
      • Snort
      • Sqlmap
      • Suricata
      • Sysmon
      • uncover
      • Volatility
      • Wfuzz
      • Wireshark
      • xray
    • 安全资源
      • 靶机
        • VulnHub
          • DC
            • DC2 WalkThrough
            • DC1 WalkThrough
            • DC3 WalkThrough
            • DC4 WalkThrough
            • DC5 WalkThrough
            • DC6 WalkThrough
            • DC9 WalkThrough
            • DC8 WalkThrough
          • It's_October
            • It’s_October1 WalkThrough
          • Kioptrix
            • Kioptrix2 WalkThrough
            • Kioptrix3 WalkThrough
            • Kioptrix4 WalkThrough
            • Kioptrix5 WalkThrough
          • Mission-Pumpkin
            • PumpkinGarden-WalkThrough
            • PumpkinFestival WalkThrough
            • PumpkinRaising WalkThrough
          • Symfonos
            • symfonos1 WalkThrough
            • symfonos2 WalkThrough
            • symfonos3 WalkThrough
            • symfonos5 WalkThrough
        • Wargames
          • Bandit
            • Bandit-WalkThrough
      • 面试问题
        • 面试问题
Powered by GitBook
On this page
  • 错误处理与异常
  • 关于错误处理
  • 异常处理
  • 异常处理的写法
  • 后处理
  • 重试
  • rescue 修饰符
  • 异常处理语法的补充
  • 指定需要捕捉的异常
  • 异常类
  • 主动抛出异常
  1. Develop
  2. Speed-Ruby

错误处理与异常

错误处理与异常


  • https://www.kancloud.cn/imxieke/ruby-base/107297


关于错误处理

在程序执行的过程中,通常会有以下错误发生:

  • 数据错误

    在计算家庭收支的时候,若在应该写金额的一栏上填上了商品名,那么就无法计算。此外,HTML 这种格式的数据的情况下,如果存在没有关闭标签等语法错误,也会导致无法处理数据。

  • 系统错误

    硬盘故障等明显的故障,或者没把 CD 插入到驱动器等程序无法恢复的问题。

  • 程序错误

    因调用了不存在的方法、弄错参数值或算法错误而导致错误结果等,像这样,程序本身的缺陷也可能会导致错误。

    程序在运行时可能会遇到各种各样的错误。如果对这些错误放任不管,大部分程序都无法正常运行,因此我们需要对这些错误做相应的处理。

  • 排除错误的原因

    在文件夹中创建文件时,如果文件夹不存在,则由程序本身创建文件夹。如果程序无法创建文件夹,则需要再考虑其他解决方法。

  • 忽略错误

    程序有时候也会有一些无伤大雅的错误。例如,假设运行程序时需要读取某个配置文件,如果我们事前已经在程序中准备好了相应配置的默认值,那么即使无法读取该设定文件,程序也可以忽略这个错误。

  • 恢复错误发生前的状态

    向用户提示程序发生错误,指导用户该如何进行下一步处理。

  • 重试一次

    曾经执行失败的程序,过一段时间后再重新执行可能就会成功。

  • 终止程序

    只是自己一个人用的小程序,也许本来就没必要做错误处理。

    而至于实际应该采取何种处理,则要根据程序代码的规模、应用程序的性质来决定,不能一概而论。但是,对于可预期的错误,我们需要留意以下两点:

  • 是否破坏了输入的数据,特别是人工制作的数据。

  • 是否可以对错误的内容及其原因做出相应的提示。

    覆盖了原有文件、删除了花费大量时间输入的数据等,像这样的重要数据的丢失、破坏可以说是灾难性的错误。另外,如果错误是由用户造成的,或者程序自身不能修复的话,给用户简明易懂的错误提示,会大大提升程序的用户体验。

异常处理

在程序执行的过程中,如果程序出现了错误就会发生异常。异常发生后,程序会暂时停止运行,并寻找是否有对应的异常处理程序。如果有则执行,如果没有,程序就会显示类似以下信息并终止运行。

> ruby test.rb
test.rb:2:in `initialize': No such file or directory - /no/file(Errno::ENOENT)
        from test.rb:2:in `open'
        from test.rb:2:in `foo'
        from test.rb:2:in `bar'
        from test.rb:9:in `main'

该信息的格式如下:

文件名: 行号:in 方法名: 错误信息(异常类名)
from 文件名: 行号:in 方法名
    ┊

以 from 开头的行表示发生错误的位置。

没有异常处理的编程语言的情况下,编程时就需要逐个确认每个处理是否已经处理完毕。在这类编程语言中,大部分程序代码都被花费在错误处理上,因此往往会使程序变得繁杂。

异常处理有以下优点:

  • 程序不需要逐个确认处理结果,也能自动检查出程序错误

  • 会同时报告发生错误的位置,便于排查错误

  • 正常处理与错误处理的程序可以分开书写,使程序便于阅读

异常处理的写法

Ruby 中使用 begin ~ rescue ~ end 语句描述异常处理。

begin
 可能会发生异常的处理
rescue
 发生异常时的处理
end

在 Ruby 中,异常及其相关信息都是被作为对象来处理的。在 rescue 后指定变量名,可以获得异常对象。

begin
 可能会发生异常的处理
rescue => 引用异常对象的变量
 发生异常时的处理
end

即使不指定变量名,Ruby 也会把异常对象赋值给变量 $!。不过,把变量名明确地写出来会使程序更加易懂。

异常发生时被自动赋值的变量

变量 | 意义

  • | - $! | 最后发生的异常(异常对象) $@ | 最后发生的异常的位置信息

异常对象的方法

方法名 | 意义

  • | - class | 异常的种类 message | 异常信息 backtrace | 异常发生的位置信息($@ 与 $!.backtrace 是等价的)

下面是 Unix 的 wc 命令的简易版。结果会输出参数中指定的各文件的行数、单词数、字数(字节数),最后输出全部文件的统计结果。

ltotal=0                             # 行数合计
wtotal=0                             # 单词数合计
ctotal=0                             # 字数合计
ARGV.each do |file|
  begin
    input = File.open(file)          # 打开文件(A)
    l=0                              # file 内的行数
    w=0                              # file 内的单词数
    c=0                              # file 内的字数
    input.each_line do |line|
      l += 1
      c += line.size
      line.sub!(/^\s+/, "")          # 删除行首的空白符
      ary = line.split(/\s+/)        # 用空白符分解
      w += ary.size
    end
    input.close                      # 关闭文件
    printf("%8d %8d %8d %s\n", l, w, c, file)  # 整理输出格式
    ltotal += l
    wtotal += w
    ctotal += c
  rescue => ex
    print ex.message, "\n"           # 输出异常信息(B)
    end
end

printf("%8d %8d %8d %s\n", ltotal, wtotal, ctotal, "total")

执行示例

> ruby wc.rb intro.rb sec01.rb sec02.rb
       50       67      1655 intro.rb
       81       92      3455 sec01.rb
      123      162      3420 sec02.rb
      254      321      8520 total

在(A)处无法打开文件时,程序会跳到 rescue 部分。这时,异常对象被赋值给变量 ex,(B)部分的处理被执行。

如果程序中指定了不存在的文件,则会提示发生错误,如下所示。提示发生错误后,并不会马上终止程序,而是继续处理下一个文件。

> ruby wc.rb intro.rb sec01.rb sec02.rb sec03.rb
       50       67      1655 intro.rb
       81       92      3455 sec01.rb
      123      188      3729 sec02.rb
No such file or directory - sec03.rb
      254      321      8520 total

如果发生异常的方法中没有 rescue 处理,程序就会逆向查找调用者中是否定义了异常处理。下面来看看下图这个例子。调用 foo 方法,尝试打开一个不存在的文件。若 File.open 方法发生异常,那么该异常就会跳过 foo 方法以及 bar 方法,被更上一层的 rescue 捕捉。

然而,并不是说每个方法都需要做异常处理,只需根据实际情况在需要留意的地方做就可以了。在并不特别需要解决错误的情况下,也可以不捕捉异常。当然,不捕捉异常就意味着如果有问题发生程序就会马上终止。

后处理

不管是否发生异常都希望执行的处理,在 Ruby 中可以用 ensure 关键字来定义。

begin
 有可能发生异常的处理
rescue => 变量
 发生异常后的处理
ensure
 不管是否发生异常都希望执行的处理
end

现在,假设我们要实现一个拷贝文件的方法,如下所示。下面的 copy 方法是把文件从 from 拷贝到 to。

def copy(from, to)
  src = File.open(from)         # 打开原文件from(A)
  begin
    dst = File.open(to, "w")    # 打开目标文件to(B)
    data = src.read
    dst.write(data)
    dst.close
  ensure
    src.close                   # (C)
  end
end

在(A)部分,如果程序不能打开原文件,那么就会发生异常并把异常返回给调用者。这时,不管接下来的处理是否能正常执行,src 都必须得关闭。关闭 src 的处理在(C)部分执行。ensure 中的处理,在程序跳出 begin ~ end 部分时一定会被执行。即使(B)中的目标文件无法打开,(C)部分的处理也同样会被执行。

重试

在 rescue 中使用 retry 后,begin 以下的处理会再重做一遍。

在下面的例子中,程序每隔 10 秒执行一次 File.open,直到能成功打开文件为止,打开文件后再读取其内容。

file = ARGV[0]
begin
  io = File.open(file)
rescue
  sleep 10
  retry
end

data = io.read
io.close

不过需要注意的是,如果指定了无论如何都不能打开的文件,程序就会陷入死循环中。

rescue 修饰符

与 if 修饰符、unless 修饰符一样,rescue 也有对应的修饰符。

表达式 1 rescue 表达式 2

如果表达式 1 中发生异常,表达式 2 的值就会成为整体表达式的值。也就是说,上面的式子与下面的写法是等价的:

begin
 表达式 1
rescue
 表达式 2
end

我们再来看看下面的例子:

n = Integer(val) rescue 0

Integer 方法当接收到 "123" 这种数值形式的字符串参数时,会返回该字符串表示的整数值,而当接收到 "abc" 这种非数值形式的字符串参数时,则会抛出异常(在判断字符串是否为数值形式时经常用到此方法)。在本例中,如果 val 是不正确的数值格式,就会抛出异常,而 0 则作为 = 右侧整体表达式的返回值。像这样,这个小技巧经常被用在不需要过于复杂的处理,只是希望简单地对变量赋予默认值的时候。

异常处理语法的补充

如果异常处理的范围是整个方法体,也就是说整个方法内的程序都用 begin ~ end 包含的话,我们就可以省略 begin 以及 end,直接书写 rescue 与 ensure 部分的程序。

def foo
 方法体
rescue => ex
 异常处理
ensure
 后处理
end

同样,我们在类定义中也可以使用 rescue 以及 ensure。但是,如果类定义途中发生异常,那么异常发生部分后的方法定义就不会再执行了,因此一般我们不会在类定义中使用它们。

class Foo
 类定义
rescue => ex
 异常处理
ensure
 后处理
end

指定需要捕捉的异常

当存在多个种类的异常,且需要按异常的种类分别进行处理时,我们可以用多个 rescue 来分开处理。

begin
 可能发生异常的处理
rescue Exception1, Exception2 => 变量
 对Exception1 或者Exception2 的处理
rescue Exception3 => 变量
 对Exception3 的处理
rescue
 对上述异常以外的异常的处理
end

通过直接指定异常类,可以只捕捉我们希望处理的异常。

file1 = ARGV[0]
file2 = ARGV[1]
begin
  io = File.open(file1)
rescue Errno::ENOENT, Errno::EACCES
  io = File.open(file2)
end

在本例中,程序如果无法打开 file1 就会打开 file2。程序中捕捉的 Errno::ENOENT 以及 Errno::EACCES,分别是文件不存在以及没权限打开文件时发生的异常。

异常类

之前我们提到过异常也是对象。Ruby 中所有的异常都是 Exception 类的子类,并根据程序错误的种类来定义相应的异常。下图为 Ruby 标准库中的异常类的继承关系。

在 rescue 中指定的异常的种类实际上就是异常类的类名。rescue 中不指定异常类时,程序会默认捕捉 StandardError 类及其子类的异常。

rescue 不只会捕捉指定的异常类,同时还会捕捉其子类。因此,我们在自己定义异常时,一般会先定义继承 StandardError 类的新类,然后再继承这个新类。

MyError = Class.new(StandardError)    # 新的异常类
MyError1 = Class.new(MyError)
MyError2 = Class.new(MyError)
MyError3 = Class.new(MyError)

这样定义后,通过以下方式捕捉异常的话,同时就会捕捉 MyError 类的子类 MyError1、MyError2、MyError3 等。

begin
  ┊
rescue MyError
  ┊
end

在本例中,

MyError = Class.new(StandardError)

上述写法的作用是定义一个继承 StandardError 类的新类,并将其赋值给 MyError 常量。这与 class 语句定义类的效果是一样的。

class MyError < StandardError
end

使用 class 语句,我们可以进行定义方法等操作,但在本例中,由于我们只需要生成继承 StandardError 类的新类就可以了,所以就向大家介绍了这个只需 1 行代码就能实现类的定义的简洁写法。

主动抛出异常

使用 raise 方法,可以使程序主动抛出异常。在基于自己判定的条件抛出异常,或者把刚捕捉到的异常再次抛出并通知异常的调用者等情况下,我们会使用 raise 方法。

raise 方法有以下 4 种调用方式:

  • raise message

    抛出 RuntimeError 异常,并把字符串作为 message 设置给新生成的异常对象。

  • raise 异常类

    抛出指定的异常。

  • raise 异常类,message

    抛出指定的异常,并把字符串作为 message 设置给新生成的异常对象。

  • raise

    在 rescue 外抛出 RuntimeError。在 rescue 中调用时,会再次抛出最后一次发生的异常($!)。

Previous正则Next对象、变量和常量