3

I have the following Java properties file that is maintained by humans.

# file.input = /very/old/name
# file.input = /old/name
file.input = /current/name
file.input.default = /default/name
other.file.input = /current/other/name
# Other comments
other.properties = must stay the same

I must create a script that will edit this file without touching at all at the rest of the files. Therefore I cannot dump new properties in the config file.

Humans will often copy/paste a line and comment it for further use. My application must edit exclusively config file without touching any of the comments.

In this particular case, I want to edit the line file.input to set the value to /new/name without any commented line being touched and without any other properties being touched as well (file.input.default, other.file.input). My script will not keep the current line for history purpose like humans usually do. Also, I don't know the current value set to the key file.input.

Where am I stuck? At the very beginning. I'm not a Bash expert, I rarely use it except to call commands in sequence and I use some ifs sometimes. But that's all.

I could use perl to use regular expressions, but perl is apparently not installed on this system. So I turned to sed, in turn, but I don't get any of this: it seems I can only replace a specific value to another. I could wrap that in the reading of the file, and rewrite it except for the expected line that I could change. Also, I would barely be able to handle human-added spaces in front of the property I want to change.

So if you have an alternative please share it, because I start to become alienated.

6
  • Your profile says “JavaEE and Python developer.” So why not Python?
    – manatwork
    Apr 8, 2014 at 9:10
  • Because modifying this file is only one part of a much more lengthy script. And while I write it, will not maintain it. My sysadmins will do that. It's a requirement for them to know and use unix commands and be able to edit a bash script, most of them don't know python. Also, bash is a requirement on the servers we install and use, but python is not. So while it is present on the very system I will adapt, I'm not sure it will be if the config file and the script I'm writing are migrated to another system. Apr 8, 2014 at 9:15
  • Is there any chance the count of uncommented “file.input” entries to not be 1? Especially I'm asking whether it can be missing, in which case the script has to add it.
    – manatwork
    Apr 8, 2014 at 9:15
  • @manatwork: no, there is only one uncommented file.input. Unless a human error occurs. But I shouldn't take that case in account. Apr 8, 2014 at 9:17
  • first off, if you want to preserve comments, you probably cannot do this without a parser. second, I would ask your saysadmins for help. you're required to use something that they know, and no one knows exactly what they know more than them. if that made sense.
    – strugee
    Apr 8, 2014 at 10:47

3 Answers 3

5

Using the -i option of sed (which edits inplace). The caveat is the character used to delimiter the substitution command must not appear in the NEW string. The example uses ':' rather than '/' to delimit (s:old_string:new_string:)

NEW=/new/name    
sed -i 's:^[ \t]*file.input[ \t]*=\([ \t]*.*\)$:file.input = '${NEW}':' f
2
  • 1
    You do not need to use [ ]* to match just spaces, a simple ` *` would do. Of course [ \t]* would probably be better, to catch tabs, too.
    – Adaephon
    Apr 8, 2014 at 9:23
  • This is just a bit more complex than the solution with awk, but I choose it, because you understood that /new/name is a dummy name and will actually be a variable, and you provided a solution with that in mind. Apr 8, 2014 at 9:47
2

An solution using awk:

$ awk -F'=' '$1 ~ /^[ \t]*file.input[ \t]*$/{$2="= /new/name"}{print}' config
# file.input = /very/old/name
# file.input = /old/name
file.input = /new/name
file.input.default = /default/name
other.file.input = /current/other/name
# Other comments
other.properties = must stay the same

Then you can save it to new config file:

awk -F'=' '$1 ~ /^\s*file.input\s*$/{$2="= /new/name"}{print > "new_config"}' config

2
  • I executed the exact same command (copy/pasted), only changing the final config to config.properties. It did good. Except that running this command several times in a row will concat to new_config everytime. For a +1, you might want to change >> "new_config" to > "new_config". Apr 8, 2014 at 9:45
  • Yeap, I use >> to test, mistyping, fixed!
    – cuonglm
    Apr 8, 2014 at 9:50
1

Note if you need this in a script to work cross platform (to work on MacOSX too) then you need to use the complete -i flag, supplying a backup file extension:

NEW=/new/thing
sed -i.backup 's|\s*file.input\s*=.*$|file.input = '${NEW}'|' filename

## Then you can check exit code and remove the backup file if all is well
## rm filename.backup

Otherwise on MacOSX you'll get an error something like "extra characters at end of command".

1
  • You can instead do sed -i '' ... to prevent creating the backup file on OSX.
    – Alex
    May 12 at 9:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .