简体   繁体   中英

Bash Scripting: Replace (or delete) string in a file if line starts with (or matches) another string

Assuming an ini-style file like this,

[Group]
Icon=xxx.ico
Title=An Image Editor
Description=Manipulates .ico, .png and .jpeg images

I want to replace/delete ".ico" ONLY in the line that starts with (or matches) "Icon="

I was trying this:

oldline="`cat "$file" | grep "Icon="`"
newline="`echo "$oldline" | tr ".ico" ".png"`"
cat "$oldfile" | tr "$oldline" "$newline" > $file

Then i realized that tr works completely different than i thought. Its NOT a tradicional "replace this for that" function. So i guess the correct way is using sed . But:

  • Ive never used sed before. No idea how it works. Is it overkill?
  • If the most indicated way is really using sed , given it is so powerful, is there any elegant way to accomplish this rather than this "fetch line -> modify line -> replace oldline for newline in file" approach?

Notes:

  • I cant replace ".ico" globally, i know that would be a LOT easier, i must restrict the replace to the Icon line, otherwise the Description line would be changed too.
  • Im new to shell scripting in Linux, so im looking not only to the solution itself, but also for the "proper" way to do it. Elegant, easy to read, conventional, etc

Thanks in advance!

Edit:

Thank you guys! Here is the final script, as a reference:

#! /bin/bash
# Fix the following WARNING in ~/.xsession-errors
# gnome-session[2035]: EggSMClient-WARNING: Desktop file '/home/xxx/.config/autostart/skype.desktop' has malformed Icon key 'skype.png'(should not include extension)

file="$HOME/.config/autostart/skype.desktop"

if [ -f "$file" ] ; then
    if `cat "$file" | grep "Icon=" | grep -q ".png"` ; then
        sed -i.bak '/^Icon=/s/\.png$//' "$file"
        cp "$file" "$PWD"
        cp "${file}.bak" "$PWD"     
    else
        echo "Nothing to fix! (maybe fixed already?)"
    fi  
else
    echo "Skype not installed (yet...)"
fi

MUCH sleeker than my original! The only thing i regret is that sed backup does not preserve original file timestamp. But i can live with that.

And, for the record, yes, ive created this script to fix an actual "bug" in Skype packaging.

Something like the following in sed should do what you need. First we check if the line starts with Icon= and if it does then we run the s command (ie substitute).

sed -i '/^Icon=/s/\.ico$/.png/' file

Edit: The sed script above can also be written like this:

/^Icon=/ {             # Only run the following block when this matches
    s/\.ico$/.png/     # Substitute '.ico' at the end of the line with '.png'
}

See this page for more details on how to restrict when commands are run.

sed is pretty easy to deal with. Here's one way:

sed 's/^\(Icon=.*\)\.ico$/\1.png/'

By default, sed works on every line in the file one at a time. The 's/.../.../' will do a regular expression match on the first argument and replace it with the second argument. The \\1 stands for everything that matched the first group, which is demarcated by the parenthesis. You have to escape the parens with \\.

The above works as part of a pipeline, but you can add an '-i' flag, like this

sed -i 's/^\(Icon=.*\)\.ico$/\1.png/' input.txt

to have it replace the file input.txt in place. Don't add that until you have tested your sed script a little.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM