【问题标题】:Reverse of any line in a text file (implement of rev command)反转文本文件中的任何行(rev 命令的实现)
【发布时间】:2018-08-12 08:49:29
【问题描述】:

我试图编写一个代码来旋转文本文件中的每一行。例如,给定下一行:

a b c

输出将是:

c b a

这个脚本只得到一个参数——文本文件的名称。此外,我想这样做对额外的空间意义重大。即,给定下一行:

a b c

输出将是:

c b a

注释:输出将在一个同名的新文件中,只是后缀为.rotate

我的代码:

#!/bin/bash

name_of_file=$1

first_step=1

while read line; do
    length_of_line=`echo -n "${line}" | wc -c`
    rotate_line=()
    index_of_line=0
    index_of_rotate=$(( length_of_line - 1 ))
        while (( ${index_of_line} < ${length_of_line} )); do
        rotate_line[${index_of_rotate}]="${line[@]:${index_of_line}:1}"
        let index_of_line++
        let index_of_rotate--
        done

        if (( ${first_step} == 1 )); then
            echo "${rotate_line[@]}" > $1.rotate1
            first_step=0
        else
            echo "${rotate_line[@]}" >> $1.rotate1
        fi
done < ${name_of_file}

问题:
我不知道为什么,但是,鉴于这一行:

a b c

输出是:

c b a

多余的空间从哪里来?

评论:当我逐字母检查旋转数组时,没关系(没有额外的空格) - 但是,当我用 "${rotate_line[@]}" 打印它时,它添加了一个新的空格.. 为什么 ?

【问题讨论】:

  • rev file 呢?
  • 我正在尝试自己实现这个命令
  • while IFS= read -r line; do for ((i=${#line};i&gt;0;i--)); do echo -n "${line:$i-1:1}"; done; echo; done &lt; file?
  • @Jor.Mal:在回答“use rev”之前我也很简短。您应该在帖子中(最好是在标题中)指出您喜欢自己实现 rev,因为每个阅读它的人都会立即朝这个方向思考。并且请不要使用 80 年代的那些过时的回溯。
  • @userunknown 在我与 &gt;&gt; 运算符一起使用的 else 部分中,不会删除旧文件。和&gt; 操作员删除旧文件。 (如果之前运行的存在同名文件我想删除它)

标签: bash shell scripting


【解决方案1】:

echo ${rotate_line[@]} 在数组的每个元素之间放置一个分隔符。

while read line; do
    // get rid of old-fashioned backticks, we're sick of explaining.
    length_of_line=$(echo -n "${line}" | wc -c)
    rotate_line=()
    index_of_line=0
    index_of_rotate=$(( length_of_line - 1 ))
    while (( ${index_of_line} < ${length_of_line} ));
    do
        rotate_line[${index_of_rotate}]="${line[@]:${index_of_line}:1}"
        let index_of_line++
        let index_of_rotate--
    done
    // output characterwise
    for i in $(seq 0 $((length_of_line - 1)))
    do
        // deactivated file-in and out for easy debugging 
        echo -n "${rotate_line[i]}" # >> $1.rotate1
    done
    // for multiple lines, we need an echo here:
    echo
done # < ${name_of_file}

以这种调试友好的方式,我们可以回显单行或做

cat file | nih-rev.sh > file.rotate1 

快速测试带来快速答案。

由于我们逐个字符地输出数组,我们可以这样做,同时向后读取行,从而摆脱整个第二个数组,但是您想知道,您的方法中什么不起作用,哪个如果我进一步重构代码,就会被掩盖。

【讨论】:

  • 您喜欢 let 而不是 POSIX ((..)) 进行后增量操作的任何特殊原因? (我想它确实从打字的角度节省了一次击键)
  • @DavidC.Rankin:不。我从不在我的脚本中使用它们。我试图问这个问题,为什么脚本不起作用。我只修改了对我最有害的部分(反引号),除了被要求的内容,以及快速测试所需的内容(没有文件),因此很容易发现差异。我会使用rev,但我完全理解做练习的必要性。问题是,为什么它不起作用,let 不是原因。但我必须承认,我不是 POSIX 的教会,而是无意识的 bash 运动,因为我不是管理员。
  • 哦,我知道,只是出于好奇查看您的脚本。我不介意let,但在过去的 15 年里通常没有使用它,所以它确实很突出。 (我责怪古代 Adv Bash 脚本指南的作者当前的使用)
  • 大约 10 年前,我阅读了一些高级 bash 脚本指南,作者是 Cooper 的名字吗?还是斯科特?我不记得那里有let,我不确定我在那里捡到了什么。我认为 $(...) 而不是反引号,以及我现在在 bash 手册页中查找的 ${foobar} 的多个变体。 (在我的最后一条评论中,我想声称“我试图回答这个问题”,而不是“问”——请原谅。可能打开的标签太多了。)
  • 推荐useless use of cat?的任何特殊原因
【解决方案2】:

如果你使用declare -p rotate_line,你会看到数组实际上包含了预期的值(没有额外的空格):

declare -a rotate_line='([0]="c" [1]=" " [2]="b" [3]=" " [4]="a")'

但是,在您的代码中,您使用echo "${rotate_line[@]}" &gt; file 将数组打印到文件中。扩展为echo &lt;item1&gt; &lt;item2&gt; ...echo 的默认行为是输出这些参数用空格分隔

一些可能的解决方法:

(IFS=; echo "${rotate_line[*]}") > file
printf '%s' "${rotate_line[@]}" $'\n' > file

您的代码还有其他几个问题,例如while read line。这可能会删除前导和尾随空格,并且会破坏反斜杠。请改用while IFS= read -r line

【讨论】:

  • 嘿。您能否解释一下“使用数组作为字符串将每个元素通过空格连接起来,这就是额外空格的来源”是什么意思?
  • @Jor.Mal 抱歉,弄错了。现在检查我的答案。
  • @Jor.Mal 尝试我提出的两种解决方法之一,它们可能完全符合您的需要:)
  • 谢谢。顺便说一句,鉴于这一行:c (c[space][space]...[space]) 我该怎么做才能让line 获得额外的空格?
  • @Jor.Mal 这是一个完全不同的问题,这是您的read 命令的问题,它默认修剪前导和尾随空格。改用这个:IFS= read -r line.
【解决方案3】:

使用纯

x='abc'
for ((i=${#x}; i>0; i--)); do echo -n ${x:$i:1}; done

输出:

cba

【讨论】:

  • 你能解释一下我的代码有什么问题吗?
  • 我收到 c b a 旋转文件,这不是您所期望的吗?
  • 是的。但我不明白我的代码有什么问题(我在我的问题中写的)
  • 对不起,你需要得到cba(没有空格,因为x中不存在空格)
  • 好的,你能解释一下我的代码有什么问题吗?旋转数组是固定的(但带有echo 的打印添加了空格......为什么?)
猜你喜欢
  • 2010-09-29
  • 2010-11-16
  • 2015-02-06
  • 2016-09-06
  • 1970-01-01
  • 2013-02-04
  • 2016-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多