【问题标题】:reverse a file in Unix shell在 Unix shell 中反转文件
【发布时间】:2015-07-06 18:37:48
【问题描述】:

我有一个文件 parse.txt

parse.txt 包含以下内容

remo/hello/1.0,remo/hello2/2.0,remo/hello3/3.0,whitney/hello/1.0,julie/hello/2.0,julie/hello/3.0

我希望 output.txt 文件作为(将顺序从最后一个到第一个颠倒)使用 parse.txt

julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0

我尝试了以下代码:

tail -r parse.txt 

【问题讨论】:

    标签: bash shell unix


    【解决方案1】:

    您可以使用来自 GNU Coreutils 的非常有用的 tac

    tac -s "," parse.txt > newparse.txt
    

    tac 默认情况下会将文件“cat”到标准输出,反转行。通过使用 -s 标志指定分隔符,您可以根据需要简单地反转字段。

    (您可能需要执行后处理步骤以使逗号正确计算,这可能是您管道中的另一个步骤。)

    【讨论】:

    • 请注意,如果有的话,换行符将成为最后一个字段的一部分
    • 感谢附带条款,这是 GNU Coreutils 的一部分。人们经常忘记并不是每个人都使用 Linux。
    • @EricRenouf - 有什么解决方法吗?例如,假设 OP 不需要反转单行,而是需要反转文件中的每一行?
    • 使用rev 反转行
    • rev 不适用于此特定问题。 printf "%s\n" abc def ghi | tacprintf "%s\n" abc def ghi | rev
    【解决方案2】:

    我喜欢tac 解决方案;它紧凑而优雅,但正如 Micah 指出的那样,tac 是 GNU Coreutils 的一部分,这意味着它在 FreeBSD、OSX、Solaris 等中默认不可用。

    这可以在纯 bash 中完成,不需要外部工具。

    #!/usr/bin/env bash
    
    unset comma
    read foo < parse.txt
    bar=(${foo//,/ })
    for (( count="${#bar[@]}"; --count >= 0; )); do
      printf "%s%s" "$comma" "${bar[$count]}"
      comma=","
    done
    

    根据您的示例输入,这显然只处理一行。如果你需要处理多行输入,你可以把它包装起来。

    这里的逻辑是,我们可以通过将逗号替换为空格来将输入转换为数组。当然,如果我们的输入数据包含空格,则必须进行调整。一旦我们有了数组,我们只需向后退一步,打印每条记录。

    请注意,这不包括终止换行符。如果你想要一个,你可以添加它:

    printf '\n'
    

    作为最后一行。

    【讨论】:

    • 更简单,将printf '\n'替换为echo
    • 您可以将readbar= 行替换为IFS=, read -a bar &lt; parse.txt 以直接读入数组,用逗号分隔
    • @glennjackman,你知道,我知道只要你在 bash 中,echo 的行为始终如一,但几年前我已经改掉了使用它的习惯由于其他 shell 和操作系统的不一致,从那以后我一直坚持使用printf。幸运的是,echoprintf 都是 bash 内置的,因此不会对性能造成严重影响。 :)
    • @DigitalTrauma,很好,并保存了一个变量。我这样做是为了更清楚地解释这些步骤(我猜是有争议的;对我来说清楚的对我来说很清楚),但是是的,优化肯定是要走的路。
    【解决方案3】:
    perl -F, -lane 'print join ",", reverse @F' parse.txt > output.txt
    

    【讨论】:

      【解决方案4】:

      你可以使用这个 awk 命令:

      awk -v RS=, '{a[++i]=$1} END{for (k=i; k>=1; k--) printf a[k] (k>1?RS:ORS)}' parse.txt
      julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0
      

      【讨论】:

        【解决方案5】:

        这个问题被标记为,你提到了tail -r,这表明你可能没有使用Linux(带有完整的GNU工具链),而是一些“真正的”Unix(BSD变体),例如.

        因此,tac 命令不可用,但如问题中所述,tail -r 可用。所以你可以使用以下内容:

        $ tr ',' '\n' < parse.txt | tail -r | tr '\n' ',' | sed 's/,$//'
        julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0
        $ 
        

        注意事项:

        • 这仅适用于只有一行的文件,因为我们依赖于将逗号转换为换行符并返回。如果多于一行,则其间的换行符将在第二个 tr 之前转换为逗号。
        • 最后的sed 是删除一个尾随逗号,它是从tail 插入的尾随换行符转换而来的

        【讨论】:

          【解决方案6】:

          sed模拟tac

          tr , '\n' <parse.txt | sed '1!G; h; $!d' | paste -sd ,
          

          或者,如果您没有paste

          tr , '\n' <parse.txt | sed '1!G; h; $!d' | tr '\n' , | sed 's/,$//'
          

          输出:

          julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0
          

          【讨论】:

            【解决方案7】:

            您可以使用任何语言来做到这一点

            xargs ruby -e "puts ARGV[0].split(',').reverse.join(',')" < parse.txt
            

            【讨论】:

            • 你需要chomp,而 xargs 似乎不必要地复杂:ruby -ne 'puts $_.chomp.split(",").reverse.join(",")' parse.txt
            【解决方案8】:

            可以通过 tac(来自 cat)进行反向操作。正如所评论的那样,这将反转不是 OP 要求的行。

            tac filename
            

            您仍然可以tac,如果您提供逐行和反向不是换行分隔符而是字段分隔符,这里是,

            echo "a,b,c" | tr '\n' ',' | tac -s "," | sed 's/,$/\n/'
            

            【讨论】:

            • 重点是反转每一行的字段,而不是反转文件中的行。
            • @glennjackman 抱歉回答不完整。文件中的行由分隔符确定,默认为换行符。通过将分隔符更改为帖子中的分隔符,您可以实现字段的反转。
            猜你喜欢
            • 2016-09-08
            • 1970-01-01
            • 2010-09-15
            • 2015-10-13
            • 1970-01-01
            • 2013-08-30
            • 2017-01-25
            • 1970-01-01
            • 2018-05-19
            相关资源
            最近更新 更多