GNU/Linux上哪一个高效的可移植shell语句可以将管道字节零填充到字边界?

u4vypkhs  于 2023-02-24  发布在  Shell
关注(0)|答案(2)|浏览(79)

我需要在超出可用存储空间和内存的字节流末尾填充NUL字节,这样输出长度就可以被N整除。

#!/bin/sh
generate_arbitrary_length | paddingN | work_with_padded

N=8192的工作代码:

padding8192(){ dd status=none bs=8192 conv=sync ; }

但是对于较小的N,减少拷贝块大小要慢 * 个数量级 *,这并没有完成:

padding4(){ dd status=none bs=4 conv=sync ; }

复制输入流后,我可以使用wcdd表示计数和填充:

padding4(){ { { tee /dev/fd/3 >&2 ; } 3>&1 | wc -c | { read -r isize ; pad=$(( 4 - isize % 4)) ; [ 0 -lt $pad ] && dd status=none if=/dev/zero bs=$pad count=1 >&2 ; } } 2>&1 ; }

已经快多了。但是很难读懂--谁能说清为什么填充在EOF结束呢?
有更好的办法吗?
虽然我只需要保留存储字节计数模字长所需的状态,但我想不出使用shell内置函数的简单而高性能的实现。使用GNU coreutils/cpio/tar,没有编译器/perl/特性,这将不同于busybox/dash/bash。我还没有提出一个awk的解决方案,因为我未能使它在二进制输入上表现良好(G/s),没有均匀的NL/NULL分隔成行。

db2dz4w8

db2dz4w81#

既然你提到有编译器可用,这里有一个小型的,可移植的C程序。它没有得到任何更快和内存经济。它甚至是可读的大多数人在编程社区。如果没有,你总是可以洒/* Comments! */。:-)

#!/bin/sh
#
# pad.sh - pad input, reading in large blocks from stdin, writing stdout.

# padding $1:padchar $2:alignment $3:blocksize
padding () {
aout="./a$$.out"
cc -x c -o "$aout" - <<EOF
#include <stdio.h>

int main (void) {
  size_t align = $2, nwritten = 0, nread;
  char buffer[$3];
  while ((nread = fread (buffer, 1, sizeof buffer, stdin)) > 0)
    nwritten += fwrite (buffer, 1, nread, stdout);
  if ((nwritten % align) != 0)
    for (align -= nwritten % align; align != 0; --align)
      putchar ($1);
  return 0;
}
EOF
"$aout" && rm "$aout"
}

printf '%s' 123456789 | padding 0       4 16384                  | od -c
printf '%s' abcdefghi | padding "'\n'" 16 BUFSIZ                 | od -c
printf '%s' PAGE_SIZE | padding 65     32 "$(getconf PAGE_SIZE)" | od -c

实际应用:

$ ./pad.sh
0000000    1   2   3   4   5   6   7   8   9  \0  \0  \0
0000014
0000000    a   b   c   d   e   f   g   h   i  \n  \n  \n  \n  \n  \n  \n
0000020
0000000    P   A   G   E   _   S   I   Z   E   A   A   A   A   A   A   A
0000020    A   A   A   A   A   A   A   A   A   A   A   A   A   A   A   A
0000040

如果你担心非POSIX编译器选项-x c,你可以很容易地把C程序写到pad.c并从那里编译它。fwritefreadputchar的高级错误处理留给读者。
注意here-document是如何避免main解析参数的,甚至可以传递PAGE_SIZE这样的字符串,如果stdio默认允许的话。
我刚刚意识到,像这样编译C和漂亮的awk脚本没有太大区别--awk也编译内部程序,然后执行它。还有什么比编译到机器的CPU并运行可执行文件更好的呢?

irlmq6kh

irlmq6kh2#

POSIX要做的事情是使用临时文件。

padding() (
   tmpf=$(mktemp) &&
   trap 'rm "$tmpf"' EXIT &&
   tee "$tmpf" &&
   isize=$(wc -c <"$tmpf") &&
   pad=$(( $1 - isize % $1 )) &&
   if [ "$pad" -ne 0 ]; then
       dd status=none if=/dev/zero bs="$pad" count=1
   fi
)

相关问题