d3vr10
d3vr10

Reputation: 31

Sed's append command (a) is for some reason deleting all file's text and then appending the provided text

I'm trying with GNU sed command to append a line into a file inside a block/section, specifically just a line under the start of the block/section. As a result it empties the file (maybe leaves a linebreak) and then appends the line in question.

The MAGIC_STRING variable contains the string delimiting the block contents at both ends.

Here's an example script:

MAGIC_STRING='#!#%%%===!!'
DNS_REGISTRY='\ntomato            IN A            222.88.1.2\n'
file=/home/anyuser/file.txt #file exists and has initial content

appended_string=$(sed -n -i "0,/^$MAGIC_STRING/!b; //a $DNS_REGISTRY" $file)
echo "APPENDED: $appended_string"
[[ -n "$appended_string" ]] && echo "Modified file"

The example file's initial contents:

#!#%%%===!!
tomato          IN A            222.88.1.2
#!#%%%===!!

I tried escaping MAGIC_STRING characters and putting "\" at the start of "a" command.

I expected it to match a single MAGIC_STRING and then just append the line below the start of the block if any and ignore all other lines. And, above all to keep file's contents intact.

Also, I expected sed to output to stdout the pattern space after modification, which I tested before... but in this case it simply doesn't for some reason.

Please, don't recommend me awk as an alternative...

Upvotes: 0

Views: 84

Answers (1)

d3vr10
d3vr10

Reputation: 31

Thanks to @Barmar I checked at the -n and -i options once again and reached to the conclusion on why the target file was empty except for the appended text. The -i option redirects sed's pattern space output to the file in a direct fashion without exceptions basically, plus -n which suppresses its automatic output.

This solution consists of writing the last line of the file (it could be anything as signal) to standard output after the text is appended to the file as well as excluding the -n option. Executing sed with sudo (if not root) is also necessary because sed requires root permissions for w (writing) to /dev/stdout.

appended_string=$(sudo -S <<< "$PASSWORD" sed -i -e "0,/^$MAGIC_STRING/!b; //!b ;  //a $DNS_REGISTRY" -e '$w /dev/stdout'  $file)

Both -e options are necessary because the a command escapes all subsequent characters in the script thus making impossible to write further commands, so separating the main script into 2 solves this.

On the other hand, there is another more reliable, neater approach to ensure whether a file was changed by sed or not, which consists of saving to a variable the old file's version, executing sed and then comparing the current file's contents with those of the variable using diff command:

local old_file="$(cat $file)"
sudo -S <<< "$PASSWORD" sed -i -e "0,/^$MAGIC_STRING/!b; //a $DNS_REGISTRY"  $file
                
if diff <(echo -n "$old_file") $file; then 
   echo Failed to add registry to \"$file\" 
else 
   echo 'Success!'; 
fi

echo -n spares the newline at the end of the output and the <() wrapping around it makes possible to refer to the variable's as a file.

Upvotes: 1

Related Questions