3

I tried to write a code that which rotates each line in a text file. For example, given the next line:

a b c

the output will be:

c b a

this script get as argument only one argument - the name of the text file. In addition, I want to do it so will be signficance to extra spaces. namely, given the next line:

a b c

the output will be:

c b a

comment: the output will be in a new file with same name just with suffix of .rotate.

My code:

#!/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}

the problem:
I don't know why, but, given this line:

a b c

the output is:

c b a

from where is the extra space?

COMMENT: While I checked the rotate array letter-by-letter, it's Ok (without extra spaces) - But, while I print it with "${rotate_line[@]}" it's adds a new spaces.. why?

9
  • 1
    What about rev file? Commented Mar 4, 2018 at 16:58
  • I am trying to implement this command myself Commented Mar 4, 2018 at 16:59
  • while IFS= read -r line; do for ((i=${#line};i>0;i--)); do echo -n "${line:$i-1:1}"; done; echo; done < file? Commented Mar 4, 2018 at 17:06
  • 1
    @Jor.Mal: I was short before answering with "use rev" too. You should point out in your post, preferably in the title, that you like to implement rev yourself, because everybody reading it will think immediately in that direction. And please don't use those archaic backtics from the 80ies. Commented Mar 4, 2018 at 18:37
  • 1
    @userunknown in the else part I used with >> operator that don't delete the old file. and > operator delete the old file. (If before of the running exists file with this name I want to delete its) Commented Mar 4, 2018 at 18:48

3 Answers 3

3

The echo ${rotate_line[@]} places a separator between each of the elements of the array.

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}

In this debugging friendly fashion, we can echo single lines or do

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

Fast testing leads to fast answers.

Since we put out the array char by char, we could have done so, while reading the line backwards and thereby getting rid of the entire 2nd array, but you wanted to know, what didn't work in your approach, which would have been obscured, if I had refactored the code further.

Sign up to request clarification or add additional context in comments.

6 Comments

Any particular reason you like let over POSIX ((..)) for post-increment operations? (I guess it does save a keystroke from a typing perspective)
@DavidC.Rankin: No. I never use them in my scripts. I tried to ask the question, why the script doesn't work. I only modified the most hurtful part (backticks) for me, beside what was being asked for, and what I needed for fast testing (without files), so that the difference is easy to spot. I would use rev but I totally understand the need in doing exercises. And the question was, why it doesn't work, and let wasn't the reason. But I have to confess, that I'm not in the church of POSIX, but in the unconcious bash-movement, since I'm not an admin.
Oh, I know that, it was just a curiosity looking over your script. I don't mind let, but generally haven't used it for the past 15 years, so it does tend to stick out. (I blame the authors of the ancient Adv Bash Scripting Guide for its current use)
I read some advanced bash scripting guide, about 10y ago, was the name of the author Cooper? Or Scott? I don't remember a let there, and I'm not sure what I picked up there. I think $(...) instead of backticks, and the multiple variants of ${foobar} which I now lookup in the bash man page. (In my last comment, I wanted to claim, that "I tried to answer the question", not "ask" - pardon. Maybe too many open tabs.)
Any particular reason to recommend a useless use of cat?
|
0

If you use declare -p rotate_line, you will see the array actually contains the expected values (without extra spaces):

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

However, in your code you are using echo "${rotate_line[@]}" > file to print out the array to the file. That expands to echo <item1> <item2> ... and the default behavior of echo is to output these arguments separated by a space.

Some possible work-arounds:

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

There are several other issues with your code, for example while read line. That might delete leading and trailing spaces and it will mangle backslashes. Use while IFS= read -r line instead.

7 Comments

hye. Can you explain please what it's mean "using array as a string joins each element by a space and that is where the extra spaces comes from" ?
@Jor.Mal sorry, made a mistake. Check my answer now.
@Jor.Mal Try one of the two work-arounds I presented, they might do exactly what you need :)
thank you. by the way, given this line: c (c[space][space]...[space]) what can I do so that line will get the extra spaces?
@Jor.Mal That's a completely different issue and it's a problem with your read command which trims leading and trailing spaces by default. Use this instead: IFS= read -r line.
|
0

Using pure :

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

Output :

cba

5 Comments

Can you explain what is the problem in my code please?
I get c b a ina rotate file, isn't what you expected ?
Yes, it is. But I don't understand what is the problem in my code (that I writed in my question)
and I am sorry, you need to get cba (without spaces, becuase that in x don't exists spaces)
Ok, and you can explain please what is the problem in my code? the rotate array is fix (but the print with echo adds spaces... WHY?)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.