>  기사  >  백엔드 개발  >  Linux 명령의 Python 구현 xxd -i 기능 소개

Linux 명령의 Python 구현 xxd -i 기능 소개

高洛峰
高洛峰원래의
2017-03-07 15:58:162285검색

1. Linux xxd -i 기능

Linux 시스템 xxd 명령은 2진수 또는 16진수 형식을 사용하여 파일 내용을 표시합니다. outfile 매개변수가 지정되지 않으면 결과가 터미널 화면에 표시됩니다. 그렇지 않으면 결과가 outfile로 출력됩니다. 자세한 사용법은 리눅스 명령어 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')를 나타냅니다.

2. xxd -i의 일반적인 용도

기기에 파일 시스템이 없거나 동적 메모리를 지원하지 않는 경우 관리 과정에서 바이너리 파일(예: 부트로더 및 펌웨어) 내용이 C 코드 정적 배열 내에 저장되는 경우가 있습니다. 이때 xxd 명령어를 이용하면 버전 배열을 자동으로 생성할 수 있다. 예는 다음과 같습니다.

1) Linux 명령 xdd를 사용하여 바이너리 파일 VdslBooter.bin을 16진수 파일 DslBooter.txt로 변환합니다.

xxd -i < DslBooter.txt

이 중 '-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-like 기능이 작동합니다.

작성자가 학습 단계에 있기 때문에 코드 내에서 다르게 작성되었지만 동일하거나 유사한 기능을 가진 곳이 많으므로 다른 구문 참조를 제공하는 것을 목표로 삼고 있습니다.

먼저 짧지만 완전한 프로그램을 살펴보십시오(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로 실행하는 경우 생성CArrayName('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)

2.5 이하의 Python 버전은 with...as 구문을 지원하지 않으며 Linux 시스템에서 사용되는 디버깅을 위한 작성자는 Python2.4.3만 설치했습니다. 따라서 Linux 시스템에서 xddi.py를 실행하려면 file = open(....)만 작성할 수 있습니다. 그러나 이를 위해서는 파일 닫기 및 예외 처리가 필요합니다. 자세한 내용은 with...as... 이해를 참조하세요. Python2.5에서 with...as 구문을 사용하는 경우 from __future__ import with_statement를 선언해야 합니다. 예를 들어 platform.python_version()을 통해 Python 버전 번호를 얻을 수 있습니다. :

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 중국어 웹사이트를 주목하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.