ホームページ > バックエンド開発 > Python チュートリアル > Linuxコマンドxxd -iの機能紹介をPythonで実装

Linuxコマンドxxd -iの機能紹介をPythonで実装

高洛峰
リリース: 2017-03-07 15:58:16
オリジナル
2414 人が閲覧しました

1. Linux xxd -i 関数

Linux システムの xxd コマンドは、ファイルの内容をバイナリまたは 16 進形式で表示します。 outfile パラメーターが指定されていない場合、結果は端末画面に表示されます。それ以外の場合、結果は outfile に出力されます。詳しい使い方はlinuxコマンドxxdを参照してください。

この記事では主に xxd コマンドの -i オプションに焦点を当てます。このオプションを使用して、inputfile という名前の C 言語配列定義を出力します。たとえば、 echo 12345 > test および xxd -i test コマンドを実行すると、出力は次のようになります:

unsigned char test[] = {
0x31, 0x32, 0x33, 0x34, 0x35, 0x0a
};
unsigned int test_len = 6;
ログイン後にコピー

表示され、配列名は入力ファイル名になります (サフィックスがある場合は、ドットが置き換えられます)アンダースコア付き)。 0x0a は改行文字 LF、つまり「n」を表すことに注意してください。

II. xxd -i

の一般的な使用法 デバイスにファイル システムがない場合、または動的メモリ管理をサポートしていない場合、バイナリ ファイル (ブートローダーやファームウェアなど) の内容が保存されることがあります。 C コードの静的配列。現時点では、xxd コマンドを使用してバージョン配列を自動的に生成できます。例:

1) Linux コマンド xdd を使用して、バイナリ ファイル VdslBooter.bin を 16 進ファイル DslBooter.txt に変換します。

xxd -i < VdslBooter.bin > ここで、 '- i ' オプションは、出力が C インクルード ファイル スタイル (配列モード) であることを示します。リダイレクト記号「<」は、VdslBooter.bin ファイルの内容を標準入力にリダイレクトします。このプロセスにより、配列宣言と長さ変数の定義が削除され、出力には 16 進値のみが含まれます。

2) C コードのソース ファイルで対応する静的配列を定義します。

static const uint8 bootImageArray[] = {
#include " ../../DslBooter.txt"
};
TargetImage bootImage = {
(uint8 *) bootImageArray,
sizeof(bootImageArray) / sizeof(bootImageArray[0])
};
ログイン後にコピー

ソース コードをコンパイルすると、DslBooter.txt ファイルの内容は上記の配列に自動的に展開されます。 #include 前処理ディレクティブをうまく使用すると、配列の内容を手動でコピーする手間を回避できます。

3. xxd -i のような関数の Python 実装


このセクションでは、Python2.7 言語を使用して xxd -i のような関数を実装します。

作成者は学習段階にあるため、コードの記述が異なる箇所が多数ありますが、同じまたは類似した機能を備えており、異なる構文のリファレンスを提供することを目的としています。

まず、短いですが完全なプログラム (xddi.py として保存) をご覧ください:

#!/usr/bin/python
#coding=utf-8
#判断是否C语言关键字
CKeywords = ("auto", "break", "case", "char", "const", "continue", "default",
"do","double","else","enum","extern","float","for",
"goto","if","int","long","register","return","short",
"signed","static","sizeof","struct","switch","typedef","union",
"unsigned","void","volatile","while", "_Bool") #_Bool为C99新关键字
def IsCKeywords(name):
for x in CKeywords:
if cmp(x, name) == 0:
return True
return False
if __name__ == &#39;__main__&#39;:
print IsCKeywords(&#39;const&#39;)
#Xxdi()
ログイン後にコピー

このコードは、指定された文字列が C 言語のキーワードであるかどうかを判断します。 Windows システムの cmd コマンド プロンプトで「E:PyTest>python xxdi.py」と入力すると、実行結果は True になります。

次のコード スニペットでは、先頭のスクリプトとエンコーディングの宣言と、最後の「main」セクションが省略されています。

C 配列を生成する前に、配列名が有効であることを確認してください。 C 言語の識別子は文字、数字、アンダースコアのみで構成でき、数字で始めることはできません。また、キーワードを識別子として使用することはできません。不正な文字はすべて処理する必要があります。ルールについてはコード コメントを参照してください:

import re
def GenerateCArrayName(inFile):
#字母数字下划线以外的字符均转为下划线
#&#39;int $=5;&#39;的定义在Gcc 4.1.2可编译通过,但此处仍视为非法标识符
inFile = re.sub(&#39;[^0-9a-zA-Z\_]&#39;, &#39;_&#39;, inFile) #&#39;_&#39;改为&#39;&#39;可剔除非法字符
#数字开头加双下划线
if inFile[0].isdigit() == True:
inFile = &#39;__&#39; + inFile
#若输入文件名为C语言关键字,则将其大写并加下划线后缀作为数组名
#不能仅仅大写或加下划线前,否则易于用户自定义名冲突
if IsCKeywords(inFile) is True:
inFile = &#39;%s_&#39; %inFile.upper()
return inFile
ログイン後にコピー

print GenerateCArrayName('1a$if1#1_4.txt') で実行すると、入力パラメータ文字列が変換されます。 __1a_if1_1_4_txt に。同様に、_Bool は _BOOL_ に変換されます。

Linux コマンド スタイルを可能な限りシミュレートするには、コマンド ライン オプションとパラメーターを指定する必要があります。解析モジュールは、optionparser を使用します。その使用法の詳細については、「Python コマンド ライン解析」を参照してください。 xxd -i-like 関数のコマンドライン実装は次のとおりです:

#def ParseOption(base, cols, strip, inFile, outFile):
def ParseOption(base = 16, cols = 12, strip = False, inFile = &#39;&#39;, outFile = None):
from optparse import OptionParser
custUsage = &#39;\n xxdi(.py) [options] inFile [outFile]&#39;
parser = OptionParser(usage=custUsage)
parser.add_option(&#39;-b&#39;, &#39;--base&#39;, dest=&#39;base&#39;,
help=&#39;represent values according to BASE(default:16)&#39;)
parser.add_option(&#39;-c&#39;, &#39;--column&#39;, dest=&#39;col&#39;,
help=&#39;COL octets per line(default:12)&#39;)
parser.add_option(&#39;-s&#39;, &#39;--strip&#39;, action=&#39;store_true&#39;, dest=&#39;strip&#39;,
help=&#39;only output C array elements&#39;)
(options, args) = parser.parse_args()
if options.base is not None:
base = int(options.base)
if options.col is not None:
cols = int(options.col)
if options.strip is not None:
strip = True
if len(args) == 0:
print &#39;No argument, at least one(inFile)!\nUsage:%s&#39; %custUsage
if len(args) >= 1:
inFile = args[0]
if len(args) >= 2:
outFile = args[1]
return ([base, cols, strip], [inFile, outFile])
ログイン後にコピー

コメントアウトされた def ParseOption(...) は、もともと次の方法で呼び出されていました:

base = 16; cols = 12; strip = False; inFile = &#39;&#39;; outFile = &#39;&#39;
([base, cols, strip], [inFile, outFile]) = ParseOption(base,
cols, strip, inFile, outFile)
ログイン後にコピー

同時にbase、cols、strip、その他のパラメータ値を変更することです。ただし、この書き方は非常に面倒なので、呼び出すときは、ParseOption() を記述するだけで済みます。読者がより良い書き方を知っている場合は、お気軽に教えてください。

-h オプションを使用して、Linux スタイルに非常に近いコマンド プロンプトを呼び出します:

E:\PyTest>python xxdi.py -h
Usage:
xxdi(.py) [options] inFile [outFile]
Options:
-h, --help show this help message and exit
-b BASE, --base=BASE represent values according to BASE(default:16)
-c COL, --column=COL COL octets per line(default:12)
-s, --strip only output C array elements
ログイン後にコピー

上記の演習に基づいて、この記事のハイライトを完了してください:

def Xxdi():
#解析命令行选项及参数
([base, cols, strip], [inFile, outFile]) = ParseOption()
import os
if os.path.isfile(inFile) is False:
print &#39;&#39;&#39;&#39;%s&#39; is not a file!&#39;&#39;&#39; %inFile
return
with open(inFile, &#39;rb&#39;) as file: #必须以&#39;b&#39;模式访问二进制文件
#file = open(inFile, &#39;rb&#39;) #Python2.5以下版本不支持with...as语法
#if True:
#不用for line in file或readline(s),以免遇&#39;0x0a&#39;换行
content = file.read()

#将文件内容"打散"为字节数组
if base is 16: #Hexadecimal
content = map(lambda x: hex(ord(x)), content)
elif base is 10: #Decimal
content = map(lambda x: str(ord(x)), content)
elif base is 8: #Octal
content = map(lambda x: oct(ord(x)), content)
else:
print &#39;[%s]: Invalid base or radix for C language!&#39; %base
return
#构造数组定义头及长度变量
cArrayName = GenerateCArrayName(inFile)
if strip is False:
cArrayHeader = &#39;unsigned char %s[] = {&#39; %cArrayName
else:
cArrayHeader = &#39;&#39;
cArrayTailer = &#39;};\nunsigned int %s_len = %d;&#39; %(cArrayName, len(content))
if strip is True: cArrayTailer = &#39;&#39;
#print会在每行输出后自动换行
if outFile is None:
print cArrayHeader
for i in range(0, len(content), cols):
line = &#39;, &#39;.join(content[i:i+cols])
print &#39; &#39; + line + &#39;,&#39;
print cArrayTailer
return
with open(outFile, &#39;w&#39;) as file:
#file = open(outFile, &#39;w&#39;) #Python2.5以下版本不支持with...as语法
#if True:
file.write(cArrayHeader + &#39;\n&#39;)
for i in range(0, len(content), cols):
line = reduce(lambda x,y: &#39;, &#39;.join([x,y]), content[i:i+cols])
file.write(&#39; %s,\n&#39; %line)
file.flush()
file.write(cArrayTailer)
ログイン後にコピー

Python 2.5 より前のバージョンは with..as 構文をサポートしておらず、作者がデバッグに使用している Linux システムには Python2.4.3 のみがインストールされています。したがって、Linux システムで xddi.py を実行するには、 file = open(.... と記述するだけです。ただし、これには、ファイルのクローズと例外の処理が必要です。詳細については、「with...as... の理解」を参照してください。 Python の構文。5 で with...as 構文を使用する場合は、__future__ import with_statement を宣言する必要があります。例:

import platform
#判断Python是否为major.minor及以上版本
def IsForwardPyVersion(major, minor):
#python_version()返回&#39;major.minor.patchlevel&#39;,如&#39;2.7.11&#39;
ver = platform.python_version().split(&#39;.&#39;)
if int(ver[0]) >= major and int(ver[1]) >= minor:
return True
return False
ログイン後にコピー

Windows および Linux システムで二重検証を行った後、Xddi () は基本的に期待どおりに動作します。123456789ABCDEF.txt ファイル (内容は「123456789ABCDEF」) を例にとると、テスト結果は次のようになります。
E:\PyTest>python xxdi.py -c 5 -b 2 -s 123456789ABCDEF.txt
[2]: Invalid base or radix for C language!
E:\Pytest>python xxdi.py -c 5 -b 10 -s 123456789ABCDEF.txt

49, 50, 51, 52, 53,
54, 55, 56, 57, 65,
66, 67, 68, 69, 70,
E:\PyTest>python xxdi.py -c 5 -b 10 123456789ABCDEF.txt
unsigned char __123456789ABCDEF_txt[] = {
49, 50, 51, 52, 53,
54, 55, 56, 57, 65,
66, 67, 68, 69, 70,
};
unsigned int __123456789ABCDEF_txt_len = 15;
E:\PyTest>python xxdi.py -c 5 -b 8 123456789ABCDEF.txt
unsigned char __123456789ABCDEF_txt[] = {
061, 062, 063, 064, 065,
066, 067, 070, 071, 0101,
0102, 0103, 0104, 0105, 0106,
};
unsigned int __123456789ABCDEF_txt_len = 15;
E:\PyTest>python xxdi.py 123456789ABCDEF.txt
unsigned char __123456789ABCDEF_txt[] = {
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43,
0x44, 0x45, 0x46,
};
unsigned int __123456789ABCDEF_txt_len = 15;
ログイン後にコピー

例として、少し大きいセカンダリ ファイルを使用して、python xxdi.py を実行します。 VdslBooter.bin booter.c の後に、booter.c ファイルの内容は次のようになります (最初と最後の部分は切り詰められています):

unsigned char VdslBooter_bin[] = {
0xff, 0x31, 0x0, 0xb, 0xff, 0x3, 0x1f, 0x5a, 0x0, 0x0, 0x0, 0x0,
//... ... ... ...
0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
unsigned int VdslBooter_bin_len = 53588;
ログイン後にコピー

要約すると、作者が実装した xxdi モジュールは Linux xxd -i の機能に非常に近く、それぞれに独自の長所と短所があることがわかります。 xxdi の利点は、配列名の有効性をより完全に検証できること (キーワード チェック)、配列の内容の表現が豊富であること (8 進数と 10 進数)、欠点はリダイレクトと値の幅をサポートしていないことです。固定ではありません (0xb や 0xff など)。もちろん、これらの欠点を解消するのは難しいことではありません。たとえば、出力ビット幅を制御するには、hex(val) の代わりに '0x%02x'%val を使用します。ただし、追加の改善によりコードの複雑さは必然的に増加し、その結果、労力が半分で済む可能性があります。

上記は編集者が紹介したLinuxコマンドxxd -i関数のPython実装です。皆さんの参考になれば幸いです。

Linux コマンド xxd -i 関数の Python 実装の紹介に関するその他の関連記事については、PHP 中国語 Web サイトに注目してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート