【问题标题】:for loop in bash simply prints n times the command instead of reiteratingbash中的for循环只是打印n次命令而不是重复
【发布时间】:2019-10-04 20:41:56
【问题描述】:

我有一个超过 6000 行的 input.txt 文件。

如果一行 a 有超过 10 个单词,那么我希望它被拆分,但不是在第 10 个单词,而是第一个逗号字符出现的位置。并且,如果新行也有10个以上的单词,那么它也应该被拆分,并不断重复这个过程7次。

最终产品:没有超过 10 个单词和逗号的行,因为它们都已被拆分。

例子:

输入

Line 1: This is me, and my sample test line that I like to get working, and I want to be able to kick some ass while doing it

预期输出:

Line 1: This is me, 
Line 2: and my sample test line that I like to get working,
Line 3: and I want to be able to kick some ass while doing it

我正在使用以下代码:

#! /bin/bash

for run in {1..7}
do

awk 'NF >= 10 {
sub (", ", ",\n")

}1' input.txt

done

此代码没有给出预期的结果。相反,我得到以下输出 7 次。

line 1: This is me,

line 2: and my sample test line that I like to get working, and I want to be able to kick some ass while doing it.

我倾向于 sed,但我不清楚某些事情。我看到了三种方法 1) 代码读取一行(比如 line7),它超过 10 个单词并以逗号分隔(但不检查新破坏的 like 是否超过 10 个单词)并移至下一行。在文件末尾,它会重复这个过程(比如 7 次)以确保新断行也在 10 个单词以下。然后,它接受这个过程的输出并做同样的事情,但有一个新的条件(例如单词“和”)。然后,它需要这个等的输出......我可以添加无穷无尽的条件。这是我更喜欢的方法。我也认为编码更容易。

第二种方法 2) 代码是否读取行,如果超过 10 个单词,它会在逗号处分解,然后如果仍然超过 10 个单词,它会在逗号处进一步分解,依此类推,直到少于 10 个单词。只有这样,它才会移动到下一行。我认为这就是 Ghoti 的代码所做的。但是添加附加条件很复杂。 3)第三种方法是:它在逗号处分行超过 10 个单词,然后在“and”处分行,以此类推。然后最后,整个过程被重复了几次。恕我直言,这也不是最好的方法。

有人可以帮忙吗。

提前谢谢你!

【问题讨论】:

  • 你能清理一下你的样本输入和输出吗?目前还不清楚我们正在处理的真正文本是什么。还有...... $run 变量将用于什么?您正在执行 awk 命令七次,但七次迭代似乎没有任何区别。您认为 bash for 循环会如何工作?
  • 你为什么不直接使用 gsub 而不是 sub?
  • 如果你想让 awk 做七次,在你的 awk 程序中编写 for 循环。
  • 老实说我不懂编程,我真的不明白你们在问我什么。我已经根据我在网上阅读的内容将其拼凑在一起。有人可以帮我做我需要做的事情吗?
  • @HenryM,如果您对上一个问题的回答不能完全解决问题,您无需提出单独的问题。您可以简单地说在 cmets 或更新现有问题。 stackoverflow.com/questions/58240415/…

标签: linux bash for-loop


【解决方案1】:

我想我明白你在追求什么。您的方法存在一些问题:

  • awk 不会就地处理文件。因此,您的 sub() 进行了更改,1 打印到标准输出,但您的输入文件永远不会更改。
  • sub() 时,不会将新记录插入到 awk 正在处理的输入流中。您的命令只是在当前记录中添加一个换行符。

鉴于这些,您可以按照您的建议多次处理输入。但是,与其随意假设一行最多有 7 个 10 字的短语,不如实际检测是否需要继续。像这样的:

#!/usr/bin/env bash

input=input.txt
temp=$(mktemp ${input}.XXXX)
trap "rm -f $temp" 0

while awk '
  BEGIN { retval=1 }
  NF >= 10 && /, / {
    sub(/, /, ","ORS)
    retval=0
  }
  1
  END { exit retval }
' "$input" > "$temp"; do
  mv -v $temp $input
done

这使用来自 awk 的退出值来确定我们是否需要运行 bash 循环的另一次迭代。如果 awk 检测到不需要替换,则循环停止。

【讨论】:

  • 哦,哇,我知道你在那里做了什么。惊人的!感谢您的帮助。这真的很聪明。赞成。
  • Ghoti,快速提问...是否可以将输出设置为不同的文件而不是覆盖我的原始文档?
  • 您可以创建一个temp2 文件,将其添加到trap 命令并以cp $input $temp2 开头。
  • @HenryM .. 绝对有可能。你会如何处理它?
  • 我想再添加两个条件来分解超过 10 个单词的句子... 1)如果没有找到逗号字符,则从左到右搜索第一个“和”(包括之后的空格,因此它不会像 android 那样拆分单词)并在“and”之前立即拆分。 2) 如果没有找到“and”,则在 25 个单词之后将行拆分。这样可以确保没有超过 10 个单词的句子。我认为我最初对以下条件进行 7 次重复的方法更容易编码。只要代码有效,代码就不需要优雅或快速。有什么想法吗?
【解决方案2】:

好的,这就是我解决这个问题的方法。这很丑陋,但它有效。另外,我可以继续传递更多 sed 命令以添加更多条件(例如我在@ghoti 上方的评论)。

sed -r '/((\w)+[., ]+){10}/s/\./\.\n/' input.txt | sed -r '/((\w)+[., ]+){10}/s/\./\.\n/' | sed -r '/((\w)+[., ]+){10}/s/\./\.\n/' | sed -r '/((\w)+[., ]+){10}/s/\./\.\n/'| sed -r '/((\w)+[., ]+){10}/s/\./\.\n/' | sed -r '/((\w)+[., ]+){10}/s/\./\.\n/' | sed -r '/((\w)+[., ]+){10}/s/\./\.\n/' | tr -s [:space:] > output.txt

基本上,我只是将相同的 sed 命令传送了 7 次(在上面的示例中,我替换的是句点而不是逗号,但都是一样的)。 根据我在网上阅读的内容,我很惊讶这个命令不允许一些递归/重复。或者如果有人知道,请随时编辑。

【讨论】:

  • 重复可以用sed :a; something; ta完成,重复某事直到某事找不到任何事情可做。
  • @WalterA sed -r ':a; /((\w)+[., ]+){9}/s/\./\r\n/; ta' input.txt | tr '\r' '.' .....这几乎奏效了,除了当一个或多个单词是单词“O.K.”时,它会拆分少于 10 个单词的句子。或“博士”或任何有句号的词。我们如何将单词定义为空格之间的任何内容?
  • 您可以在s/\. /\r\n /; 的匹配中包含一个空格,并使用((\w)+[,.]*[ ]+[,. ]*)((\w[^ ]*)+[ ]+){9} 之类的空格定义一个单词加边界
  • 我试过这个:sed -r ':a; /((\w[^ ]*)+[ ]+){9}/s/\. /\r\n /;塔' | tr '\r' '.'而这个 sed -r ':a; /((\w)+[,.]*[ ]+[,.]*)/s/\. /\r\n /;塔' | tr '\r' '.'两者都没有工作。相反,他们从一行中删除了除最后一个句号之外的所有句点。
  • 感谢您迄今为止的帮助。我会再发一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-29
  • 2016-09-07
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 2019-10-28
  • 2017-03-13
相关资源
最近更新 更多