部分单步执行 25k 字符串的最佳方法?

部分单步执行 25k 字符串的最佳方法?

我有 25k 个字符。

我希望编写一个脚本来打印(printf据我所知是最便携的)任意数量的字符;按顺序逐步执行它们。

说:

命令号

在哪里数字可以是 1-25000 之间的任意值,并获得该输出。

我不想将数据放在单独的文件中(最简单的解决方案?),并且我更愿意仅使用 POSIX shell 命令(以使脚本尽可能可移植:我知道 awk 或 perl 可以破解这个简单地出来)。

我应该将这些数据存储在变量中吗?或者printf通过cut命令 ( cut -c -$1) 运行我的完整程序?或者还有另一种(更好的?)解决方案吗?为什么我会选择一个选项而不是另一个选项?

我还忽略了哪些其他问题/警告?

答案1

你考虑过这个dd命令吗?它允许您跳过任意数量的字节,然后输出任意数量的字节。

dd if=infilename bs=1 skip=sk count=ct 2>/dev/null

dd,输入文件名,块大小1,先跳过斯克输入文件的字节,然后复制CT字节到标准输出(或使用 指定文件 of=name)。重定向错误消息以避免通常在末尾打印的状态消息。

答案2

出于可移植性和可靠性的原因,将大数据存储为变量可能不是一个好主意。对于非awkPOSIX 解决方案以便更容易移植,请使用sed.

解释

对于大量数据,避免存储为变量。虽然Bash 本身没有限制,但操作系统可能会这样做

假设您说“它可以在我的操作系统上运行”。但,

  • 不同的操作系统会有不同的限制
  • 因此,如果您想最大限度地提高可移植性,为什么要冒着脚本在一个操作系统上运行而在另一个操作系统上崩溃的风险,仅仅因为它们有不同的限制呢?
  • 因此,通过首先不存储在变量中来避免此问题

那么我们将它存储在一个文件中。具体来说,将字符串分解为一个字符(或您希望的任何最小单位),并分成单独的行。

然后,使用sed

另外考虑一下使用文件的代码维护优势。更新文件中存储的行可能比在脚本中导航代码更容易。

例子

拥有数据,每行一个字符(或任何您想要“逐步执行”的最小单位),例如在文件中data.lst

a
b
c
d
e

让你script.sh包含:

#!/bin/bash

stop_number="$1"

sed -n "1,${stop_number}p" data.lst

因此,您在命令提示符下测试它并看到:

$ ./script.sh 3
a
b
c
  • 它用于sed打印1一直到由 指定的数字的行$stop_number。为了清楚起见,我们写出来$stop_number而不是直接写$1
  • $stop_number当然是通过位置参数获得的$1,就是你想要输入的任意数字
  • 所以它成功地按照data.lst它出现在的序列中的 3 个字符data.lst
  • 此时如果您输入的数字大于实际行数,则只会显示所有行。
  • 目前data.lst只是在同一个目录中,script.sh但如果你不想要这样,如果你实际上在其他地方有它,那么~/some/dir/data.lst你只需要调整它来说~/some/dir/data.lst

因此,一旦获得实际数据,data.lst您就可以自己测试此脚本。

答案3

借用汤姆的一点:

#!/bin/sh
skp(){  dd bs="$1" skip=1 count=0; }    # direct seek to target
rd (){  dd bs="$1" skip=0 count=1; }    # single read at target
tail=$(sed -ne'/^don/{=;q;}' <"$0")     # skip script by line#
while   [ 1 -gt "$#" ] && exit          # exit when args exhausted
        exec <&- <"$0" || exit          # exec <"$0" each iteration
do      head -n "$tail" >&3             # only consider the tail
        case ${2+$1}  in                # test args
        (*[1-9]*|-*[!0]*)               # skp() when ${2++} && $1 != 0
          skp "$1";esac 2>&3            # send stderr to dev/null
          rd  "${2-$1}" 2>&3            # else just rd() from head of offset
        echo; shift ${2+"2"}            # append a newline and shift args away
done    3>/dev/null                     # put your data below this

不要将其放在变量中 - 将其放入您的文件中。一个 25k 的变量对于 shell 来说处理起来并不有趣,并且你的文件可以是寻求的在一个单一的,几乎原子行动。因此,如果您想打印字节 23843 - 24843,您可以执行类似上面的操作,然后使用以下命令调用它:

myscript 23843 1000

...首先 ahead将从文件描述符中的共享标准中删除脚本的所有行,以便将偏移量精确设置为 25k 字符串的头部,然后第一个dd寻找该偏移量〜23k in,第二个dd将读出它。这是最简单的方法。 shell 是为了逐个字符地读取而设计的 -read例如,一个典型的 shell 的内置函数会执行以下操作:一字节 read()循环直到找到换行符 - 并且直到找到换行符为止才停止。dd将为每个read参数对执行一个操作。

我是这样测试的:

# after a copy to my clipboard
ddscr(){ sh /tmp/ddscr.sh "$@"; }
{ xsel; man man; } > /tmp/ddscr.sh
{ echo show the size; ls -l /tmp/ddscr*
  echo read from the top; ddscr 80
  echo from the middle;   ddscr 15k 160
  echo from the tail;     ddscr 64k | tail -n5
}

show the size
-rw-r--r-- 1 mikeserv mikeserv 37564 Dec 13 11:27 /tmp/ddscr.sh
read from the top
MAN(1)                           Manual pager utils                          MAN
from the middle
lso use manconv(1) directly.
              However, this option allows you to convert several manual pages to  a
              single  encoding  without  having
from the tail
       31st  March  2001  -  present day: Colin Watson <[email protected]> is now
       developing and maintaining man-db.

2.7.5                                2015-11-06                              MAN(1)

...和...

ddscr 10k 10 20k 10 10250 10

       fi
is  option
le.   If

答案4

如果你确实想将程序和数据包装在同一个文件中,最好的方法是使用perl.我不知道为什么你认为它是不可移植的:它是你遇到的任何 Unix 发行版的标准(包括 Linux 和 OS X);你在 Windows 上找不到它的标准,但在那里也找不到bash

#!/usr/bin/env perl
print substr(<DATA>, $ARGV[0], $ARGV[1]), "\n";

__DATA__
Just add all your text after 
the __DATA__ line... no fuss, no quoting, 
no tricks

例如,假设您将其命名为selective_print并且想要打印从10开始的30个字符:

% selective_print 10 30

相关内容