Linux uuencode命令

历史说明:uuencode(Unix-to-Unix encode)是Unix系统中的一个传统工具,用于将二进制文件转换为ASCII文本格式,以便通过电子邮件等文本协议传输。虽然现在有更现代的替代方案(如Base64),但uuencode在某些旧系统中仍有使用。
功能说明:uuencode命令将二进制文件编码为ASCII文本格式,生成的文件可以通过文本方式传输。与之对应的uudecode命令用于解码还原原始文件。uuencode/uudecode是早期Unix系统中二进制文件传输的标准方法。
主要功能
  • 将二进制文件编码为ASCII文本
  • 支持指定输出文件名
  • 可处理标准输入
  • 生成包含文件权限的编码
  • 支持多文件编码
  • 编码结果可通过文本协议传输
安装说明

uuencode在大多数Linux发行版中需要单独安装:

  • Debian/Ubuntu: sudo apt install sharutils
  • RHEL/CentOS: sudo yum install sharutils
  • Fedora: sudo dnf install sharutils
  • Arch Linux: sudo pacman -S sharutils
  • openSUSE: sudo zypper install sharutils
  • 从源码编译: 下载GNU sharutils源码包

命令语法

语法格式
uuencode [选项] [输入文件] 输出文件名

命令参数

参数 说明
-m 使用Base64编码(而非传统uuencode)
-o 输出文件 将编码输出到指定文件(而非标准输出)
-p 编码到标准输出,并在最后显示编码统计
-v, --version 显示版本信息
--help 显示帮助信息
输入文件 要编码的文件(默认为标准输入)
输出文件名 编码文件中指定的输出文件名(必需)

使用示例

1. 基本编码操作 常用
示例
# 将二进制文件编码为文本
uuencode image.jpg image.jpg > image.uue

# 查看编码结果
head -5 image.uue
# 输出示例:
# begin 644 image.jpg
# M_]C_X``02D9)1@`!`@`"`0`4`!`0`%`!`0`#`0`0`"`0`0`!`0`$`!`0`#`!`8
# M`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0
# M`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0`"`!`0
# `

# 编码并直接输出到文件
uuencode -o image.uue image.jpg image.jpg

# 从标准输入读取并编码
cat binary.dat | uuencode binary.dat > binary.uue

# 编码文本文件
uuencode document.txt document.txt > doc.uue

# 编码时指定不同的输出文件名
uuencode source.pdf download.pdf > encoded.uue
# 解码时会生成download.pdf而非source.pdf
2. 使用Base64编码(现代替代)
# 使用Base64编码(-m参数)
uuencode -m image.jpg image.jpg > image.b64

# 查看Base64编码结果
head -3 image.b64
# 输出示例:
# begin-base64 644 image.jpg
# /9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a
# HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy

# Base64编码到标准输出
uuencode -m -p archive.tar.gz archive.tar.gz

# 比较uuencode和Base64编码大小
ls -lh image.*
# 输出:
# -rw-r--r-- 1 user user 100K Jan 10 10:00 image.jpg
# -rw-r--r-- 1 user user 134K Jan 10 10:00 image.b64
# -rw-r--r-- 1 user user 134K Jan 10 10:00 image.uue
# Base64和uuencode编码大小相近,都比原文件大约33%

# 使用纯粹的Base64命令
base64 image.jpg > image.base64
# 注意:纯粹的base64输出不包含uuencode格式的头尾标记
3. 解码操作(使用uudecode)
# 解码uuencode文件
uudecode image.uue
# 生成image.jpg文件

# 解码Base64编码的文件
uudecode image.b64
# 生成image.jpg文件

# 解码到指定文件
uudecode -o output.jpg image.uue

# 解码标准输入
cat encoded.uue | uudecode

# 解码并显示详细信息
uudecode -v image.uue
# 输出:image.uue: decoded image.jpg (102400 bytes)

# 只测试不解码
uudecode -t image.uue
# 检查文件完整性但不解码

# 解码时保持原始权限
# uuencode编码中包含文件权限,uudecode会自动恢复
uuencode -m script.sh script.sh > script.b64
uudecode script.b64
ls -l script.sh
# 输出:-rwxr-xr-x 1 user user ... script.sh
4. 编码权限信息
# 查看原始文件权限
ls -l script.sh
# 输出:-rwxr-xr-x 1 user user 1024 Jan 10 10:00 script.sh

# 编码文件(包含权限信息)
uuencode script.sh script.sh > script.uue
head -1 script.uue
# 输出:begin 755 script.sh
# 755是八进制权限表示(rwxr-xr-x)

# 解码后会恢复权限
uudecode script.uue
ls -l script.sh
# 输出:-rwxr-xr-x 1 user user 1024 Jan 10 10:00 script.sh

# 编码不同权限的文件
chmod 644 document.txt
uuencode document.txt document.txt > doc.uue
head -1 doc.uue
# 输出:begin 644 document.txt

# 编码设备文件等特殊文件
# 注意:普通用户可能无权限读取设备文件
sudo uuencode /dev/null null.dev > null.uue
head -1 null.uue
# 输出:begin 666 /dev/null
5. 处理多个文件
# 方法1:分别编码每个文件
for file in *.jpg; do
    uuencode "$file" "$file" > "${file}.uue"
done

# 方法2:使用tar打包后编码
tar -cf archive.tar *.jpg
uuencode archive.tar archive.tar > archive.uue

# 方法3:使用shar创建自解压文档
# shar工具可以创建包含多个文件的自解压shell脚本
shar *.jpg > images.shar
# 恢复时执行:sh images.shar

# 方法4:分别编码后合并
uuencode file1.txt file1.txt > files.uue
uuencode file2.txt file2.txt >> files.uue
uuencode file3.txt file3.txt >> files.uue
# 注意:这样合并的文件需要分别解码

# 解码多个文件
# 如果多个文件编码在一个文件中,uudecode会依次解码
uudecode files.uue
# 生成file1.txt, file2.txt, file3.txt
6. 实际应用场景
# 场景1:通过电子邮件发送附件
# 编码附件
uuencode -m report.pdf report.pdf > report.b64
# 将report.b64内容粘贴到邮件正文
# 接收方保存为.report.b64并运行:
uudecode report.b64

# 场景2:在论坛或聊天中分享文件
# 将二进制文件转换为文本,可直接粘贴
uuencode small_image.png small_image.png | head -20
# 复制输出的文本,接收方保存为.uue文件后解码

# 场景3:配置文件中的二进制数据
# 将小图标编码后嵌入配置文件
echo "# Icon data (uuencoded)" > config.ini
uuencode icon.ico icon.ico >> config.ini
echo "# End of icon data" >> config.ini

# 场景4:通过串口传输文件
# 编码文件
uuencode firmware.bin firmware.bin > firmware.uue
# 通过串口发送
cat firmware.uue > /dev/ttyS0
# 接收方从串口读取并解码
cat /dev/ttyS0 | uudecode

# 场景5:备份脚本中的二进制数据
#!/bin/bash
# 备份脚本,包含二进制数据
echo "Backup created: $(date)"
uuencode -m /etc/passwd passwd.backup
uuencode -m /etc/shadow shadow.backup
echo "Backup complete"
7. 编码格式验证和分析
# 检查uuencode文件格式
file image.uue
# 输出:image.uue: ASCII uuencoded text

# 检查Base64文件格式
file image.b64
# 输出:image.b64: ASCII text, with very long lines

# 查看编码文件头部信息
head -2 image.uue
# 输出:
# begin 644 image.jpg
# M_]C_X``02D9)1@`!`@`"`0`4`!`0`%`!`0`#`0`0`"`0`0`!`0`$`!`0`#`!`8

# 查看编码文件尾部信息
tail -2 image.uue
# 输出:
# `
# end

# 统计编码文件行数
wc -l image.uue
# 输出:451 image.uue

# 计算编码膨胀率
original_size=$(stat -c%s image.jpg)
encoded_size=$(stat -c%s image.uue)
ratio=$(echo "scale=2; $encoded_size * 100 / $original_size" | bc)
echo "原始大小: ${original_size}字节"
echo "编码大小: ${encoded_size}字节"
echo "膨胀率: ${ratio}%"

# 验证编码完整性
uuencode -t image.uue 2>/dev/null && echo "编码完整" || echo "编码损坏"

uuencode编码原理

uuencode编码过程
二进制数据(3字节=24位)
分割为4个6位组(每组0-63)
每个6位组加32(变成可打印ASCII)
转换为ASCII字符(32=空格,33=!,...)
编码文本(每行最多45字节原始数据)
编码算法详解
# uuencode算法:
# 1. 每3字节(24位)二进制数据分为4个6位组
# 2. 每个6位组值(0-63)加上32,变成可打印ASCII字符(32-95)
# 3. 空白字符(ASCII 32,即空格)用'`'字符(ASCII 96)表示
# 4. 每行编码45字节原始数据(60字符编码文本)

# 示例:编码三个字节 0x12 0x34 0x56
# 二进制:00010010 00110100 01010110
# 分为4个6位组:
# 000100 100011 010001 010110
# 十进制:4 35 17 22
# 加32:36 67 49 54
# ASCII字符:$ C 1 6
# 所以0x12 0x34 0x56编码为 "$C16"

# Base64算法(uuencode -m使用):
# 1. 每3字节(24位)分为4个6位组
# 2. 每个6位组映射到64字符表:A-Z a-z 0-9 + /
# 3. 每行76字符(规范建议)

# 查看编码表
echo "=== uuencode编码表 ==="
echo "值 字符  值 字符  值 字符  值 字符"
for ((i=0; i<16; i++)); do
    for ((j=0; j<4; j++)); do
        n=$((i + j*16))
        if [ $n -lt 64 ]; then
            c=$((n + 32))
            # 空格特殊处理
            if [ $c -eq 32 ]; then
                c=96  # 空格用'`'表示
            fi
            printf "%2d \\x$(printf %x $c)  " $n
        fi
    done
    echo
done

与其他编码工具对比

工具 格式 膨胀率 标准化 现代使用 特点
uuencode 传统uu编码 ~33% POSIX 遗留系统 Unix传统,包含权限
Base64 RFC 4648 ~33% IETF标准 广泛 Web标准,通用
xxd 十六进制 ~200% 工具特定 调试用 可读性好,用于调试
od 八进制/十六进制 POSIX 调试用 多种格式,可读性
openssl base64 Base64 ~33% 标准 安全应用 OpenSSL实现,安全
gzip+base64 压缩+编码 可小于0% 组合 高效传输 先压缩再编码,高效
各工具使用示例
# uuencode(传统)
uuencode file.bin file.bin > file.uue

# Base64(现代标准)
base64 file.bin > file.b64
# 或
uuencode -m file.bin file.bin > file.b64

# xxd(十六进制,用于调试)
xxd file.bin | head -5
# 输出:
# 00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
# 00000010: 0000 02d0 0000 0240 0802 0000 00c5 1c4c  .......@.......L

# od(八进制/十六进制转储)
od -x file.bin | head -5

# openssl base64
openssl base64 -in file.bin -out file.b64
openssl base64 -d -in file.b64 -out file.decoded

# gzip压缩后base64编码(高效传输)
gzip -c file.bin | base64 > file.bin.gz.b64
# 解码
base64 -d file.bin.gz.b64 | gzip -d > file.restored.bin

# 比较不同编码的大小
ls -lh file.*
# 输出示例:
# -rw-r--r-- 1 user user 100K file.bin      # 原始
# -rw-r--r-- 1 user user 134K file.b64      # Base64
# -rw-r--r-- 1 user user 134K file.uue      # uuencode
# -rw-r--r-- 1 user user 201K file.hex      # xxd十六进制
# -rw-r--r-- 1 user user  45K file.bin.gz.b64  # 压缩+编码

实际应用脚本

脚本1:批量文件编码转换工具
#!/bin/bash
# 批量文件编码转换工具

SOURCE_DIR="$1"
OUTPUT_DIR="$2"
ENCODING="base64"  # 或 "uuencode"
ACTION="encode"    # 或 "decode"

# 默认值
if [ -z "$SOURCE_DIR" ]; then
    SOURCE_DIR="."
fi

if [ -z "$OUTPUT_DIR" ]; then
    OUTPUT_DIR="encoded_files"
fi

echo "=== 批量文件编码转换工具 ==="
echo "源目录: $SOURCE_DIR"
echo "输出目录: $OUTPUT_DIR"
echo "编码类型: $ENCODING"
echo "操作: $ACTION"
echo "开始时间: $(date)"
echo ""

# 创建输出目录
mkdir -p "$OUTPUT_DIR"

# 检查命令是否存在
check_command() {
    if ! command -v "$1" >/dev/null 2>&1; then
        echo "错误: 命令未找到 - $1"
        return 1
    fi
    return 0
}

# 编码函数
encode_file() {
    local src_file="$1"
    local dst_file="$2"

    if [ "$ENCODING" = "base64" ]; then
        uuencode -m "$src_file" "$(basename "$src_file")" > "$dst_file"
    elif [ "$ENCODING" = "uuencode" ]; then
        uuencode "$src_file" "$(basename "$src_file")" > "$dst_file"
    else
        echo "错误: 不支持的编码类型: $ENCODING"
        return 1
    fi
}

# 解码函数
decode_file() {
    local src_file="$1"
    local dst_dir="$2"

    cd "$dst_dir"
    if uudecode "$src_file"; then
        echo "✓ 解码成功"
    else
        echo "✗ 解码失败"
        return 1
    fi
    cd - >/dev/null
}

# 处理单个文件
process_file() {
    local file="$1"
    local filename=$(basename "$file")
    local extension=""

    if [ "$ENCODING" = "base64" ]; then
        extension=".b64"
    elif [ "$ENCODING" = "uuencode" ]; then
        extension=".uue"
    fi

    echo "处理: $filename"

    if [ "$ACTION" = "encode" ]; then
        # 编码
        local output_file="$OUTPUT_DIR/${filename}${extension}"
        echo -n "  编码..."
        if encode_file "$file" "$output_file"; then
            # 计算大小和膨胀率
            original_size=$(stat -c%s "$file" 2>/dev/null || echo 0)
            encoded_size=$(stat -c%s "$output_file" 2>/dev/null || echo 0)

            if [ "$original_size" -gt 0 ]; then
                ratio=$(echo "scale=1; $encoded_size * 100 / $original_size" | bc 2>/dev/null || echo 0)
                echo "✓ 完成 (${original_size} → ${encoded_size} 字节, 膨胀: ${ratio}%)"
            else
                echo "✓ 完成"
            fi
        else
            echo "✗ 失败"
        fi
    elif [ "$ACTION" = "decode" ]; then
        # 解码
        echo -n "  解码..."
        if decode_file "$file" "$OUTPUT_DIR"; then
            echo "✓ 完成"
        else
            echo "✗ 失败"
        fi
    fi
}

# 主处理循环
processed=0
success=0
failed=0

if [ "$ACTION" = "encode" ]; then
    # 查找常规文件进行编码
    find "$SOURCE_DIR" -maxdepth 1 -type f ! -name ".*" | while read file; do
        processed=$((processed + 1))
        if process_file "$file"; then
            success=$((success + 1))
        else
            failed=$((failed + 1))
        fi
        echo ""
    done
elif [ "$ACTION" = "decode" ]; then
    # 查找编码文件进行解码
    find "$SOURCE_DIR" -maxdepth 1 -type f \( -name "*.uue" -o -name "*.b64" \) | while read file; do
        processed=$((processed + 1))
        if process_file "$file"; then
            success=$((success + 1))
        else
            failed=$((failed + 1))
        fi
        echo ""
    done
fi

# 显示统计信息
echo "=== 处理完成 ==="
echo "总处理文件: $processed"
echo "成功: $success"
echo "失败: $failed"
echo "输出目录: $OUTPUT_DIR"
echo ""

# 显示输出目录内容
if [ "$processed" -gt 0 ]; then
    echo "输出目录内容:"
    ls -lh "$OUTPUT_DIR" | head -20
fi
脚本2:电子邮件附件编码/解码工具
#!/bin/bash
# 电子邮件附件编码/解码工具

VERSION="1.0"
AUTHOR="System Tool"
DEFAULT_ENCODING="base64"

show_menu() {
    clear
    echo "╔══════════════════════════════════════════════╗"
    echo "║      电子邮件附件工具 v$VERSION               ║"
    echo "╚══════════════════════════════════════════════╝"
    echo ""
    echo "1. 编码文件为电子邮件正文"
    echo "2. 从电子邮件正文解码文件"
    echo "3. 编码多个文件"
    echo "4. 解码多个文件"
    echo "5. 查看编码文件信息"
    echo "6. 测试编码/解码完整性"
    echo "7. 设置默认编码 (当前: $DEFAULT_ENCODING)"
    echo "8. 退出"
    echo ""
    echo -n "请选择操作 [1-8]: "
}

encode_for_email() {
    echo -n "请输入要编码的文件路径: "
    read filepath

    if [ ! -f "$filepath" ]; then
        echo "错误: 文件不存在"
        read -p "按回车键继续..."
        return
    fi

    echo -n "使用Base64编码? [Y/n]: "
    read use_base64
    if [[ "$use_base64" =~ ^[Nn]$ ]]; then
        encoding="uuencode"
    else
        encoding="base64"
    fi

    echo -n "输出到文件? (留空显示在屏幕): "
    read output_file

    echo ""
    echo "=== 编码结果 ==="
    echo ""

    if [ -n "$output_file" ]; then
        echo "保存到: $output_file"
        echo "=== 开始编码 ===" > "$output_file"
        echo "" >> "$output_file"

        if [ "$encoding" = "base64" ]; then
            uuencode -m "$filepath" "$(basename "$filepath")" >> "$output_file"
        else
            uuencode "$filepath" "$(basename "$filepath")" >> "$output_file"
        fi

        echo "" >> "$output_file"
        echo "=== 编码结束 ===" >> "$output_file"

        echo "文件已保存。使用方法:"
        echo "1. 将 $output_file 内容复制到电子邮件正文"
        echo "2. 接收方保存邮件正文为 .uue 或 .b64 文件"
        echo "3. 运行: uudecode 文件名"
    else
        echo "复制以下内容到电子邮件正文:"
        echo ""
        echo "=== 开始复制 === (共3行分隔线)"
        echo ""

        if [ "$encoding" = "base64" ]; then
            uuencode -m "$filepath" "$(basename "$filepath")"
        else
            uuencode "$filepath" "$(basename "$filepath")"
        fi

        echo ""
        echo "=== 结束复制 ==="
        echo ""
        echo "接收方使用方法:"
        echo "1. 保存邮件正文为 attachment.uue"
        echo "2. 运行: uudecode attachment.uue"
    fi

    read -p "按回车键继续..."
}

decode_from_email() {
    echo "请选择解码方式:"
    echo "1. 从文件解码"
    echo "2. 粘贴内容解码"
    echo -n "选择 [1-2]: "
    read method

    if [ "$method" = "1" ]; then
        echo -n "输入编码文件路径: "
        read encoded_file

        if [ ! -f "$encoded_file" ]; then
            echo "错误: 文件不存在"
            read -p "按回车键继续..."
            return
        fi

        echo -n "输出目录 (留空为当前目录): "
        read output_dir

        if [ -n "$output_dir" ]; then
            mkdir -p "$output_dir"
            cd "$output_dir"
        fi

        echo "开始解码..."
        if uudecode "$encoded_file"; then
            echo "✓ 解码成功"
            echo "生成的文件:"
            ls -l | grep -v "$encoded_file" | head -5
        else
            echo "✗ 解码失败"
        fi

        if [ -n "$output_dir" ]; then
            cd - >/dev/null
        fi

    elif [ "$method" = "2" ]; then
        echo "请粘贴编码内容 (以空行结束):"
        echo ""

        # 创建临时文件
        temp_file=$(mktemp)

        # 读取多行输入,直到空行
        echo "粘贴内容 (输入空行结束):"
        while IFS= read -r line; do
            if [ -z "$line" ]; then
                break
            fi
            echo "$line" >> "$temp_file"
        done

        echo -n "输出目录 (留空为当前目录): "
        read output_dir

        if [ -n "$output_dir" ]; then
            mkdir -p "$output_dir"
            cd "$output_dir"
        fi

        echo "开始解码..."
        if uudecode "$temp_file"; then
            echo "✓ 解码成功"
            echo "生成的文件:"
            ls -l | head -5
        else
            echo "✗ 解码失败"
        fi

        # 清理
        rm -f "$temp_file"
        if [ -n "$output_dir" ]; then
            cd - >/dev/null
        fi
    fi

    read -p "按回车键继续..."
}

# 主循环
while true; do
    show_menu
    read choice

    case $choice in
        1) encode_for_email ;;
        2) decode_from_email ;;
        3)
            echo "批量编码功能开发中..."
            read -p "按回车键继续..."
            ;;
        4)
            echo "批量解码功能开发中..."
            read -p "按回车键继续..."
            ;;
        5)
            echo -n "输入编码文件路径: "
            read info_file
            if [ -f "$info_file" ]; then
                echo "文件信息:"
                file "$info_file"
                echo ""
                echo "前5行内容:"
                head -5 "$info_file"
            fi
            read -p "按回车键继续..."
            ;;
        6)
            echo "编码测试功能开发中..."
            read -p "按回车键继续..."
            ;;
        7)
            echo -n "选择默认编码 (base64/uuencode): "
            read new_encoding
            if [ "$new_encoding" = "base64" ] || [ "$new_encoding" = "uuencode" ]; then
                DEFAULT_ENCODING="$new_encoding"
                echo "默认编码已设置为: $DEFAULT_ENCODING"
            else
                echo "无效的编码类型"
            fi
            read -p "按回车键继续..."
            ;;
        8)
            echo "再见!"
            exit 0
            ;;
        *)
            echo "无效选择"
            read -p "按回车键继续..."
            ;;
    esac
done
脚本3:uuencode/Base64编解码库(Python实现)
#!/bin/bash
# 这是一个bash包装器,调用Python实现更强大的编解码功能

PYTHON_SCRIPT=$(cat << 'EOF'
#!/usr/bin/env python3
"""
uuencode/Base64 编解码工具库
支持多种编码格式和高级功能
"""

import os
import sys
import base64
import binascii
import argparse
from pathlib import Path
from typing import Optional, Tuple

class UUEncoder:
    """uuencode/Base64 编码解码器"""

    @staticmethod
    def uuencode(data: bytes, filename: str = "data", mode: int = 0o644) -> str:
        """
        将二进制数据编码为uuencode格式

        Args:
            data: 二进制数据
            filename: 输出文件名
            mode: 文件权限(八进制)

        Returns:
            uuencode编码的字符串
        """
        # uuencode头部
        header = f"begin {mode:o} {filename}\n"

        # uuencode编码
        encoded_lines = []
        for i in range(0, len(data), 45):
            chunk = data[i:i+45]
            # 每行第一个字符是长度字节(ASCII 32 + 长度)
            line_len = len(chunk)
            if line_len == 0:
                break
            encoded_lines.append(chr(line_len + 32))

            # 编码3字节为一组
            for j in range(0, len(chunk), 3):
                group = chunk[j:j+3]
                # 补齐到3字节
                while len(group) < 3:
                    group += b'\x00'

                # 3字节 = 24位,分为4个6位组
                value = (group[0] << 16) | (group[1] << 8) | group[2]
                for k in range(4):
                    six_bits = (value >> (18 - k*6)) & 0x3F
                    # 6位值加32变成可打印ASCII
                    char_code = six_bits + 32
                    if char_code == 32:  # 空格特殊处理
                        char_code = 96  # 反引号
                    encoded_lines.append(chr(char_code))

            encoded_lines.append('\n')

        # 结束标记
        encoded_lines.append('`\n')
        encoded_lines.append('end\n')

        return header + ''.join(encoded_lines)

    @staticmethod
    def uudecode(encoded_data: str) -> Tuple[Optional[bytes], Optional[str], Optional[int]]:
        """
        解码uuencode数据

        Args:
            encoded_data: uuencode编码的字符串

        Returns:
            (二进制数据, 文件名, 权限模式) 或 (None, None, None) 如果失败
        """
        lines = encoded_data.strip().split('\n')
        if not lines or not lines[0].startswith('begin '):
            return None, None, None

        # 解析头部
        header_parts = lines[0].split()
        if len(header_parts) < 3:
            return None, None, None

        try:
            mode = int(header_parts[1], 8)  # 八进制解析
            filename = header_parts[2]
        except ValueError:
            return None, None, None

        # 解码数据
        decoded = bytearray()
        for line in lines[1:]:
            if line == 'end':
                break
            if not line or line == '`':
                continue

            # 第一个字符是长度
            line_len = ord(line[0]) - 32
            if line_len <= 0:
                continue

            # 解码数据
            data_chars = line[1:]
            for i in range(0, len(data_chars), 4):
                chunk = data_chars[i:i+4]
                if len(chunk) < 4:
                    break

                # 4个字符恢复为3字节
                value = 0
                for j, char in enumerate(chunk):
                    char_code = ord(char)
                    if char_code == 96:  # 反引号转空格
                        char_code = 32
                    six_bits = char_code - 32
                    value = (value << 6) | six_bits

                # 提取3字节
                bytes_decoded = [
                    (value >> 16) & 0xFF,
                    (value >> 8) & 0xFF,
                    value & 0xFF
                ]

                # 添加到结果,根据实际长度截断
                bytes_to_add = min(3, line_len - len(decoded))
                decoded.extend(bytes_decoded[:bytes_to_add])

        return bytes(decoded), filename, mode

    @staticmethod
    def base64_encode(data: bytes, filename: str = "data", mode: int = 0o644) -> str:
        """
        将二进制数据编码为Base64格式(uuencode风格)

        Args:
            data: 二进制数据
            filename: 输出文件名
            mode: 文件权限

        Returns:
            Base64编码的字符串
        """
        # Base64头部
        header = f"begin-base64 {mode:o} {filename}\n"

        # Base64编码
        encoded = base64.b64encode(data).decode('ascii')

        # 每行76字符(符合RFC)
        encoded_lines = []
        for i in range(0, len(encoded), 76):
            encoded_lines.append(encoded[i:i+76])

        # 结束标记
        encoded_lines.append('')
        encoded_lines.append('===')
        encoded_lines.append('')

        return header + '\n'.join(encoded_lines)

    @staticmethod
    def base64_decode(encoded_data: str) -> Tuple[Optional[bytes], Optional[str], Optional[int]]:
        """
        解码Base64数据(uuencode风格)

        Args:
            encoded_data: Base64编码的字符串

        Returns:
            (二进制数据, 文件名, 权限模式) 或 (None, None, None) 如果失败
        """
        lines = encoded_data.strip().split('\n')
        if not lines or not lines[0].startswith('begin-base64 '):
            return None, None, None

        # 解析头部
        header_parts = lines[0].split()
        if len(header_parts) < 3:
            return None, None, None

        try:
            mode = int(header_parts[1], 8)
            filename = header_parts[2]
        except ValueError:
            return None, None, None

        # 提取Base64数据
        base64_data = []
        in_data = False
        for line in lines[1:]:
            if line.startswith('===') or line.startswith('end'):
                break
            if line and not line.isspace():
                base64_data.append(line)

        # 解码
        try:
            decoded = base64.b64decode(''.join(base64_data))
            return decoded, filename, mode
        except (binascii.Error, ValueError):
            return None, None, None

def main():
    parser = argparse.ArgumentParser(description='uuencode/Base64 编解码工具')
    parser.add_argument('action', choices=['encode', 'decode', 'info'],
                       help='操作: encode=编码, decode=解码, info=信息')
    parser.add_argument('-i', '--input', help='输入文件')
    parser.add_argument('-o', '--output', help='输出文件')
    parser.add_argument('-t', '--type', choices=['uuencode', 'base64'], default='base64',
                       help='编码类型 (默认: base64)')
    parser.add_argument('-f', '--filename', help='输出文件名')
    parser.add_argument('-m', '--mode', type=lambda x: int(x, 8), default=0o644,
                       help='文件权限 (八进制, 默认: 644)')

    args = parser.parse_args()
    encoder = UUEncoder()

    if args.action == 'encode':
        # 读取输入文件
        if args.input:
            with open(args.input, 'rb') as f:
                data = f.read()
            default_filename = args.input
        else:
            data = sys.stdin.buffer.read()
            default_filename = 'data'

        # 编码
        if args.type == 'uuencode':
            encoded = encoder.uuencode(data, args.filename or default_filename, args.mode)
        else:  # base64
            encoded = encoder.base64_encode(data, args.filename or default_filename, args.mode)

        # 输出
        if args.output:
            with open(args.output, 'w') as f:
                f.write(encoded)
        else:
            sys.stdout.write(encoded)

    elif args.action == 'decode':
        # 读取编码数据
        if args.input:
            with open(args.input, 'r') as f:
                encoded_data = f.read()
        else:
            encoded_data = sys.stdin.read()

        # 自动检测编码类型
        if encoded_data.startswith('begin-base64 '):
            data, filename, mode = encoder.base64_decode(encoded_data)
        elif encoded_data.startswith('begin '):
            data, filename, mode = encoder.uuencode_decode(encoded_data)
        else:
            # 尝试Base64解码
            try:
                data = base64.b64decode(encoded_data)
                filename = args.filename or 'decoded.bin'
                mode = args.mode
            except binascii.Error:
                print("错误: 无法识别的编码格式", file=sys.stderr)
                sys.exit(1)

        if data is None:
            print("错误: 解码失败", file=sys.stderr)
            sys.exit(1)

        # 输出
        output_file = args.output or filename
        with open(output_file, 'wb') as f:
            f.write(data)

        # 设置权限
        if mode is not None:
            try:
                os.chmod(output_file, mode)
            except OSError:
                pass

        print(f"解码成功: {output_file} (权限: {oct(mode)[2:] if mode else '默认'})")

    elif args.action == 'info':
        if args.input:
            with open(args.input, 'r') as f:
                encoded_data = f.read()
        else:
            encoded_data = sys.stdin.read()

        if encoded_data.startswith('begin-base64 '):
            print("格式: Base64 (uuencode风格)")
            header = encoded_data.split('\n')[0]
            parts = header.split()
            if len(parts) >= 3:
                print(f"文件名: {parts[2]}")
                print(f"权限: {parts[1]}")
        elif encoded_data.startswith('begin '):
            print("格式: uuencode")
            header = encoded_data.split('\n')[0]
            parts = header.split()
            if len(parts) >= 3:
                print(f"文件名: {parts[2]}")
                print(f"权限: {parts[1]}")
        else:
            print("格式: 未知或原始Base64")

        # 估计原始大小
        lines = encoded_data.strip().split('\n')
        data_lines = [l for l in lines if l and not l.startswith('begin') and not l.startswith('end') and l != '`' and not l.startswith('===')]
        if data_lines:
            print(f"数据行数: {len(data_lines)}")
            # 粗略估计
            if encoded_data.startswith('begin '):
                # uuencode: 每行约45字节原始数据
                estimated = len(data_lines) * 45
            else:
                # Base64: 每4字符对应3字节
                total_chars = sum(len(l) for l in data_lines)
                estimated = total_chars * 3 // 4
            print(f"估计原始大小: {estimated} 字节")

if __name__ == '__main__':
    main()
EOF
)

# 检查Python是否可用
if command -v python3 >/dev/null 2>&1; then
    # 执行Python脚本
    python3 -c "$PYTHON_SCRIPT" "$@"
else
    echo "错误: 需要Python 3来运行此工具"
    echo "请安装Python 3或使用系统自带的uuencode/uudecode命令"
    exit 1
fi

注意事项

重要注意事项:
  • 安装需求:uuencode/uudecode通常包含在sharutils包中,需要手动安装
  • 编码膨胀:编码后文件大小增加约33%,不适合大文件传输
  • 安全性:uuencode编码不提供加密,敏感数据应先加密再编码
  • 标准兼容:不同系统的uuencode实现可能有细微差异
  • 行长度限制:uuencode每行最多45字节原始数据,Base64每行最多76字符
  • 二进制安全:uuencode和Base64都是二进制安全的,可编码任何数据
  • 现代替代:对于新项目,建议使用标准Base64而非传统uuencode
  • 权限恢复:uuencode编码包含文件权限,解码时会恢复,但可能需要相应权限

常见问题

A: 主要区别:
对比项 uuencode Base64
历史 Unix传统(1980年代) MIME标准(1990年代)
标准 POSIX标准 RFC 4648(IETF)
字符集 32-95的ASCII字符 A-Z a-z 0-9 + /
行格式 每行60字符,首字符为长度 每行76字符建议
头部 begin 权限 文件名 begin-base64 权限 文件名
空白处理 空格(32)用`(96)表示 无特殊处理
现代使用 遗留系统 Web标准
使用建议:
# 兼容性要求高时使用uuencode
uuencode file.bin file.bin > file.uue

# 现代应用使用Base64
base64 file.bin > file.b64
# 或
uuencode -m file.bin file.bin > file.b64

# Web应用使用标准Base64
# 注意:uuencode -m生成的Base64包含头部,而纯base64命令不包含
# 电子邮件附件通常需要头部信息

# 转换uuencode为Base64
uudecode file.uue
uuencode -m decoded.bin decoded.bin > file.b64

A: 在大多数Linux发行版中,uuencode/uudecode包含在sharutils包中:
  • Debian/Ubuntu: sudo apt update && sudo apt install sharutils
  • RHEL/CentOS: sudo yum install sharutils
  • Fedora: sudo dnf install sharutils
  • Arch Linux: sudo pacman -S sharutils
  • openSUSE: sudo zypper install sharutils
  • macOS: 通常已预装,或通过Homebrew安装
  • Windows: 通过Cygwin、WSL或GNUWin32安装
安装验证:
# 检查是否安装成功
which uuencode
which uudecode

# 查看版本
uuencode --version
uudecode --version

# 测试基本功能
echo "test" > test.txt
uuencode test.txt test.txt > test.uue
uudecode test.uue
cat test.txt
# 输出:test

# 如果没有包管理器,从源码编译
wget https://ftp.gnu.org/gnu/sharutils/sharutils-4.15.2.tar.xz
tar -xf sharutils-4.15.2.tar.xz
cd sharutils-4.15.2
./configure
make
sudo make install

# 替代方案:使用其他Base64工具
# 大多数系统有base64命令
base64 --version
# 或使用openssl
openssl base64 -in file -out file.b64
openssl base64 -d -in file.b64 -out file

# 使用Python(大多数系统已安装)
python3 -m base64 -e file.bin > file.b64
python3 -m base64 -d file.b64 > file.decoded

# 使用Perl
perl -MMIME::Base64 -e 'print encode_base64(join("", <>))' < file.bin > file.b64
perl -MMIME::Base64 -e 'print decode_base64(join("", <>))' < file.b64 > file.decoded

A: 编码文件损坏可能的原因和解决方法:
  • 行结束符问题:Windows/Unix行结束符差异
  • 编码格式错误:错误的编码类型识别
  • 传输损坏:电子邮件或复制过程中损坏
  • 编码错误:原始编码过程出错
故障排除:
# 1. 检查文件格式
file encoded.uue
# 应该显示:ASCII uuencoded text 或类似

# 2. 检查文件头部
head -1 encoded.uue
# 应该以 "begin" 或 "begin-base64" 开头

# 3. 检查文件尾部
tail -2 encoded.uue
# 应该以 "end" 或 "`" 和 "end" 结尾

# 4. 修复行结束符(如果来自Windows)
dos2unix encoded.uue
# 或
tr -d '\r' < encoded.uue > encoded_fixed.uue

# 5. 尝试不同的解码工具
# 使用uuencode
uudecode encoded.uue
# 使用base64
base64 -d encoded.uue > decoded.bin
# 使用openssl
openssl base64 -d -in encoded.uue -out decoded.bin

# 6. 手动修复常见问题
# 删除非ASCII字符
tr -cd '\11\12\15\40-\176' < encoded.uue > cleaned.uue
# 修复可能的空格问题
sed 's/ /`/g' encoded.uue > fixed.uue

# 7. 分步解码
# 提取Base64数据(如果是Base64编码)
sed -n '/^begin-base64/,/^===/p' encoded.uue | sed '1d;$d' > data.b64
base64 -d data.b64 > decoded.bin

# 8. 使用Python进行灵活解码
python3 -c "
import base64, sys
with open('encoded.uue', 'r') as f:
    data = f.read()
if data.startswith('begin-base64'):
    lines = [l for l in data.split('\n')
             if l and not l.startswith('begin') and not l.startswith('===')]
    data = ''.join(lines)
    try:
        decoded = base64.b64decode(data)
        with open('decoded.bin', 'wb') as f:
            f.write(decoded)
        print('解码成功')
    except:
        print('Base64解码失败')
elif data.startswith('begin '):
    print('尝试使用系统uudecode')
    import subprocess
    subprocess.run(['uudecode', 'encoded.uue'])
else:
    print('无法识别的格式')
"

# 9. 验证编码完整性
# 重新编码解码后的文件,比较是否一致
uudecode encoded.uue
uuencode decoded.bin decoded.bin > reencoded.uue
diff encoded.uue reencoded.uue
# 如果没有输出,说明编码一致

# 10. 如果所有方法都失败,尝试从编码中提取文本
strings encoded.uue | head -20
# 或尝试修复为纯Base64
grep -v '^begin' encoded.uue | grep -v '^end' | tr -d '\n' | base64 -d > recovered.bin

相关命令

uudecode

解码uuencode文件:

uudecode file.uue
uudecode -o output.bin file.uue
uudecode -t file.uue
cat file.uue | uudecode
base64

Base64编码解码:

base64 file.bin > file.b64
base64 -d file.b64 > file.bin
base64 -w 0 file.bin  # 不换行
cat file.bin | base64
xxd

十六进制转储:

xxd file.bin
xxd -r hex.txt > file.bin
xxd -l 100 file.bin
xxd -p file.bin  # 纯十六进制
shar

创建自解压shell归档:

shar *.txt > archive.shar
sh archive.shar
shar -c *.txt  # 创建压缩归档
unshar archive.shar