知道为什么排序实用程序给我错误的结果?

时间:2017-03-19 20:30:25

标签: bash shell

编辑: 为了清楚起见,我们从一个类似于这个的for循环得到了我们的STDOUT

for (( i=1; i<="$FILE_AMOUNT"; i++ )); do
    MY_FILE=`find $DIR -type f | head -$i | tail -1`
    FILE_TYPE=`file -b "$MY_FILE"
    FILE_TYPE_COUNT=`echo $FILE_TYPE" | sort | uniq -c`
    echo "$FILE_TYPE_COUNT"
done

因此,我们的STDOUT基本上是从逐个打印的文件实用程序输出的,而不是它实际上是我们可以复制的字符串集 - 这可能是所有问题背后的核心

`

所以有一个泡菜,我绝对不能把头包起来。

基本上我创建了一个shellcript,它将打印出我们目录中的各种文件类型。但是,由于某些奇怪的原因,当我尝试在输出上使用uniq时,它几乎无效。这是我的输出

POSIX shell script, ASCII text executable
ASCII text
Bourne-Again shell script, ASCII text executable
UTF-8 Unicode text, with overstriking
Bourne-Again shell script, ASCII text executable

似乎相当不言自明,但是当我使用

FILE_TYPE_COUNT=`echo "$FILE_TYPE" | sort | uniq -c`

这是它打印的结果

  1 POSIX shell script, ASCII text executable
  1 ASCII text
  1 Bourne-Again shell script, ASCII text executable
  1 UTF-8 Unicode text, with overstriking
  1 Bourne-Again shell script, ASCII text executable

显然应该是

  1 POSIX shell script, ASCII text executable
  1 ASCII text
  2 Bourne-Again shell script, ASCII text executable
  1 UTF-8 Unicode text, with overstriking

知道我做错了吗?

显然,uniq认为线条并不相同,但我认为这是排序错误,因为它无法对我的STDOUT进行排序。那么任何线索如何正确排序ALPHABETICALlY?

3 个答案:

答案 0 :(得分:4)

你的方法看起来过于复杂,试试这个:

find $DIR -type f -exec file -b -- {} \; | sort | uniq -c

如果您不熟悉-exec,它会在每个文件中执行给定的命令,在我们的情况下为file -b -- {}。占位符{}将替换为当前正在处理的文件的路径。

为什么你的方法不起作用:

您在for循环中执行此操作echo $FILE_TYPE" | sort | uniq -c$FILE_TYPE仅包含该点的一个文件的文件类型。您需要将sort | uniq -c移出循环。

我调整了您的代码以便它可以运行:

declare -a TYPES=()
for (( i=1; i<="$FILE_AMOUNT"; i++ )); do
    MY_FILE=`find a/ -type f | head -$i | tail -1`
    FILE_TYPE=`file -b "$MY_FILE"`
    TYPES+=("$FILE_TYPE") # add type of current file to TYPES array
done

# TYPES now contains the types of all files and we can now count them
printf "%s\n" "${TYPES[@]}" | sort | uniq -c

答案 1 :(得分:1)

您看到的问题是因为您正在为循环的每次迭代排序一个项目的集合。

您需要对循环的整个输出进行排序。

你的(语法修复)脚本:

for (( i=1; i<="$FILE_AMOUNT"; i++ )); do
    MY_FILE=`find $DIR -type f | head -$i | tail -1`
    FILE_TYPE=`file -b "$MY_FILE"`
    FILE_TYPE_COUNT=`echo "$FILE_TYPE" | sort | uniq -c`
    echo "$FILE_TYPE_COUNT"
done

Mofified正常工作:

for (( i=1; i<="$FILE_AMOUNT"; i++ )); do
    MY_FILE=`find $DIR -type f | head -$i | tail -1`
    file -b "$MY_FILE"
done | sort | uniq -c

优化一次:

for FILE in $(find $DIR -type f); do
    file -b "$FILE"
done | sort | uniq -c

优化两次(参见@P.Gerber的答案):

find $DIR -type f -exec file -b -- {} \; | sort | uniq -c

你的原始剧本非常低效。

关于效率的说明&amp;操作:

  • ${FILE_AMOUNT}必须正确迭代整个数据集
  • 您正在运行find,它会返回整个数据集,然后丢弃您不感兴趣的所有内容,每次迭代
  • 您在每次迭代时,在大小为1的数据集上运行sortuniq
  • 由于您不断重新计算数据集,如果它在脚本执行过程中发生了一半变化(例如:创建/删除了文件/目录),那么结果将无效
  • 请记住,每次启动新程序时,都要支付性能损失 - 这会因为您不断计算数据集然后丢弃“不想要的所有内容”而加剧了这一点。 ;

答案 2 :(得分:1)

除了其他好的解决方案之外,请务必了解您正在使用的排序规则集。要检查当前的排序规则,您可以执行以下操作:

echo anything | sort --debug

使用注释查看结果。考虑:

echo -e "a 2\na1" | sort --debug
sort: using ‘en_US.UTF-8’ sorting rules
a1
__
a 2
___

请注意,规则集正在排序,可能会出现意外结果。如果您正在寻找简单的字节比较,请务必将LC_ALL=C设置为:

LC_ALL=C sort

例如:

echo -e "a 2\na1" | LC_ALL=C sort --debug
sort: using simple byte comparison
a 2
___
a1
__

使用LC_ALL对于获得您期望的结果非常重要。最后,运行locale命令并阅读手册页以获取特定于语言环境的信息。