【问题标题】:How can I retain numbers for sorting them later?我怎样才能保留数字以便以后对它们进行排序?
【发布时间】:2015-05-24 22:43:23
【问题描述】:

我有一个听起来像这样的问题:编写一个 shell 脚本,从命令行为每个文件输出 长于从键盘读取的数字 k 的单词数。 输出必须按字数排序。

如何保留每个文件的字符数,以便对它们进行排序?

我尝试过类似的方法:

#!/bin/bash
if [ #@ -ne 1 ]
        then exit 1
fi
array[$@]=''
echo -n "Give the number>"
read k
for f in $@;
do
        n=`$f | wc -c`
        if [ $n -gt $k ];
        then
                i++
                array[i]=$n
        fi
done
echo {array[@]} | sort -n

【问题讨论】:

  • 从标准输入读取k 是一个可怕的界面。想象一下如果grep 那样工作!将k 设为第一个参数。
  • 使用$@ 本质上是不正确的;你应该总是使用"$@"。这会保留文件名中的空格。您还应该使用$(…) 而不是反引号。如果dothen 不在同一行,则不需要在forif 语句末尾使用分号。
  • 请您澄清您是否想要单词数(任意长度)大于阈值k(以及文件中的单词数)的文件列表,或者是否需要每个文件中单词长度超过阈值k 的单词数的文件列表。在当前的 3 个答案中,2 个解决了第一个问题,1 个解决了第二个问题。

标签: linux bash shell vi


【解决方案1】:

挑战是:

  • 编写一个shell脚本,从命令行为每个文件输出长于从键盘读取的数字k的字数。输出必须按字数排序。

我拒绝回答提示——命令带有参数。我会同意 William Pursell 的建议,即数字是第一个参数——这是一个合理的解决方案。另一种方法是使用 -l 23 之类的选项作为长度(以及调整其他操作的其他选项)。

到目前为止,我看到的解决方案是计算单词的数量,而不是超过给定长度的单词的数量。这是个问题。为此,我认为awk 是合适的:

awk -v min=$k '{ for (i = 1; i <= NF; i++) if (length($i) >= min) print $i; }'

这会在标准输出上每行生成至少min 个字符。我们将一次处理一个文件,至少在第一遍中。

然后我们可以用wc -l 来计算此类单词的数量。最后,我们可以对数据进行数字排序。

把它们放在一起产生:

#!/bin/bash

case "$#" in
0|1) echo "Usage: $0 length file ..." >&2; exit 1;;
esac

k=${1:?"Cannot provide an empty length"}
shift

for file in "$@"
do
    echo "$(awk -v min=$k '{ for (i = 1; i <= NF; i++)
                                 if (length($i) >= min) print $i
                           }' "$file" |
            wc -l) $file"
done | sort -n

这会列出最后一个单词最长的文件;这很方便,因为最有趣的文件位于列表的末尾。如果您想先获得高数字,请将-r 添加到sort

当然,如果我们使用awk,我们可以改进。它可以计算每个文件中长字的数量,并打印文件名和数量,因此所有文件只需一次调用awk。不过,这需要更多的编程:

#!/bin/sh

case "$#" in
0|1) echo "Usage: $0 length file ..." >&2; exit 1;;
esac

k=${1:?"Cannot provide an empty length"}
shift

awk -v min=$k '
    FILENAME != oldfile { if (oldfile != "") { print longwords, oldfile }
                          oldfile = FILENAME; longwords = 0
                        }
    { for (i = 1; i <= NF; i++) if (length($i) >= min) longwords++ }
    END { if (oldfile != "") { print longwords, oldfile } }
    ' "$@" |
sort -n

如果您有 GNU awk,甚至还有一些方法可以对 awk 中内置的结果进行排序。

【讨论】:

  • 很高兴知道阅读理解在现代社会并不是一门失传的艺术:p
  • @DavidC.Rankin: 我不知道像我这样的恐龙是否代表现代社会 :D — :(
  • 这似乎是那些在 70 年代末或 80 年代初开始编程的人所面临的挑战。也许这是 age:wisdom 二分法的一部分?
  • @DavidC.Rankin:有时,困难在于英语作为第二语言;对于非母语的人来说,英语并不是世界上最容易理解的语言。
  • 我认为这是一项任务,因此,这个答案回应了实际问题。但是标题,第一段,第二段,代码讲的是不同的故事,很容易混淆。无论如何,我都投赞成票。
【解决方案2】:

你可以稍微简化一下脚本:

#!/bin/bash
(( $# > 0 )) || exit
read -r -p 'Enter number > ' k
wc -w "$@" | sed '$d' | gawk -v k="$k" '$1>k{print $0}' | sort -nr

在哪里

  • read -r -p ... 提示并读取输入
  • wc -w - 计算您输入的所有文件的字数作为参数
  • sed ... - 跳过最后一行 (total...)
  • awk 跳过计数小于 $k 的行
  • sort - 用于对输出进行排序

在@Tom Fench here 的大力帮助下,它可以简化为:

wc -w "$@" | awk -v k="$k" 'NR>1&&p>k{print p}{p=$1}' | sort -nr

或使用文件名(基于@Wintermute 的评论here

wc -w "$@" | awk -v k="$k" 'p { print p; p="" } $1 > k { p = $0 }' | sort -nr

编辑

根据@Jonathan Leffler 的评论添加了一个变体,用于计算每个文件中长度为数字k 的单词。

#!/bin/bash
(( $# > 0 )) || exit
read -r -p 'Enter number > ' k
let k++
grep -HoP "\b\w{${k:-3},}\b" "$@" |\
 awk -F: '{f[$1]++}END{for(n in f)print f[n],n}' |\
 sort -nr

地点:

  • grep... 搜索长度为输入数字的单词(如果希望相等且更长,请省略let 行)。打印出如下行:
file1:word1
file1:word2
...
file2:wordx
file2:wordy
  • 和 awk 根据第一个字段计算频率,例如按文件名计数。

【讨论】:

  • 在我看来,这误解了这个问题。问题是关于每个文件中超过给定长度的字数,而不是每个文件中的字数,如果总字数大于给定数。使用wc -w 会立即丢失相关信息。
  • @JonathanLeffler - 嗯,我将其理解为“查找单词数大于输入数字的文件” - in line 与“OP's”示例代码他也使用wc -c。无论如何,会更新我的答案。;)
  • 所以我明白了。文本说“输出长于数字 k 的单词数”,根据我对英语的理解,这意味着您必须评估文件中每个单词的长度,并且只计算长度超过的单词给定的长度。最终由 OP 决定。
猜你喜欢
  • 2018-03-26
  • 2021-09-17
  • 1970-01-01
  • 1970-01-01
  • 2018-12-06
  • 1970-01-01
  • 2022-07-03
  • 1970-01-01
  • 2023-01-23
相关资源
最近更新 更多