1

While implementing a script, I am facing the following issue : when putting the multi-line result of a command into a variable, it seems the last (empty) line of my multi-line string disappear.

This line is "empty", but however, I can not lose the carriage return it contains (because I am concatenating blocks of code saved in DB and containing "\n" character into a human-readable string... If I lose some of the "\n", I will lose a part of my code indentation)

Here is the code to illustrate my issue :

test="A

B
";
test2=`echo "$test"`;
echo "||$test2||";

This returns

||A

B||

while I was expecting :

||A

B
||

--> the last (empty) line has disappeared... and a carriage return is thus missing in my human-readable code.

This issue only occurs when the last line of my multi-line string is empty...

Do you know

  • Why this last line disappears ?
  • How I can ensure my last empty line is saved in my multi-line string variable ?

Note that I can of course not use the easiest solution

test2="$test";

because the complete process is rather :

test="^A\n\nB\n^"
test2="`echo "$test" | sed -e 's/\^//g'`";

but I tried to simplify the issue the most I could.

Seba991
  • 13
  • 3
  • Command substitutions always trim trailing newlines -- that's in accordance with design and specification. Otherwise, `foo=$(echo foo)` wouldn't contain the string `foo`, but would have a trailing newline -- that would confuse people. – Charles Duffy Sep 07 '17 at 19:45
  • BTW, which specific version of ksh is this? There are a lot of different, incompatible implementations floating around. – Charles Duffy Sep 07 '17 at 19:46
  • @CharlesDuffy None of the command I tried to find which ksh version I am using worked... I tried "ksh --version", "echo ${.sh.version}" & "echo $KSH_VERSION". If you know other possibilities to identify my ksh version I am very interested ;-) – Seba991 Sep 07 '17 at 20:13
  • What's the operating system? If it has a package manager, we can ask *that* what your ksh is. – Charles Duffy Sep 07 '17 at 20:29
  • @CharlesDuffy : IBM AIX 7.1.0.0 – Seba991 Sep 07 '17 at 20:34
  • Hmm. Some versions of AIX use rpm (allowing the `rpm -q` family of subcommands to be used to check installed package details), but I don't recall which those are. – Charles Duffy Sep 08 '17 at 17:40
  • @CharlesDuffy I think I finally found the version by doing Escape CTRL+V Result : Version M-11/16/88f – Seba991 Sep 08 '17 at 19:22

2 Answers2

2

Command substitutions always trim trailing newlines -- that's in accordance with design and specification. If you don't want that, you can append a fixed sigil character to your output and trim it, such that the newlines you want to preserve are before the sigil:

test="A

B
"
test_wip=$(printf '%sEND' "$test")
test2=${test_wip%END}
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Indeed, with this idea, I can get the result I want test_wip=$(printf '%sENDOFLINE' "$test"); test_wip2="`echo "$test_wip" | sed -e 's/\^//g'`"; test2=${test_wip2%ENDOFLINE}; Let's just hope nobody will use "ENDOFLINE" in the code I am trying to interprete. – Seba991 Sep 07 '17 at 20:01
  • @Seba991 : you can shorten that a bit with `test2="$(echo "${test}"ENDOFLINE | sed -e 's/\^//g')"` ; `test2="${test2%ENDOFLINE}"` – markp-fuso Sep 07 '17 at 20:04
  • @Seba991, well, we're only removing exactly one `ENDOFLINE` (the one at the very end), so it doesn't matter if there *is* an extra literal one in the data. – Charles Duffy Sep 07 '17 at 20:28
  • @markp, modern versions of real ksh (which, granted, it appears the OP doesn't have) optimize `var=$(printf ...)` to be equivalent in efficiency to bash's `printf -v var ...` with no subshell; using `sed` is going to be significantly more expensive. – Charles Duffy Sep 07 '17 at 20:30
  • @CharlesDuffy : ummm, not sure I understand how `printf` can replace `sed` to strip out the `^`'s ... or are you saying `sed` is still required, but a bit of overhead can be eliminated by using `$(printf..)` instead of `$(echo..)` – markp-fuso Sep 07 '17 at 20:37
  • Ahh -- missed the context (of the OP trying to perform a separate operation, and not having a PE in whatever odd ksh they have locally available). – Charles Duffy Sep 07 '17 at 20:47
  • @CharlesDuffy: I understand your comments about `$(printf...)` being more efficient, and thanks for the heads up; but OP has extended the issue to also needing to remove an actual caret (^), but since `${...//^}` is not an option ... `sed` to the 'rescue'; yeah, we're talking past each other :-) – markp-fuso Sep 07 '17 at 20:48
0

Instead of trying to work around the issues that arise from assigning the output from echo to a variable (eg, stripping of trailing \n's), consider using ksh's built in string processing in this case, eg:

$ test="^A\n\nB\n^"
$ test2="${test//^}"
$ echo "||${test2}||"
||A

B
||
  • //^ : remove all ^ characters
markp-fuso
  • 28,790
  • 4
  • 16
  • 36