3

I have string like

name1::1.1.1.1::ps -ax

I want to split the string based on delimiter :: using bash scripting.

The desired output should be an array of 3 elements

("name1" "1.1.1.1" "ps -ax")

without double quotes

I appreciate your help.

hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • Check [this](https://stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash) – kip Sep 20 '17 at 23:45
  • Possible duplicate of [How do I split a string on a delimiter in Bash?](https://stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash) – Will Barnwell Sep 20 '17 at 23:51
  • 4
    One difference: those links, which both point to the same question, apply to the use of a single character as a delimiter. The question here uses two-characters for the delimiter. – John1024 Sep 20 '17 at 23:58
  • Please clarify whether the data can contain _single_ `:`s, for example `ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm`. – agc Sep 21 '17 at 08:50

4 Answers4

5

Assuming there are no :s in the array data, use bash pattern substitution to squeeze the :: to : while assigning the string to $array, then show the whole array, then just element #2:

a="name1::1.1.1.1::ps -ax"
IFS=: array=(${a//::/:}) ; echo ${array[@]} ; echo "${array[2]}" 

Output:

name1 1.1.1.1 ps -ax
ps -ax

But what if there are :s in the array data? Specifically in the third field, (the command), and only in that field. Use read with dummy variables to absorb the extra :: separators:

a="name1::1.1.1.1::parallel echo ::: 1 2 3 ::: a b"
IFS=: read x a y b z <<< "$a"; array=("$x" "$y" "$z"); printf "%s\n" "${array[@]}"

Output:

name1
1.1.1.1
parallel echo ::: 1 2 3 ::: a b
agc
  • 7,973
  • 2
  • 29
  • 50
  • 1
    But then the values cannot contain any single colons, which I imagine was the reason the OP chose double colons in the first place. You could replace double colons with something unlikely like `$'\xff'` though. `IFS=$'\xff' array=(${a//::/$'\xff'})` – tripleee Sep 21 '17 at 04:32
  • @tripleee, Instead of imagining the OP's reason, let's [ask](https://stackoverflow.com/questions/46333288/bash-split-a-string-by-delimiter-ignoring-spaces/46334343?noredirect=1#comment79638591_46333288). Also the `\xff` still wouldn't work correctly if `${array[2]}` were a command like `parallel --link diff {1} {2} :::: manifest1 checklist1`. – agc Sep 21 '17 at 08:58
  • If the commands contain double colons then obviously double colons are not a good separator to use in this context in the first place. – tripleee Sep 21 '17 at 09:01
  • @tripleee, Agreed. Either method makes assumptions about what's *not* in the data, and may hit corner cases. A more general fix would be if the data format instead used counted strings, (say a border delimiter for human readability, the number of chars to the *next* delimiter or linefeed, then the item data), *e.g.* `:7:name1:9:1.1.1.1:8:ps -ax`. – agc Sep 21 '17 at 09:20
  • That's not a particularly good scheme. Use a standard data format with well-defined semantics and escaping mechanisms like JSON if you have complex requirements. – tripleee Sep 21 '17 at 09:55
  • @tripleee, I'd be interested to see a simple example of a data item (given the OP's type of data) that a counted string wouldn't handle. – agc Sep 21 '17 at 11:30
  • I'm not saying it's impossible, just that if your requirements are nontrivial, I'd move to a standardized solution instead of rolliing my own. – tripleee Sep 21 '17 at 11:33
1

The only safe possibility is use a loop:

a='name1::1.1.1.1::ps -ax'
array=()

a+=:: # artificially append the separator
while [[ $a ]]; do
    array+=( "${a%%::*}" )
    a=${a#*::}
done

This will work with any symbol in a (spaces, glob characters, newlines, etc.)

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
0
i=0                                                                            
string="name1::1.1.1.1::ps -ax"                                                
echo "$string" | awk 'BEGIN{FS="::";OFS="\n"}{$1=$1;print $0}'>tempFile            
while read line;                                                               
do                                                                             
  arr["$i"]="$line"                                                                  
  i=$(expr $i + 1)                                                              
done<tempFile                                                                  
echo "${arr[@]}"                                                                
echo "${arr[0]}"                                                                
echo "${arr[1]}"                                                                
echo "${arr[2]}"

Output:

sh-4.4$ ./script1.sh
name1 1.1.1.1 ps -ax
name1
1.1.1.1
ps -ax
tripleee
  • 175,061
  • 34
  • 275
  • 318
abhishek phukan
  • 751
  • 1
  • 5
  • 16
0
echo "name1::1.1.1.1::ps -ax" | awk -F"::" '{print $1 $2 $3}'
Rome
  • 563
  • 1
  • 9
  • 25
  • This doesn't particularly help, you might as well `echo "${value//::/ }"`. The task is to get the individual members parsed into a Bash array. – tripleee Sep 21 '17 at 09:02