2

I want to read lines from a file and append string to it. But it's not appending correctly.

#! /bin/bash

FILE=$1 while IFS= read -r line do line2="${line} &" echo $line2 done <$FILE

My input file contains

message
sample
text

expected output

message &
sample &
text &

But after executing script i am getting below output.

 &ssage
 &mple
 &xt

Why it's not appending string at the end ?

3 Answers3

1
 &ssage
 &mple
 &xt

The problem is your input file (apparently created in Windows) has CRLF line endings.

Fix it with sed -i 's/\r//' file

fuzzydrawrings
  • 1,656
  • 5
  • 12
0

I cannot reproduce this.

I just ran it locally, and also made a version you can easily try on https://www.onlinegdb.com/online_bash_shell.

while IFS= read -r line
do
    line2="${line} &"
    echo $line2
done <<EOM
message
sample
text
EOM

Output

message &
sample &
text &
mpe
  • 106
0

Your issue is that the input text file is a DOS text file. This means that each line ends with a carriage-return character. When outputted, this character brings the cursor back to the start of the line, which means that when echo outputs the space before &, it will be written to the beginning of the line.

You should convert the text file to Unix text format using a tool like dos2unix.

You also have issues with quoting in your shell code when you output the value of $line2. Since you do not quote the variable expansion, the shell will split its value on spaces, tabs, and newlines to generate words. These words will also undergo filename globbing before being passed as individual arguments to echo. This means that any tab or set of more than one consecutive space in your input would be converted to single spaces and that any globbing pattern in the input that happens to match one or more existing filenames would be replaced by those filenames. Try, for example, with a line of input with a single * on it.

The echo utility has a tendency to, under some circumstances, modify the data that it outputs (expanding \n into literal newlines, etc.), so if you want to preserve the literal data of the input, you should be using printf instead.

Reading text line by line in a shell loop is also a bit inefficient, and your task is better performed by tools such as sed,

sed 's/$/ \&/' <"$1"

(replaces the end of the line with a space and an ampersand)

... or awk,

awk '{ printf "%s &\n", $0 }' <"$1"

(outputs each line with space and & added; could also have used print $0, "&").

See also:

Kusalananda
  • 333,661
  • See also gawk -v RS='\r?\n' -v ORS=' &\n' 1 to convert CRLF or LF line delimiters to "& LF" ones – Stéphane Chazelas Jan 25 '22 at 08:20
  • "each line ends with a carriage-return character" - that is not correct, each line ends with a line-feed character preceded by a carriage-return character. CRLF. You also don't need to install dos2unix for the simple task of removing carriage-returns from a text file. – fuzzydrawrings Jan 25 '22 at 19:18
  • @fuzzydrawings 1) Each line of a DOS text file, as interpreted on a Unix system, does end with a carriage-return character. There is a carriage-return, and then the line-feed, and the line-feed constitutes the end of the line, but is not part of the line (a delimiter is not part of the data that it delimits). Any utility on any Unix system that reads lines of text will read strings ended by carriage-returns when reading a DOS-formatted text file. – Kusalananda Jan 25 '22 at 20:36
  • @fuzzydrawings 2) Yes, it's easy to remove all carriage-returns with tr or any other simple tool, but dos2unix was developed for that task. By using dos2unix, you are guaranteed that any carriage-return that you wouldn't expect to remove, isn't removed. This is the utility that is made for converting text files from DOS or Windows systems for use on Unix. The tool also automatically converts the text from common DOS/Windows encodings to encoding more commonly used on Unix. – Kusalananda Jan 25 '22 at 20:39