1

I'm working on an automation script and I need to change the value in a config file. The config file that I'm trying to edit has a line like this (including the spaces before):

    "peer-port": 23456,

The 23456 might be any 5-digit number. The script that I'm writing will pass a new port that is stored in a variable called $newport. How can use a sed (or awk) command to replace "23456", (or whatever it is), with the new port value contained in the variable $newport?

A more-complete sample config file is:

{
    "alt-speed-down": 50, 
    "alt-speed-enabled": false, 
    "alt-speed-time-begin": 540, 
    "alt-speed-time-day": 127, 
    "alt-speed-time-enabled": false, 
    "alt-speed-time-end": 1020, 
    "alt-speed-up": 50, 
    "bind-address-ipv4": "0.0.0.0", 
    "bind-address-ipv6": "::", 
    "blocklist-enabled": false, 
    "blocklist-url": "http://www.example.com/blocklist", 
    "cache-size-mb": 2, 
    "dht-enabled": true, 
    "download-dir": "/var/media/orng/dl/", 
    "download-queue-enabled": true, 
    "download-queue-size": 1, 
    "encryption": 2, 
    "idle-seeding-limit": 30, 
    "idle-seeding-limit-enabled": false, 
    "incomplete-dir": "/var/media/orng/dl//incoming", 
    "incomplete-dir-enabled": true, 
    "lpd-enabled": false, 
    "message-level": 2, 
    "peer-congestion-algorithm": "", 
    "peer-limit-global": 100, 
    "peer-limit-per-torrent": 20, 
    "peer-port": 51413, 
    "peer-port-random-high": 65535, 
    "peer-port-random-low": 49152, 
    "peer-port-random-on-start": false, 
    "peer-socket-tos": "default", 
    "pex-enabled": true, 
    "port-forwarding-enabled": true, 
    "preallocation": 1, 
    "prefetch-enabled": 0, 
    "queue-stalled-enabled": true, 
    "queue-stalled-minutes": 30, 
    "ratio-limit": 0, 
    "ratio-limit-enabled": true, 
    "rename-partial-files": true, 
    "rpc-authentication-required": false, 
    "rpc-bind-address": "0.0.0.0", 
    "rpc-enabled": true, 
    "rpc-password": "{43041796ac49801e85577ba94a0e4ee5642d53a6soamZcAy", 
    "rpc-port": 9091, 
    "rpc-url": "/transmission/", 
    "rpc-username": "", 
    "rpc-whitelist": "127.0.0.1, 192.168.1.*", 
    "rpc-whitelist-enabled": true, 
    "scrape-paused-torrents-enabled": true, 
    "script-torrent-done-enabled": false, 
    "script-torrent-done-filename": "", 
    "seed-queue-enabled": false, 
    "seed-queue-size": 10, 
    "speed-limit-down": 100, 
    "speed-limit-down-enabled": false, 
    "speed-limit-up": 100, 
    "speed-limit-up-enabled": false, 
    "start-added-torrents": true, 
    "trash-original-torrent-files": false, 
    "umask": 18, 
    "upload-slots-per-torrent": 14, 
    "utp-enabled": true, 
    "watch-dir": "/var/media/orng/dl//watch", 
    "watch-dir-enabled": true
}
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Shr00m
  • 11
  • I’m running Ubuntu 18.04 and I’m not set on anything specific like sed or awk. Whatever is easiest. Here is a sample of the config file: https://gist.github.com/yyolk/4747010 – Shr00m Apr 21 '19 at 00:04
  • jq should be available from the Ubuntu universe repository - in which case you should be able to use something like jq --argjson var "$newport" '."peer-port" = $var' settings.json I think – steeldriver Apr 21 '19 at 01:43

4 Answers4

1
jq --argjson port "$newport" '."peer-port" |= $port' file.json >file-new.json

This would produce a new file called file-new.json with the peer-port key's value changed to whatever the shell variable $newport is. The shell variable will be inserted as-is, i.e. without any encoding, so make sure it's a plain integer (using --arg instead of --argjson would insert it as an encoded string).

Kusalananda
  • 333,661
0

Try this test:

sed 's/^    "peer-port": [0-9]*,/    "peer-port": '"${newport}"',/' settings.json

...examine the output, and if it looks good, add an -i to actually change the file like so:

sed -i 's/^    "peer-port": [0-9]*,/    "peer-port": '"${newport}"',/' settings.json

Notes: the substitution strings may need a little tweaking, (i.e. replacing "" with "\t"), depending on whether the whitespace is spaces or tabs.

A more compact and efficient, (but maybe less portable), method:

sed `/^    "peer-port": /{s/ [0-9]*,/ '"${newport}",'/;q}' settings.json
agc
  • 7,223
  • I actually forgot to mention that the port will never be the same. The sed command needs to replace the old port (random 5 digit port) with one stored in $newport – Shr00m Apr 21 '19 at 00:20
  • @Shr00m, See revised answer. – agc Apr 21 '19 at 00:23
  • @agc instead of trying to guess what the leading whitespace is composed of (and the exact number of spaces or tabs) you should be able to use \s*? – Jeff Apr 21 '19 at 00:38
  • 1
    Further suggestion to improve upon @agc answer, this is a little more concise: sed -i -E 's/^(\s*?"peer-port":\ )[0-9]{5},/\1'"$newport"'/' settings.json – Jeff Apr 21 '19 at 00:50
  • @JeffH., Well maybe, but this is a novice question and \s is both harder to explain and not always intuitive. Consider the output of this code echo foo | sed 's/\s*/\t/'; there's no visible leading whitespace in the input, but there is in the output... Also it's uncertain whether or not the OP is running GNU sed; if not then stuff like [0-9]{5} won't work. The lead ^ is an improvement in any case though. – agc Apr 21 '19 at 00:52
  • agc and @Jeff H, I tried both of your suggestions and they worked. However, I'm trying to run this remotely via a bash script. Here is the full line I'm trying to run in my script: /usr/bin/ssh -t user@192.168.4.104 "echo | sudo -S sed -i -E 's/^(\s*?"peer-port":\ )[0-9]{5},/\1'"$newport"'/' /etc/transmission-daemon/settings.json" For some reason the variable isn't being passed when I execute that. I echo the variable out directly below that line and its displaying properly. Thoughts? – Shr00m Apr 21 '19 at 01:47
  • @acg I can run this on the local machine that I'm trying to change the config on and it works: sudo -S sed -i -E 's/^(\s*?"peer-port":\ )[0-9]{5},/\144444,/' /etc/transmission-daemon/settings.json If i put that command in the script I'm trying to execute remotely using SSH it does not work. I don't think this is a forwarding variable issue because hard coding it like I just did does not work AND I can echo the variable that I'm forwarding without problems. I'm stumped... – Shr00m Apr 21 '19 at 03:09
  • @agc thank you for the link. I ended up putting all the commands in a bash script on the remote server and am passing the $newport variable into that script via SSH as an argument. All working good now!!! – Shr00m Apr 21 '19 at 13:06
0

Generally, you do not need the whole string to make a pattern for a sed search string, you just need a unique part of it, and "(peer-port.+) ([0-9]+)" looks perfectly sufficient for the case, sort of find the keyword with a little tail for it then then sequence of digits and replace all that with first thing unchanged(\1) and second thing using the string in a script.

#!/bin/bash

FNAME=$1
NPORT=$2
SSTR='{s/(peer-port.+) ([0-9]+)/\1 '$NPORT'/};'
echo "new port $NPORT in $FNAME"

sed -E -i -e "$SSTR" $FNAME
MolbOrg
  • 710
  • 7
  • 11
0

Parsing nested data formats (like json) must be done with respective (json format aware) utilities. sed is a line only aware. Here's an easy solution based on walk-path unix utility jtc:

bash $ newport=12345
bash $ jtc -w[peer-port] -u"$newport" -f file.json 
bash $ jtc -w[peer-port] file.json 
12345
bash $ 
Dmitry
  • 1