2

I am using a script to obtain the last n-2 lines of a file using a command and then using echo to print this lines into a file.

last_n2_lines=`tail -n+3 $file`
echo "$last_n2_lines" >> $file

This echo here removes a trailing new line from the end of file. FYI

tail -n+3 file > file.new

is working perfectly well.

user000001
  • 32,226
  • 12
  • 81
  • 108
prathmesh.kallurkar
  • 5,468
  • 8
  • 39
  • 50
  • Are you asking a question, or just noticing? :) – robertklep Feb 10 '13 at 09:30
  • possible duplicate of [How to avoid bash command substitution to remove the newline character?](http://stackoverflow.com/questions/15184358/how-to-avoid-bash-command-substitution-to-remove-the-newline-character) – user000001 Apr 09 '13 at 13:56

2 Answers2

3

From the Manual: http://www.gnu.org/software/bash/manual/bashref.html#Command-Substitution

3.5.4 Command Substitution

Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed as follows:

$(command)

or

`command`

Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during word splitting. The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).

When the old-style backquote form of substitution is used, backslash retains its literal meaning except when followed by ‘$’, ‘`’, or ‘\’. The first backquote not preceded by a backslash terminates the command substitution. When using the $(command) form, all characters between the parentheses make up the command; none are treated specially.

Command substitutions may be nested. To nest when using the backquoted form, escape the inner backquotes with backslashes.

If the substitution appears within double quotes, word splitting and filename expansion are not performed on the results.

Emphasis mine. So it is not a bug. It is a feature.

Community
  • 1
  • 1
user000001
  • 32,226
  • 12
  • 81
  • 108
  • how should i prevent this removal of trailing new line ?? I tried last_n2_lines=$(tail -n +3 $file; echo -n x) last_n2_lines="${last_n2_lines%x}" but this is not working – prathmesh.kallurkar Feb 10 '13 at 09:55
  • A workaround would be `echo -e "\n" >> $file` but that applies only if you know that a newline is present. – user000001 Feb 10 '13 at 10:00
2

You could preserve the trailing empty lines by using readarray to save the data, and printf for output.

Something like:

readarray last_n2_lines < <(tail -n+3 $file)
printf "%s" "${last_n2_lines[@]}" > ${file}.new

Demo:

$ cat test; echo end 
1
2
3
4


end
$ tail -n+3 test ; echo end
3
4


end
$ readarray data < <(tail -n+3 test)
$ printf "%s" "${data[@]}" ; echo end  
3
4


end

Explanation:

  • readarray data reads lines from standard input into the array called data.
  • cmd1 < <(cmd2 param1 param2 ...) redirects the output of cmd2 param1 param2 ... to the standard input if cmd1. Careful with the syntax: it must be < [space] <(...), no space between <(, space required between the two < <.

So after the first line, data contains all the lines that tail -n+3 output. Each item in the array is a line of the input with the line terminator included.
Here's how you access each element. Notice that two lines are printed for each item defined in the array: the one in the array (which contains a newline), and the newline added by echo.

$ echo "${data[0]}"
3

$ echo "${data[1]}"
4

$ echo "${data[2]}"


$ echo "${data[3]}"


$ echo "${data[4]}"  # out of bounds

$ 
  • ${data[@]} expands to all elements of the array data
  • printf "%s" param1 param2 ... prints all of its arguments unchanged (i.e. doesn't add or remove line terminators).

So the second statement prints back everything that was read in, unchanged.

Mat
  • 202,337
  • 40
  • 393
  • 406
  • 1
    Added a demo. Please always remember: "not working" is not a useful thing to say. Describe how it is not working. – Mat Feb 10 '13 at 11:35
  • Oh sorry for the vagueness ... Actually I was doing echo on the array variable. On trying printf it works perfectly. Thanks – prathmesh.kallurkar Feb 10 '13 at 11:52