AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / computer / 问题 / 1569959
Accepted
Christian Hick
Christian Hick
Asked: 2020-07-20 14:36:13 +0800 CST2020-07-20 14:36:13 +0800 CST 2020-07-20 14:36:13 +0800 CST

Bash:如何消除循环生成的变量末尾的逗号

  • 772

我有以下 bash 脚本,它在同一行输出两个随机数。

#!/bin/bash

for i in 1 2; do
   unset var
   until [ "$var" -lt 10000 ] 2>/dev/null; do
      var="$RANDOM"
done
printf "%s," "${var/%,/}"
done

输出是:

5751,2129,

我试图摆脱我尝试过的逗号,$var以便"${var/%,/}"$var = 5751,2129 的输出可以使用。有人能帮忙吗?

bash
  • 1 1 个回答
  • 1688 Views

1 个回答

  • Voted
  1. Best Answer
    Kamil Maciorowski
    2020-07-21T00:25:52+08:002020-07-21T00:25:52+08:00

    像这样分配后:var="$RANDOM",var变量包含一个来自扩展的字符串$RANDOM。在 Bash$RANDOM中,从 到 范围内扩展为十进制0数32767。那里没有,字符,因此尝试,从以后的扩展中删除是没有意义的$var。

    您在输出中观察到的逗号字符来自这部分代码:

    printf "%s," "${var/%,/}"
    # here    ^
    

    在循环的每次迭代中都会调用此命令,因此每次迭代都会在输出中添加一个逗号字符。

    已打印的内容不能不打印。有细微差别:

    • 您可以将输出通过管道传输到过滤器,过滤器可能会删除其中的某些部分。过滤器可能在脚本之外(例如你调用the_script | the_filter);或者您可以将脚本某些部分的输出通过管道传输到脚本内的过滤器。在后一种情况下,整个脚本的输出将被过滤;脚本部分已经打印的内容仍然不是未打印的;我的意思是它确实进入了过滤器。过滤器稍后将其删除。
    • 如果您打印到终端,则可以使用新数据覆盖先前输出的某些部分。有字符和序列可以移动光标;他们仍然到达终点站。您几乎可以立即在视觉上隐藏先前的输出,但如果您将输出重定向到文件(或不理解所使用序列的终端),那么您会发现它就在那里。

    摆脱不需要的逗号的正确方法是首先不要打印它;或在脚本中过滤它。有几种方法可以做到这一点,我不打算找到所有方法。我将讨论其中的一些。我假设您想知道对于具有两次以上迭代的循环的可能方法;可能是预先不知道迭代次数的循环;也许 for 循环不是由数字枚举的;也许 for 可能永远不会完成的循环(例如while true,而不是for)。

    注意:您使用printf "%s," "${var/%,/}"了 ,它不打印尾随换行符。如果可能的话,我会尝试复制这种行为。

    几种可能的方法:

    1. 循环的内部不依赖于$i. 您可以摆脱循环并使用两个单独的变量:

      unset var1
      until [ "$var1" -lt 10000 ] 2>/dev/null; do
         var1="$RANDOM"
      done
      unset var2
      until [ "$var2" -lt 10000 ] 2>/dev/null; do
         var2="$RANDOM"
      done
      printf '%s,%s' "$var1" "$var2"
      

      笔记:

      • 它不是DRY。
      • 它不能很好地扩展。(如果你有for i in {1..100}呢?)
      • 如果事先不知道迭代次数,就会变得很麻烦。
    2. 您可以将当前代码放在管道中并过滤掉尾随的逗号。例子:

      for i in 1 2; do
         unset var
         until [ "$var" -lt 10000 ] 2>/dev/null; do
            var="$RANDOM"
         done
      printf "%s," "$var"
      done | sed 's/,$//'
      

      笔记:

      • sed(或您使用的任何过滤器)可能会或可能不会处理循环产生的不完整行(未由换行符终止的行)。在这种情况下sed,取决于实施。
      • 如果sed确实处理了它,它仍然可以用换行符终止其输出。
      • sed(或您使用的任何过滤器)可能无法处理太长的行。上面的特定代码生成了一条相当短的线,但通常(想象许多迭代)长度可能是一个问题。
      • sed,作为面向行的工具,必须在处理之前读取整行。在这种情况下,它必须读取其整个输入。在所有迭代完成之前,您不会从中得到任何东西。
      • 循环在子shell 中运行。在一般情况下,无论出于何种原因,您都可能希望它在主 shell 中运行。
    3. 您可以将当前代码的输出捕获到变量中。最后,在扩展变量时删除尾随逗号:

      capture="$(for i in 1 2; do
         unset var
         until [ "$var" -lt 10000 ] 2>/dev/null; do
            var="$RANDOM"
         done
      printf "%s," "$var"
      done)"
      printf "%s" "${capture%,}"
      

      笔记:

      • 循环在子shell 中运行。在一般情况下,无论出于何种原因,您都可能希望它在主 shell 中运行。
      • 代码在到达最后一个printf(循环外)之前是静默的。在所有迭代完成之前,您将一无所获。
      • 一般来说,循环可以输出任意数量的字节。我认为 Bash 可以在变量中存储大量数据;然后,因为printf是内置的,它可能printf "%s" "${capture%,}"可以在不达到命令行长度限制的情况下处理。我没有对此进行彻底测试,因为 IMO 将大量数据存储在 shell 变量中并不是最佳实践。如果您知道输出的长度有限,那么这种做法仍然是合理的。(记录一下:上面的代码肯定会产生非常短的输出。)
      • Bash 不能将 NUL 字符存储在变量中(大多数 shell 不能;zsh 可以)。另外$()删除所有尾随换行符。这意味着您不能使用变量来存储任意输出并在以后准确地重现它。(记录一下:在上面的代码中,内部的片段$()不会生成 NUL 或尾随换行符。)
    4. 您可以将每次迭代附加到某个变量,而不是捕获输出:

      capture=''
      for i in 1 2; do
         unset var
         until [ "$var" -lt 10000 ] 2>/dev/null; do
            var="$RANDOM"
         done
      capture="$capture$var,"
      done
      printf "%s" "${capture%,}"
      

      笔记:

      • 代码在主 shell 中运行(而不是在子 shell 中)。
      • 将数据存储在变量中的限制(参见前面的方法)仍然适用。
      • 在 Bash 中,您可以使用capture+="$var,". (注意:如果为变量设置了整数属性,则=+表示“添加”,而不是“追加”。)
    5. 您可以检测最后一次迭代并使用没有的格式,:

      # this example is more educative with more than two iterations
      for i in {1..5}; do
         unset var
         until [ "$var" -lt 10000 ] 2>/dev/null; do
            var="$RANDOM"
         done
         if [ "$i" -eq 5 ]; then
            printf "%s" "$var"
         else 
            printf "%s," "$var"
         fi
      done
      

      笔记:

      • 没有子壳。
      • 如果您事先不知道数字,则检测最后一次迭代会更加困难。
      • 如果您遍历一个数组(例如for i in "${arr[@]}"),那就更难了。
      • 每次迭代都会立即打印,您会按顺序获得输出。即使循环是无限的,这也会起作用。
    6. 您可以检测第一次迭代并使用不带,. 请注意,您可以在原始代码中使用,%s而不是;%s,那么你会得到,5751,2129而不是5751,2129,. 通过此更改,上述任何避免或删除尾随逗号的方法都可以转换为避免或删除前导逗号的方法。最后一种方法变成:

      # this example is more educative with more than two iterations
      for i in {1..5}; do
         unset var
         until [ "$var" -lt 10000 ] 2>/dev/null; do
            var="$RANDOM"
         done
         if [ "$i" -eq 1 ]; then
            printf "%s" "$var"
         else 
            printf ",%s" "$var"
         fi
      done
      

      笔记:

      • 没有子壳。
      • 如果您总是从1(或一般固定的唯一字符串)开始,则检测第一次迭代很容易。
      • 但是如果你遍历一个数组就更难了,例如for i in "${arr[@]}". 您不应该检查if [ "$i" = "${arr[1]}" ],因为数组中可能有一个与"${arr[1]}"后面相同的元素。解决这个问题的一种直接方法是为循环保留一个索引(index=1在循环之前,然后在每次迭代结束时加一)并针对1;测试它的值。我会发现这样的代码有点麻烦。
      • 每次迭代都会立即打印,您会按顺序获得输出。即使循环是无限的,这也会起作用。
    7. 你可以让,自己来自一个变量。输入变量为空的循环,并,在每次迭代结束时将其设置为。这将在第一次迭代结束时有效地改变一次值。例子:

      # this example is more educative with more than two iterations
      separator=''
      for i in {1..5}; do
         unset var
         until [ "$var" -lt 10000 ] 2>/dev/null; do
            var="$RANDOM"
         done
         printf "%s%s" "$separator" "$var"
         separator=,
      done
      

      笔记:

      • 没有子壳。
      • 即使迭代数组也能很好地工作。
      • 每次迭代都会立即打印,您会按顺序获得输出。即使循环是无限的,这也会起作用。

      我个人觉得这种方法很优雅。

    一般注意事项:

    • 每个片段都会生成没有尾随换行符的输出(带有sed或另一个过滤器的可能例外)。如果您需要整个输出形成正确终止的文本行,请在循环之后运行printf '\n'(或唯一)。echo
    • 你想,成为分隔符,而不是终结者。$var这意味着如果在迭代中扩展为空字符串,则具有恰好零次迭代的循环将生成与具有恰好一次迭代的循环相同的空输出。在我们的例子$var中,每次都扩展为一个非空字符串,并且我们知道我们有不止零次迭代;但在一般情况下,使用分隔符而不是终止符可能会导致歧义。
    • 1

相关问题

  • 在非 root 用户上用 bash 替换 zsh

  • 在 macOS High Sierra 的终端中设置环境变量时遇到问题

  • 对于 cp 或 mv,是否有等同于 cd - 的东西?

  • Notify-发送窗口下出现的通知

  • 如何从 WSL 打开 office 文件

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何减少“vmmem”进程的消耗?

    • 11 个回答
  • Marko Smith

    从 Microsoft Stream 下载视频

    • 4 个回答
  • Marko Smith

    Google Chrome DevTools 无法解析 SourceMap:chrome-extension

    • 6 个回答
  • Marko Smith

    Windows 照片查看器因为内存不足而无法运行?

    • 5 个回答
  • Marko Smith

    支持结束后如何激活 WindowsXP?

    • 6 个回答
  • Marko Smith

    远程桌面间歇性冻结

    • 7 个回答
  • Marko Smith

    子网掩码 /32 是什么意思?

    • 6 个回答
  • Marko Smith

    鼠标指针在 Windows 中按下的箭头键上移动?

    • 1 个回答
  • Marko Smith

    VirtualBox 无法以 VERR_NEM_VM_CREATE_FAILED 启动

    • 8 个回答
  • Marko Smith

    应用程序不会出现在 MacBook 的摄像头和麦克风隐私设置中

    • 5 个回答
  • Martin Hope
    CiaranWelsh 如何减少“vmmem”进程的消耗? 2020-06-10 02:06:58 +0800 CST
  • Martin Hope
    Jim Windows 10 搜索未加载,显示空白窗口 2020-02-06 03:28:26 +0800 CST
  • Martin Hope
    v15 为什么通过电缆(同轴电缆)的千兆位/秒 Internet 连接不能像光纤一样提供对称速度? 2020-01-25 08:53:31 +0800 CST
  • Martin Hope
    fixer1234 “HTTPS Everywhere”仍然相关吗? 2019-10-27 18:06:25 +0800 CST
  • Martin Hope
    andre_ss6 远程桌面间歇性冻结 2019-09-11 12:56:40 +0800 CST
  • Martin Hope
    Riley Carney 为什么在 URL 后面加一个点会删除登录信息? 2019-08-06 10:59:24 +0800 CST
  • Martin Hope
    zdimension 鼠标指针在 Windows 中按下的箭头键上移动? 2019-08-04 06:39:57 +0800 CST
  • Martin Hope
    jonsca 我所有的 Firefox 附加组件突然被禁用了,我该如何重新启用它们? 2019-05-04 17:58:52 +0800 CST
  • Martin Hope
    MCK 是否可以使用文本创建二维码? 2019-04-02 06:32:14 +0800 CST
  • Martin Hope
    SoniEx2 更改 git init 默认分支名称 2019-04-01 06:16:56 +0800 CST

热门标签

windows-10 linux windows microsoft-excel networking ubuntu worksheet-function bash command-line hard-drive

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve