2

I have a two-line "keyword=keyvalue" line pattern (selectively excised from systemd/networkd.conf file):

DNS=0.0.0.0
DNS=

and need the following 2-line answer:

0.0.0.0

But all attempts using sed or awk resulted in omitting the newline if the last line pattern matching resulted in an empty match.

EDIT: Oh, one last thing, this multiline-follow-cut result has to be stored back into a bash variable containing this same 'last blank-line" as well, so this is a two-step operation of preserving last-blank-line

  1. multiline prepending-cut-out before (or save content after) the equal = symbol while preserving a newline ... in case of an empty result (this is the key here). Or possibly jerry-rig a weak fixup to attach a new-line in case of an empty match result at the last line.
  2. save the multi-line result back into a bash variable

sed Approach

When performing cut up to and include that matched character in bash shell, the sed will remove any blank lines having an empty pattern match:

raw="DNS=0.0.0.0
DNS=
"
rawp="$(printf "%s\n" "$raw")"

kvs="$(echo "$rawp"| sed -e '/^[^=]*=/s///')"

echo "result: '${kvs}'"

gives the result:

0.0.0.0

without the corresponding blank line.

awk Approach

Awk has the same problem:

raw="DNS=0.0.0.0
DNS=
"
rawp="$(printf "%s\n" "$raw")"

kvs="$(echo "$rawp"| awk -F '=' -v OFS="" '{$1=""; print}')"

echo "result: '${kvs}'"

gives the same answer (it removed the blank line).

Please Advise

Somehow, I need the following answer:

0.0.0.0

in form of a two-line output containing 0.0.0.0 and a blank line.

Other Observations Made

I also noticed that if I provided a 3-line data as followed (two with a keyvalue and middle one without a keyvalue:

DNS=0.0.0.0
DNS=
DNS=999.999.999.999

Both sed and awk provided the correct answer:

0.0.0.0

999.999.999.999

Weird, uh?

The above regex (both sed and awk) works for:

  • a one-line with its keyvalue,
  • any-line provided that any lines have its non-empty keyvalue, BUT
  • last line MUST have a keyvalue.

Just doesn't work when the last-line has an empty keyvalue.

:-/

John Greene
  • 2,239
  • 3
  • 26
  • 37

1 Answers1

2

You can use this awk:

raw="DNS=0.0.0.0
DNS=
"

awk -F= 'NF == 2 {print $2}' <<< "$raw"
0.0.0.0

Following cut should also work:

cut -d= -f2 <<< "${raw%$'\n'}"
0.0.0.0


To store output including trailing line breaks use read with process substitution:

IFS= read -rd '' kvs < <(awk -F= 'NF == 2 {print $2}' <<< "$raw")

declare -p kvs
declare -- s="0.0.0.0

"

Code Demo:

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Weird. The answer technically produces two-lines (as checked using the `| wc -l` line-counter pipe. But I am unable to store that back into a bash variable this said-missing last blank line. Qualified the question further to state "... and store back into a bash variable." – John Greene Mar 06 '22 at 12:31
  • ok check my updated answer now – anubhava Mar 06 '22 at 12:53
  • 1
    Testing that now; Apparently, I've been impacted by a command substitution limitation with regard to last newline via `$(awk ...)`. I just saw this snippet over at https://stackoverflow.com/a/5322980/4379130 And it is a lot cleaner than delving into `declare/read` complex-combo territory: `RESULTX="$(./myscript; echo x)" RESULT="${RESULTX%x}"` If you agree, I'll accept the answer. (still testing your original approach). – John Greene Mar 06 '22 at 12:59
  • 2
    Putting a character in the end and removing it is a work around but using `read -d ''` with *process substitution* is proper and standard way of getting it done. – anubhava Mar 06 '22 at 13:11
  • 1
    And ye be absolutely right; not only that, the variable is POSIX-compliant too. – John Greene Mar 06 '22 at 13:22
  • Can you add a corresponding first step using `grep -E` as a curiosity, if POSIXively possible? – John Greene Mar 06 '22 at 16:14
  • `grep` is not the right tool for this. `cut` or `awk` will do better job as shown in answer. – anubhava Mar 06 '22 at 16:30