2

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.

  • Your profile says “JavaEE and Python developer.” So why not Python? – manatwork Apr 8 '14 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. – Olivier Grégoire Apr 8 '14 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 '14 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. – Olivier Grégoire Apr 8 '14 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 '14 at 10:47
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
  • 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 '14 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. – Olivier Grégoire Apr 8 '14 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

  • 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". – Olivier Grégoire Apr 8 '14 at 9:45
  • Yeap, I use >> to test, mistyping, fixed! – cuonglm Apr 8 '14 at 9:50
0

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".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.