5

我有一个文本文件,每行包含一些单词,例如:

stackoverflow coding programming
tag question badges

我必须对每一行进行排序并保留行的顺序。例如,对于上面的示例,输出应该是:

coding programming stackoverflow
badges question tag

到目前为止,我的解决方案是创建一个临时文件,其中所有行都已排序。bash 脚本如下所示:

FILE_TMP=$FILE".tmp" 
while read line
do
echo $line | xargs -n1 | sort | xargs >>$FILE_TMP
done < $FILE

mv $FILE_TMP $FILE

它工作正常,但我不高兴我必须创建一个重复的文件,特别是因为文件很大。

那么,我的问题是有什么解决方案可以对文件的每一行进行排序吗?

谢谢,

4

6 回答 6

4

试试这个(如果文件不是空格分隔,您可能必须更改 sed):

cat datafile.dat | while read line; do echo $line | sed 's/ /\n/g' | sort | gawk '{line=line " " $0} END {print line}' ; done
于 2013-04-24T14:17:23.907 回答
2

如果 Python 是一个选项,那么使用 fileinput 模块的就地支持将非常容易

>>> import os
>>> import fileinput
>>> for line in fileinput.input('file.txt', inplace=1):
...     line = line.rstrip(os.linesep)
...     print(' '.join(sorted(line.split())))
...
于 2013-04-24T14:29:23.300 回答
1

您可以编写一个文本编辑器(例如 vim 或 emacs)脚本来“就地”执行它,但这并不能真正帮助您避免使用临时文件,因为文本编辑器将在内部使用临时文件。

如果您真正的问题是运行速度很慢,那可能是因为它为源文件中的每一行生成了 3 个不同的进程。您可以通过使用像 perl 这样的脚本语言来解决这个问题,该语言可以通过文件排序行而不产生任何额外的进程。您仍然有一个附加文件用于输出。

于 2013-04-23T17:09:32.257 回答
1

接受的答案有点慢。尝试这个:

awk ' {split( $0, a, " " ); asort( a ); for( i = 1; i <= length(a); i++ ) printf( "%s ", a[i] ); printf( "\n" ); }' input >output

注意:您的 awk 必须是 GNU,以便具有 asort()。

于 2018-06-14T06:48:12.170 回答
0

我认为以下 awk 的优点应该可以完成这项工作:

prompt$ cat foo.awk
{
    n = split($0, words)
    do {
        change_occured = 0
        for (idx = 1; idx <= n; ++idx) {
            if (words[idx] > words[idx + 1]) {
                t = words[idx]
                words[idx] = words[idx + 1]
                words[idx + 1] = t
                change_occured = 1
            }
        }
    } while (change_occured != 0)
    for (idx in words) {
        printf("%s ", words[idx])
    }
    split("", array)
    print ""
}
prompt$ awk -f foo.awk <<EOF
heredoc> stackoverflow coding programming
heredoc> tag question badges
heredoc> EOF
coding programming stackoverflow  
badges question tag

编辑请注意,这不是就地编辑。它充当从标准输入到标准输出的过滤器。您也可以为此使用 awk,但在那里读写文件感觉“笨拙”。如果您真的想避免临时文件,请使用 Perl 之类的东西。

于 2013-04-23T17:30:36.933 回答
0

实际上,针对此问题的任何“合理”解决方案都会将新内容写入新的临时文件,然后重命名。甚至像 perl “就地”处理 ( perl -pi...) 或文本编辑器之类的东西实际上也能做到这一点。如果你想真正做到,写入相同的物理磁盘位置,可以做到(新内容与旧内容占用完全相同的空间)但它相当痛苦

您可以将此答案中的代码编译为overwrite可执行文件,然后运行(警告:这很危险,请先备份您的文件!)

while read line ; do echo $line | xargs -n1 | sort | xargs ; done < f | ./overwrite f

这是相当脆弱的,例如,您应该绝对确保脚本的排序不会混淆空白字符(DOS 换行符呢?和连续的空白?),脚本必须吐出相同数量(或更少)的每行字节数。

于 2013-04-23T17:37:29.907 回答