I have a string like 1001.2001.3001.5001.6001
or 1001-2001-3001-5001-6001
. How to extract the 4th string i.e., 5001
, add a value like 121
to it and put it back in the same string. The output should be like 1001.2001.3001.5122.6001
or 1001-2001-3001-5122-6001
. I have to achieve this in Linux bash scripting.

- 69,818
- 15
- 125
- 179

- 205
- 2
- 4
- 11
-
3What did you try for yourself? – Inian May 13 '17 at 06:54
-
You should probably check [here](https://stackoverflow.com/a/428118/6381711). – nyedidikeke May 13 '17 at 07:05
-
The bottleneck here is that you've multiple delims.This [\[ answer \]](http://stackoverflow.com/a/39326264/1620779) nicely points a workaround provided you've GNU awk – sjsam May 13 '17 at 08:14
-
Without any attempt it would seem like you just want someone else to do the work for you. This could all be done with simple bash substitution. – grail May 13 '17 at 09:39
5 Answers
Try this
#!/bin/bash
str=$1
if [[ $(echo $str | grep '\.' | wc -l) == 1 ]]
then
str1=$(echo $str | cut -d '.' -f 1,2,3)
str2=$(echo $str | cut -d '.' -f 4 | awk {'print $1+121'})
str3=$(echo $str | cut -d '.' -f 5)
echo $str1.$str2.$str3
elif [[ $(echo $str | grep - | wc -l) == 1 ]]
then
str1=$(echo $str | cut -d '-' -f 1,2,3)
str2=$(echo $str | cut -d '-' -f 4 | awk {'print $1+121'})
str3=$(echo $str | cut -d '-' -f 5)
echo $str1-$str2-$str3
else
echo "do nothing"
fi
Pass a string as parameter

- 1,387
- 3
- 10
- 11
No pipes, no forks, no cutting, no awking, just plain POSIX shell:
$ s=1001.2001.3001.5001.6001
$ oldIFS=$IFS
$ IFS=.-
$ set -- $s
$ case $s in
> (*.*) echo "$1.$2.$3.$(($4 + 121)).$5";;
> (*-*) echo "$1-$2-$3-$(($4 + 121))-$5";;
> esac
1001.2001.3001.5122.6001
$ IFS=$oldIFS

- 69,818
- 15
- 125
- 179
-
Why the downvote? If there's anything wrong, please give me a chance for improvement. – Jens May 14 '17 at 20:57
One liner
value=121 ; str='1001.2001.3001.5001.6001' ; token="$(echo "$str" | cut -f 4 -d '.')" ; newtoken=$(( $token + $value )) ; newstr="$(echo "$str" | sed -e "s/$token/$newtoken/g" | tr '.' '-')" ; echo "$newstr"
Breakdown:
value=121 # <- Increment
str='1001.2001.3001.5001.6001' # <- Initial String
token="$(echo "$str" | cut -f 4 -d '.')" # <- Extract the 4th field with . sep
newtoken=$(( $token + $value )) # <- Add value and save to $newtoken
newstr="$(echo "$str" \
| sed -e "s/$token/$newtoken/g" \
| tr '.' '-')" # <- Replace 4th field with $newtoken
# and translate "." to "-"
echo "$newstr" # <- Echo new string
Works in:
- Bash
- sh
- FreeBSD
- Busybox
Using out of the box tools
If the field separator can either be .
or -
, then do something like
echo "1001.2001.3001.5001.6001" | awk 'BEGIN{FS="[.-]";OFS="-"}{$4+=121}1'
1001-2001-3001-5122-6001
However, if you need to match the regex FS
or field separator with OFS
then you need to have gawk
installed
echo "1001.2001.3001.5001.6001" |
gawk 'BEGIN{FS="[.-]"}{split($0,a,FS,seps)}{$4+=121;OFS=seps[1]}1'
1001.2001.3001.5122.6001

- 21,411
- 5
- 55
- 102
-
@hmedia1 shame?? Why don't you consider this as a possible solution? And I have not even claimed this to be the best solution. – sjsam May 14 '17 at 16:01
Though resetting the argument list with the values is probably the preferred way, or by setting IFS
to the delimiter and reading the values into an array and adding the desired value to the array index at issue, you can also do it with a simple loop to look for the delimiters and continually skipping characters until the desired segment is found (4
in you case -- when the delimiter count is 3
). Then simply appending the digit at each array index until your next delimiter is found will give you the base value. Simply adding your desired 121
to the completed number completes the script, e.g.
#!/bin/bash
str=${1:-"1001.2001.3001.5001.6001"} ## string
ele=${2:-4} ## element to add value to [1, 2, 3, ...]
add=${3:-121} ## value to add to element
cnt=0 ## flag to track delimiters found
num=
## for each character in str
for ((i = 0; i < ${#str}; i++))
do
if [ "${str:$i:1}" = '.' -o "${str:$i:1}" = '-' ] ## is it '.' or '-'
then
(( cnt++ )) ## increment count
(( cnt == ele )) && break ## if equal to ele, break
## check each char is a valid digit 0-9
elif [ "0" -le "${str:$i:1}" -a "${str:$4i:1}" -le "9" ]
then
(( cnt == (ele - 1) )) || continue ## it not one of interest, continue
num="$num${str:$i:1}" ## append digit to num
fi
done
((num += add)) ## add the amount to num
printf "num: %d\n" $num ## print results
Example Use/Output
$ bash parsenum.sh
num: 5122
$ bash parsenum.sh "1001.2001.3001.5001.6001" 2
num: 2122
$ bash parsenum.sh "1001.2001.3001.5001.6001" 2 221
num: 2222
Look things over and let me know if you have any questions.

- 81,885
- 6
- 58
- 85