Skip to main content

More elegant approach to append text to a file only if the String doesn't exist? [Resolved]

I want to append NoDisplay=true to a .desktop file, but only if the entry doesn't exist. I do this the following way:

grep -q 'NoDisplay=true' '/usr/share/applications/yelp.desktop' || bash -c 'echo "NoDisplay=true" >> /usr/share/applications/yelp.desktop'

I was wondering if there is a shorther oneliner for the same operation?

I use this command in a bash script and had to use command "bash -c".

Question Credit: Kenth Kvien
Question Reference
Asked July 18, 2019
Posted Under: Unix Linux
4 Answers

Using sed, based off this answer:

sed -i "/^NoDisplay=true\$/h;\${x;/^\$/{s//NoDisplay=true/;H};x}" '/usr/share/applications/yelp.desktop'

Broken down without escaping $s:

# Store the line in the hold space if found

# At the end of the file
    # Switch to the hold space
    # If it's empty
        # Replace the line
        # Store it in the hold space
    # Switch to the hold space

It's possible to do in awk, but since there's no inplace editing before GNU 4.1.0, you'd essentially be doing the same thing as grep+echo.

credit: Dabombber
Answered July 18, 2019

I think the only possibly inelegant things in your approach are the use of a bash -c, which is not required, and the redundancy of the literal strings you used for the pattern and the filename, which are in fact what make your one-liner long, and which you could instead put in variables (perhaps short-named) to be used like in:

p='NoDisplay=true' f='/usr/share/applications/yelp.desktop'; grep -q "${p}" "${f}" || echo "${p}" >>"${f}"

You could make this a bit more compact by playing with redirections, just to avoid specifying the filename twice, hence avoiding one variable, like in:

p='NoDisplay=true'; (grep -q "${p}" || echo "${p}" >&0) <>/usr/share/applications/yelp.desktop

But whether that is actually elegant is probably a matter of preference.

Anyways, the pattern still needs to be specified twice, although through variables. I can't imagine ways where you could avoid that.

Yet another variant with sed, which would save only a few keystrokes more, could be:

p='NoDisplay=true'; sed -n "/${p}/q;\$a${p}" <>/usr/share/applications/yelp.desktop >&0

Requires GNU sed (typically present on Linux) due to the a command immediately followed by the text to be appended (the pattern's variable in our case).

The same for POSIX sed would be:

p='NoDisplay=true'; sed -n "/${p}/q;\$a\\
" <>/usr/share/applications/yelp.desktop >&0

credit: LL3
Answered July 18, 2019
Your Answer