0

我在 D 中编写了一个小实用程序,用于将 find -print0 的输出转换为 printf %b 格式。这样的实用程序已经存在(来自http://www.dwheeler.com/essays/filenames-in-shell.html的 nul2pfb ),但是链接已失效,我找不到该程序,所以我决定自己实现它D. 我正在使用以下 zsh 命令进行测试:

diff <(find) <(for i in "$(find -print0 | dd | char2code)"; do printf '%b\n' "$i"; done)

预期的输出为空,但我发现某些包含连字符的文件名处理错误。

我的源代码是这样的:

import std.stdio;
import std.conv;
import std.ascii;
import std.c.stdlib;

void main()
  {
    foreach (ubyte[] mybuff; chunks(stdin, 4096)) {
    encodeline (mybuff);
  }
}

@safe void encodeline (ubyte[] mybuff) {
  // char[] outstring;
  foreach (ubyte i; mybuff) {
    char b = to!char(i);
    switch (i) {
      case 'a': .. case 'z':
      case 'A': .. case 'Z':
      case '0': .. case '9':
      case '/':
      case '.':
      case '_':
      case ':': writeChar(b); break;
      default: writeOctal(b); break;
      case 0: writeChar ('\n'); break;
      case '\\': writeString(`\\`); break;
      case '\t': writeString(`\t`); break;
      case '\n': writeString(`\n`); break;
      case '\r': writeString(`\r`); break;
      case '\f': writeString(`\f`); break;
      case '\v': writeString(`\v`); break;
      case '\a': writeString(`\a`); break;
      case '\b': writeString(`\b`); break;
    }
  }
  // writeString (outstring);
}

@trusted void writeString (string a)
{
  write (a);
}

@trusted void writeOctal (int a)
{
  try
  {
    writef ("\\%.#o", a); // leading 0 needed for for zsh printf '%b'
  }
  catch (std.format.FormatException b)
  {
    write ("Format exception in function writeOctal");
    throw b;
  }
}

@trusted void writeChar (char a)
{
  try
  {
    write (a);
  }
  catch (std.format.FormatException b)
  {
    write ("Format exception in function writeChar");
    throw b;
  }
}

@trusted void writeNewline ()
{
  writeln;
}

这是 diff 输出的一部分:

Correct filenames:
./.ibam/profile-004-battery
./.ibam/profile-034-charge
./.ibam/profile-054-charge
./.ibam/profile-045-battery
(a bunch of lines skipped)
---
Wrong filenames:
./.ibam/profileh04-battery
./.ibam/profileh34-charge
./.ibam/profileh54-charge
./.ibam/profileh45-battery

似乎 -0 正在被 h 取代。

更新:在 writef 的转换说明符中将 .# 替换为 .4 解决了问题,但我仍然看到通过 /proc 搜索的输出存在差异。但是,当我这样做时也是如此(以root身份)

diff <(find / print) <(find / print) > (myhomedirectory)/log.txt

所以我选择忽略它。

4

1 回答 1

1

事实证明,问题在于,每当一个八进制转义序列后跟一个介于 0 和 7 之间的数字,并且原始转义序列还不是 4 个八进制数字长时,该数字就成为八进制转义序列的一部分,导致 printf %b 产生不正确的输出。这已通过将 D 程序中的转换说明符中的 # 替换为 .4 来解决。

这是更正后的源代码,删除了一些不需要的功能:

import std.stdio;
import std.conv;
import std.ascii;
import std.c.stdlib;

void main()
  {
    foreach (ubyte[] mybuff; chunks(stdin, 4096)) {
    encodeline (mybuff);
  }
}

@safe void encodeline (ubyte[] mybuff) {
  // char[] outstring;
  foreach (ubyte i; mybuff) {
    char b = to!char(i);
    switch (i) {
      case 'a': .. case 'z':
      case 'A': .. case 'Z':
      case '0': .. case '9':
      case '/':
      case '.':
      case '_':
      case ':': writeChar(b); break;
      default: writeOctal(b); break;
      case 0: writeChar ('\n'); break;
      case '\\': writeString(`\\`); break;
      case '\t': writeString(`\t`); break;
      case '\n': writeString(`\n`); break;
      case '\r': writeString(`\r`); break;
      case '\f': writeString(`\f`); break;
      case '\v': writeString(`\v`); break;
      case '\a': writeString(`\a`); break;
      case '\b': writeString(`\b`); break;
    }
  }
}

@trusted void writeString (string a)
{
  write (a);
}

@trusted void writeOctal (int a)
{
  writef ("\\%.4o", a); // leading 0 needed for for zsh printf '%b'
}

@trusted void writeChar (char a)
{
  write (a);
}
于 2013-10-15T15:52:26.027 回答