6

我是脚本新手,但我在 C# 和 Java 等语言方面拥有丰富的编程经验。

我有一个包含二进制数据的文件。我想编写一个 Bash 脚本来读取该文件中包含的年、月和日,以便我可以根据记录的日期将关联的 MOD 文件分类到文件夹中。我无法找到一种读取二进制数据并在 bash 脚本中解析它的方法。有没有办法做到这一点?

4

4 回答 4

8

您可以为此使用 od (加上 head 和 awk 进行一些后期处理)。获取年份:

year=$(od -t x2 --skip-bytes=6 --read-bytes=2 file.moi | head -1 | awk '{print $2}')

当月:

month=$(od -t x1 --skip-bytes=8 --read-bytes=1 file.moi | head -1 | awk '{print $2}')

那天:

day=$(od -t x1 --skip-bytes=9 --read-bytes=1 file.moi | head -1 | awk '{print $2}')
于 2009-12-29T00:31:29.320 回答
2

我建议为此使用python。

但是,如果您坚持使用 bash,我会尝试使用sed二进制模式(从未尝试过)或dd用于提取特定字节然后转换它们。

于 2009-12-29T00:29:31.563 回答
1

如果这对你来说不是太硬核,我建议编译以下 C 语言程序:

#include <stdio.h>
#include <inttypes.h>

typedef union {
  char array[sizeof(int32_t)];
  int32_t val;
} int32_u;

typedef union {
  char array[sizeof(uint32_t)];
  uint32_t val;
} uint32_u;

typedef union {
  char array[sizeof(uint64_t)];
  uint64_t val;
} uint64_u;

typedef union {
  char array[sizeof(int64_t)];
  int64_t val;
} int64_u;

int swap(char* mem, int size) {
  if (size & 1 != 0)
    return -1;
  int i;
  for (i = 0; i < size / 2; i++) {
    char tmp = mem[i];
    mem[i] = mem[size - i - 1];
    mem[size - i - 1] = tmp;
  }
  return 0;
}

int sys_big_endian() {
    int x = 1;
    return !(*(char*)&x);
}

int main(int argc, char** argv) {
  char* file_name = NULL;
  int offset = 0;
  char* type = "int32";
  int big_endian = 0;

  int i;
  for(i = 1; i < argc; i++) {
    if(!strncmp("-o", argv[i], 2)) {
      ++i;
      sscanf(argv[i], "%d", &offset);
    } else if(!strncmp("-t", argv[i], 2)) {
      ++i;
      type = argv[i];
    } else if(!strncmp("-e", argv[i], 2)) {
      ++i;
      big_endian = !strncmp("big", argv[i], 3);
    } else {
      file_name = argv[i];
      break;
    }
  }

  if (i < argc - 1) {
    fprintf(stderr, "Ignoring extra arguments: ");
    ++i;
    for (; i < argc; i++) {
      fprintf(stderr, "%s ", argv[i]);
    }
    fprintf(stderr, "\n");
  }

  if (file_name == NULL) {
    fprintf(stderr, "Syntax: readint [-o offset] [-t type] [-e endian] <filename>\n"
      "Where:\n"
      "  type      'uint32', 'uint64', 'int32' (default), 'int64'.\n"
      "  endian    'big' or 'little' (default).\n"
      "  offset    offset in a file from where the read will happen, default is 0.\n"
    );
    return -1;
  }

  FILE* fp = fopen(file_name, "rb");

  if (fp == NULL) {
    fprintf(stderr, "Could not open the file: %s\n", file_name);
    return -1;
  }

  fseek(fp, offset, SEEK_SET);

  if (!strncmp("uint32", type, 6)) {
    uint32_u u;
    fread(u.array, sizeof(u.array), 1, fp);
    if (big_endian ^ sys_big_endian())
      swap(u.array, sizeof(u.array));
    printf("%u\n", u.val);
  } else if (!strncmp("int32", type, 5)) {
    int32_u u;
    fread(u.array, sizeof(u.array), 1, fp);
    if (big_endian ^ sys_big_endian())
      swap(u.array, sizeof(u.array));
    printf("%d\n", u.val);
  } else if (!strncmp("uint64", type, 6)) {
    uint64_u u;
    fread(u.array, sizeof(u.array), 1, fp);
    if (big_endian ^ sys_big_endian())
      swap(u.array, sizeof(u.array));
    printf("%"PRIu64"\n", u.val);
  } else if (!strncmp("int64", type, 5)) {
    int64_u u;
    fread(u.array, sizeof(u.array), 1, fp);
    if (big_endian ^ sys_big_endian())
      swap(u.array, sizeof(u.array));
    printf("%"PRId64"\n", u.val);
  } else {
    printf("Unknown type: %s\n", type);
  }

  fclose(fp); 
  return 0;
}

然后这样做:

gcc -o readint readint.c
sudo mv readint /usr/local/bin

现在您有了一个名为“readint”的便捷工具,其语法如下:

readint [-o offset] [-t int32|uint32|int64|uint64 ] [-e little|big ] <filename>
于 2015-11-26T01:07:10.913 回答
0

您可以在网上搜索模块来解释 MOI 文件(Perl 或 Python)。否则,我真的不认为您可以像从二进制文件中那样获取日期,因为如果您查看内部,它实际上是“垃圾”,因为它是二进制文件。尽管您也可以尝试使用 strings 命令查看是否有与日期匹配的清晰字符串

于 2009-12-29T00:35:07.717 回答