【问题标题】:How to repeat a dash (hyphen) in shell如何在 shell 中重复破折号(连字符)
【发布时间】:2016-08-09 23:16:31
【问题描述】:

如何在 shell 中重复字符 - n 次?我已阅读并尝试过this,但这不适用于-。它抛出错误invalid option。以下是我使用的确切命令:

printf '-%.0s' {1..100}

原贴:printf '-%0.s' {1..100}

我还尝试通过放置\ 来转义-,但在这种情况下它会重复\- n 次。

【问题讨论】:

  • 标题写着“dash”,但它被标记为“bash”。你指的是哪一个?
  • @Kusalananda:OP 指的是短划线字符-,ASCII / Unicode 代码点0x2d)。 (严格来说,角色的名字是连字符,但实际上这两个名字可以互换使用。)
  • @mklement0 啊。我的严重误解。我最近读了太多关于语言严重受损的问题......

标签: linux bash shell repeat


【解决方案1】:

这会引发错误:

$ printf '-%.0s' {1..100}; echo ""
bash: printf: -%: invalid option
printf: usage: printf [-v var] format [arguments]

这在bash 下工作正常:

$ printf -- '-%.0s' {1..100}; echo ""
----------------------------------------------------------------------------------------------------

对于其他shell,试试:

printf -- '-%.0s' $(seq 100); echo ""

问题是printf 期望- 启动一个选项。在这种情况下,在 Unix/POSIX 实用程序中很常见,--printf 发出信号,表示没有更多选择。

【讨论】:

  • +1。这个解决方案很容易记住,因为它不是特定于 printf 的;许多实用程序支持 -- 作为一个特殊标志,意思是“在此之后没有更多选项”。
  • 不仅仅是 Linux 实用程序,它还是一个 POSIX 指定的实用程序约定。请参阅pubs.opengroup.org/onlinepubs/9699919799/basedefs/…中的准则 10
  • @CharlesDuffy 非常好。答案已更新以提及 POSIX。我检查了POSIX printf,因为它不接受任何选项,所以没有提到接受--
  • 确实如此。也就是说,POSIX 的实用程序指南部分表明与指南的偏差将在实用程序的单个文档的选项部分中描述。
【解决方案2】:

John1024's helpful answer 提供了一个通用解决方案,该解决方案展示了如何为所有类 POSIX 实用程序的操作数消除选项的歧义。

就目前的情况而言,最简单的解决方案是(不仅适用于bash,还适用于kshzsh):

printf '%.0s-' {1..100}

%.0s 放在之前 - 避免了初始- 被误认为是选项的问题。

略微优化:[1]

printf '%.s-' {1..100}

[1] %.0s 实际上是最便携的形式(要完全便携,还必须避免大括号扩展,{...} )。
%.s,等效的速记形式,bashkshdash 支持,但 @ 987654340@ - 即使是 equally POSIX-compliant :“精度 [.] 之后的部分应采用 ('.') 后跟十进制数字字符串的形式;a空数字字符串被视为零。”

作为旁注:该问题最初包含引发争论的良性(在 Bash 中)错字:%0.s 而不是%.0s%0.s 应该 实际上与%.0s 相同,就此而言,与%.s%0.0s 相同(都有效地请求:打印一个用零长度字符串填充的[最小零宽度] 字段) ,但实际上不是zsh %0.s(同样,由于 .s 部分)。
类似地,从 GNU coreutils v8.24 开始,GNU printf external-utility 实现 (/usr/bin/printf) 报告一个 error %0.s,因为它通常不接受 0s 的字段宽度:invalid conversion specification - 这对于不提供 printf 作为 内置 的鲜为人知的 shell 很重要时间>。请注意,BSD/OSX 实现没有这个问题。
zsh (%.s 的行为和 GNU /usr/bin/printf%0s 的行为都是与 POSIX 规范的偏差,闻起来像错误。
This question 询问 @ 987654362@ 对%.s 的行为,该错误已得到确认,reported as fixed 通过post-v5.2 commit post-v5.2 commit 在撰写本文时尚未发布。

【讨论】:

  • 如果,正如您评论的那样,zsh 和 GNU printf(至少)在您写这篇文章的时候失败了(在您写这篇文章的时候)%0.s 和/或%.s,那么这两个选项都不可移植并且您应该(如果您以负责任的方式行事)不推荐他们。
  • @BinaryZebra:我的回答中没有这样的建议。 is 建议使用%.0s 作为当前最便携的形式,if 需要可移植性(这个问题是附带的,因为如前所述,all 格式字符串变体在bash 中工作正常。除此之外,还分析了实际实现中的不同行为以及它们与 POSIX 规范的关系。
  • @mklement0 声明 %0.s should effectively be the same as %.0s 是错误的。您最初的错误(检查编辑 1)是建议 %0.s 是解决方案。你从来没有说过那是错误的。现在就做。
  • @BinaryZebra:说声明是错误的而不解释为什么是没有帮助的。我的回答解释了为什么它们应该是相同的,所以如果你在那个推理中发现错误,请告诉我们。由于解释的等效性,%0.sbashkshdash 中工作得很好;由于(不相关的)错误,它在 zsh /usr/bin/printf 中不起作用。但是,由于%0.s 不是解决方案意图的良好表达(并且最初是 OP 的一个错字,早已更正)并且 实际上 限制了可移植性,所以我切换到 %.0s(并且仅在脚注中讨论%0.s)。
【解决方案3】:

使用for 循环和数字范围:

for i in {1..10}; 
    do echo "-"; 
done

或单行:

for i in {1..10}; 
    do echo -n "-"; 
done

输出----------

编辑:这是在您的printf 编辑之前。

【讨论】:

  • echo -n 是明确的非 POSIX 指定的行为。如果尝试便携,您可能会避开它。请参阅pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html:“如果第一个操作数是 -n,或者任何操作数包含反斜杠 ('\') 字符,则结果是实现定义的”
  • 感谢您的提示!出于这个原因,printf 是更好的选择吗?
  • 是的。 (请参阅先前链接的应用程序使用部分)。
【解决方案4】:

我建议使用传统的 for 循环,因为不需要生成子进程或扩展 100 个参数:

N=100
for((i = 0; i < $N; ++i)); do
  printf -
done

很奇怪printf -%s 会触发“无效选项”但printf - 不会。为了更加安全,您可以使用printf %s -

【讨论】:

  • 不是那么好奇。 - 是一个完全有效的参数,通常用于根据上下文引用标准输入或标准输出,而 -x 设置一个选项标志。
  • 在算术评估中不需要$for((i = 0; i&lt;N;i++)); do
  • printf '%.s-' {1..100} 涉及 no 子shell,因为大括号扩展是一个shell 功能,而printf 是一个Bash 内置。因此,将大括号扩展与内置 printf 结合起来受制于最大值。命令行长度,但你是对的,重复次数较多时,有一些替代方案更快,尤其是内存效率更高(但 shell 循环 不是大重复计数的正确工具) - 请参阅the answer the OP linked to
  • @mklement0 确实,关于子流程批评,我正在考虑涉及perltrhead等的解决方案。如果printf 不是内置的,那么我发布的解决方案将产生 100 个进程!是的,我对使用 {1..N} 的批评是它需要 O(N) 空间。
  • 其实{1..N}实际上需要Ω(N log N)空间,因为每个参数的大小都是以对数方式增加的……
【解决方案5】:

您还可以使用tputprintf 来完成用确切数量的破折号填充终端,或者直接画线,或者将破折线写入变量(比如line)以重复使用,例如要将屏幕宽度的虚线写入变量line,您可以这样做:

$ eval printf -v line '%.0s-' {1..$(tput cols)}

然后每次需要画线时只需echo "$line"。也可以直接写到屏幕上用。

$ eval printf '%.0s-' {1..$(tput cols)}

(我不是eval 的忠实粉丝,但这是一种保证命令结果不会有害的用途)。

【讨论】:

    【解决方案6】:

    多年来,这一直是我的首选……虽然对 AIX 不友好。更改最后一个字符以更改组成行的重复字符

    printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' _
    

    【讨论】:

      【解决方案7】:
      1. jot 可以做到,没有 bashisms:

        jot -s '' -b - 100
        
      2. seq 也一样,但不是很好,它需要一个tr

        seq -s- 100 | tr -d '[0-9]'
        

      【讨论】:

      • 鉴于 jot 不是 POSIX 指定的工具,它可以说与 bash 一样不可移植。 seq 的批评完全相同。
      • @CharlesDuffy,是的...但是 OP 中没有 POSIX 标记。
      猜你喜欢
      • 2014-06-25
      • 2015-09-11
      • 2015-02-26
      • 2013-07-19
      • 2011-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-14
      相关资源
      最近更新 更多