How to split a string into an array in bash Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern) 2019 Community Moderator Election Results Why I closed the “Why is Kali so hard” questionHow to convert a String into Array in shell scriptHow to split command's output and assigns result to an array using only a single expression?Split: how to split into different percentages?String index processingSplit single string into character array using ONLY bashawk search for string containing forward slash in variablesplit a string into path and applicationBash: split multi line input into arrayUse sed to edit characters 65-79 if string is present in earlier positionBash Array Contains false positivesSplit string of filenames into an arrayHow do I split a string into two strings?
Is there a documented rationale why the House Ways and Means chairman can demand tax info?
Direct Experience of Meditation
What computer would be fastest for Mathematica Home Edition?
Slither Like a Snake
If I can make up priors, why can't I make up posteriors?
Fishing simulator
What are the performance impacts of 'functional' Rust?
Cold is to Refrigerator as warm is to?
How are presidential pardons supposed to be used?
Why don't the Weasley twins use magic outside of school if the Trace can only find the location of spells cast?
Is it possible to ask for a hotel room without minibar/extra services?
Replacing HDD with SSD; what about non-APFS/APFS?
I'm having difficulty getting my players to do stuff in a sandbox campaign
Strange behaviour of Check
What do you call a plan that's an alternative plan in case your initial plan fails?
Classification of bundles, Postnikov towers, obstruction theory, local coefficients
Need a suitable toxic chemical for a murder plot in my novel
Using "nakedly" instead of "with nothing on"
Estimated State payment too big --> money back; + 2018 Tax Reform
Who can trigger ship-wide alerts in Star Trek?
What is the electric potential inside a point charge?
How is simplicity better than precision and clarity in prose?
How should I respond to a player wanting to catch a sword between their hands?
Active filter with series inductor and resistor - do these exist?
How to split a string into an array in bash
Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)
2019 Community Moderator Election Results
Why I closed the “Why is Kali so hard” questionHow to convert a String into Array in shell scriptHow to split command's output and assigns result to an array using only a single expression?Split: how to split into different percentages?String index processingSplit single string into character array using ONLY bashawk search for string containing forward slash in variablesplit a string into path and applicationBash: split multi line input into arrayUse sed to edit characters 65-79 if string is present in earlier positionBash Array Contains false positivesSplit string of filenames into an arrayHow do I split a string into two strings?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I have a problem with the output of a program. I need to launch a command in bash and take its output (a string) and split it to add new lines in certain places. The string looks like this:
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
basically it is an xxx.yy.zz: value, but the value may contain spaces.
Here's the output I'd like to get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
I have an idea to search for first dot and then look back from that position for space to put a new line there, but I'm not sure how to achieve it in Bash.
bash string split
add a comment |
I have a problem with the output of a program. I need to launch a command in bash and take its output (a string) and split it to add new lines in certain places. The string looks like this:
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
basically it is an xxx.yy.zz: value, but the value may contain spaces.
Here's the output I'd like to get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
I have an idea to search for first dot and then look back from that position for space to put a new line there, but I'm not sure how to achieve it in Bash.
bash string split
add a comment |
I have a problem with the output of a program. I need to launch a command in bash and take its output (a string) and split it to add new lines in certain places. The string looks like this:
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
basically it is an xxx.yy.zz: value, but the value may contain spaces.
Here's the output I'd like to get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
I have an idea to search for first dot and then look back from that position for space to put a new line there, but I'm not sure how to achieve it in Bash.
bash string split
I have a problem with the output of a program. I need to launch a command in bash and take its output (a string) and split it to add new lines in certain places. The string looks like this:
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
basically it is an xxx.yy.zz: value, but the value may contain spaces.
Here's the output I'd like to get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
I have an idea to search for first dot and then look back from that position for space to put a new line there, but I'm not sure how to achieve it in Bash.
bash string split
bash string split
edited 17 hours ago
Rui F Ribeiro
42.1k1483142
42.1k1483142
asked May 13 '14 at 11:33
user67524user67524
33113
33113
add a comment |
add a comment |
6 Answers
6
active
oldest
votes
Pure bash solution, no external tools used to process the strings, just parameter expansion:
#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'
IFS=: read -a fields <<< "$str"
for (( i=0 ; i < $#fields[@] ; i++ )) ; do
f=$fields[i]
notfirst=$(( i>0 ))
last=$(( i+1 == $#fields[@] ))
(( notfirst )) && echo -n $f% *
start=('' $'n' ' ')
colon=('' ': ')
echo -n "$start[notfirst + last]$f##* $colon[!last]"
done
echo
Explanation: $notfirst
and $last
are booleans. The part before the last space $f% *
isn't printed for the first field, as there is no such thing. $start
and $colon
hold various strings that separate the fields: at the first item, notfirst + last
is 0, so nothing is prepended, for the rest of the lines, $notfirst
is 1, so a newline is printed, and for the last line, the addition gives 2, so a space is printed. Then, the part after the last space is printed $f##*
. Colon is printed for all lines except the last one.
Thanks a lot! I think I'll use your solution although I understand almost nothing. Can I ask you for some additions? Because original string begin with STRING: " and end with ". I forgot to mention that I need to remove them. It's constant 9 characters on the beginning and one char. from the end of the string.
– user67524
May 13 '14 at 21:12
@user67524: Read about Parameter Expansion inman bash
. To remove the parts, usestr=$str#STRING: "; str=$str%"
.
– choroba
May 13 '14 at 21:23
add a comment |
A perl
solution:
$ perl -pe 'sS+:$seen++ ? "n$&" : "$&"ge' file
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Explanation
S+:
matches string end with:
.- With all matched strings, we insert the newline before them
("n$&")
except the first one($seen++)
.
add a comment |
With GNU sed, you can match each contiguous string (i.e. without whitespace) terminated by :
and then place a newline before all but the first one:
sed 's/[^[:space:]]+:/n&/g2'
If your version of sed does not support the gn
extension, you can use a plain g
modifier
sed 's/[^[:space:]]1,:/
&/g'
which will work the same except for printing an additional newline before the first key. You could use perl -pe 's/S+:/n$&/g'
with the same proviso (there may be a perl equivalent of the GNU sed g2
but I don't know it).
While you're at using GNUsed
, you could also make it:sed -r 's/S+:/n&/g2'
– Stéphane Chazelas
May 13 '14 at 15:24
add a comment |
It's easier using a tool that supports lookarounds:
$ s="battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500"
$ grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
If you wanted the result in an array:
$ IFS=$'n' foo=($(grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"))
$ for i in "$!foo[@]"; do echo "$i<==>$foo[i]"; done
0<==>battery.charge: 90
1<==>battery.charge.low: 30
2<==>battery.runtime: 3690
3<==>battery.voltage: 230.0
4<==>device.mfr: MGE UPS SYSTEMS
5<==>device.model: Pulsar Evolution 500
EDIT: Explanation of the regex:
'S+:s+.*?(?=s+S+:|$)'
S+
matches one or more non-whitespace characters:
matches:
s+
matches one or more spaces after the:
.*?
denotes a non-greedy match(?=s+S+:|$)
is a lookahead assertion to determine if there is:- one or more space followed by a string (non-whitespace charaters) and a colon, or
- end of string
So the string is split into parts like battery.charge: 90
, ... device.mfr: MGE UPS SYSTEMS
, ...
Below are links to a couple of online regular expression analyzers:
- http://rick.measham.id.au/paste/explain.pl
- http://xenon.stanford.edu/~xusch/regexp/
+1 - very nice approach! If you can, please explain so that everyone understands what you're doing in thegrep
. This is a common problem in other Q's and a good explanation would be helpful here.
– slm♦
May 13 '14 at 13:38
add a comment |
Here's a naive approach that should work assuming you don't care that tabs and newlines in the input (if any) are converted to plain spaces.
The idea is simple: split the input on whitespace, and print every token except that you prepend tokens that end with :
with a newline (and re-add a space in front of the others). The $count
variable and related if
are only useful to prevent an initial empty line. Could be removed if that's not a problem. (The script assumes the input is in a file called intput
in the current directory.)
#! /bin/bash
count=0
for i in $(<input) ; do
fmt=
if [[ $i =~ :$ ]] ; then
if [[ $count -gt 0 ]] ; then
fmt="n%s"
else
fmt="%s"
fi
((count++))
else
fmt=" %s"
fi
printf "$fmt" "$i"
done
echo
echo "Num items: $count"
I hope someone can come up with a nicer alternative though.
$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6
add a comment |
You can use awk(1) with the following script split.awk:
BEGIN RS=" "; first=1;
first first=0; printf "%s", $1; next;
/[a-z]+.[^:]+:/ printf "n%s", $1; next;
printf " %s", $1
END printf "n"
When you run
awk -f split.awk input.dat
you will get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
The idea is to let awk split the input when it sees a space (setting record separator RS in line 1). Then it matches xxx.yy.zz: values in line 2 and 3 (distinguishing the very first match from subsequent ones), while line 4 matches whenever line 2 and 3 do not match. Line 5 just print the last newline.
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f129191%2fhow-to-split-a-string-into-an-array-in-bash%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
Pure bash solution, no external tools used to process the strings, just parameter expansion:
#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'
IFS=: read -a fields <<< "$str"
for (( i=0 ; i < $#fields[@] ; i++ )) ; do
f=$fields[i]
notfirst=$(( i>0 ))
last=$(( i+1 == $#fields[@] ))
(( notfirst )) && echo -n $f% *
start=('' $'n' ' ')
colon=('' ': ')
echo -n "$start[notfirst + last]$f##* $colon[!last]"
done
echo
Explanation: $notfirst
and $last
are booleans. The part before the last space $f% *
isn't printed for the first field, as there is no such thing. $start
and $colon
hold various strings that separate the fields: at the first item, notfirst + last
is 0, so nothing is prepended, for the rest of the lines, $notfirst
is 1, so a newline is printed, and for the last line, the addition gives 2, so a space is printed. Then, the part after the last space is printed $f##*
. Colon is printed for all lines except the last one.
Thanks a lot! I think I'll use your solution although I understand almost nothing. Can I ask you for some additions? Because original string begin with STRING: " and end with ". I forgot to mention that I need to remove them. It's constant 9 characters on the beginning and one char. from the end of the string.
– user67524
May 13 '14 at 21:12
@user67524: Read about Parameter Expansion inman bash
. To remove the parts, usestr=$str#STRING: "; str=$str%"
.
– choroba
May 13 '14 at 21:23
add a comment |
Pure bash solution, no external tools used to process the strings, just parameter expansion:
#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'
IFS=: read -a fields <<< "$str"
for (( i=0 ; i < $#fields[@] ; i++ )) ; do
f=$fields[i]
notfirst=$(( i>0 ))
last=$(( i+1 == $#fields[@] ))
(( notfirst )) && echo -n $f% *
start=('' $'n' ' ')
colon=('' ': ')
echo -n "$start[notfirst + last]$f##* $colon[!last]"
done
echo
Explanation: $notfirst
and $last
are booleans. The part before the last space $f% *
isn't printed for the first field, as there is no such thing. $start
and $colon
hold various strings that separate the fields: at the first item, notfirst + last
is 0, so nothing is prepended, for the rest of the lines, $notfirst
is 1, so a newline is printed, and for the last line, the addition gives 2, so a space is printed. Then, the part after the last space is printed $f##*
. Colon is printed for all lines except the last one.
Thanks a lot! I think I'll use your solution although I understand almost nothing. Can I ask you for some additions? Because original string begin with STRING: " and end with ". I forgot to mention that I need to remove them. It's constant 9 characters on the beginning and one char. from the end of the string.
– user67524
May 13 '14 at 21:12
@user67524: Read about Parameter Expansion inman bash
. To remove the parts, usestr=$str#STRING: "; str=$str%"
.
– choroba
May 13 '14 at 21:23
add a comment |
Pure bash solution, no external tools used to process the strings, just parameter expansion:
#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'
IFS=: read -a fields <<< "$str"
for (( i=0 ; i < $#fields[@] ; i++ )) ; do
f=$fields[i]
notfirst=$(( i>0 ))
last=$(( i+1 == $#fields[@] ))
(( notfirst )) && echo -n $f% *
start=('' $'n' ' ')
colon=('' ': ')
echo -n "$start[notfirst + last]$f##* $colon[!last]"
done
echo
Explanation: $notfirst
and $last
are booleans. The part before the last space $f% *
isn't printed for the first field, as there is no such thing. $start
and $colon
hold various strings that separate the fields: at the first item, notfirst + last
is 0, so nothing is prepended, for the rest of the lines, $notfirst
is 1, so a newline is printed, and for the last line, the addition gives 2, so a space is printed. Then, the part after the last space is printed $f##*
. Colon is printed for all lines except the last one.
Pure bash solution, no external tools used to process the strings, just parameter expansion:
#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'
IFS=: read -a fields <<< "$str"
for (( i=0 ; i < $#fields[@] ; i++ )) ; do
f=$fields[i]
notfirst=$(( i>0 ))
last=$(( i+1 == $#fields[@] ))
(( notfirst )) && echo -n $f% *
start=('' $'n' ' ')
colon=('' ': ')
echo -n "$start[notfirst + last]$f##* $colon[!last]"
done
echo
Explanation: $notfirst
and $last
are booleans. The part before the last space $f% *
isn't printed for the first field, as there is no such thing. $start
and $colon
hold various strings that separate the fields: at the first item, notfirst + last
is 0, so nothing is prepended, for the rest of the lines, $notfirst
is 1, so a newline is printed, and for the last line, the addition gives 2, so a space is printed. Then, the part after the last space is printed $f##*
. Colon is printed for all lines except the last one.
edited May 13 '14 at 21:28
answered May 13 '14 at 11:58
chorobachoroba
27.1k45176
27.1k45176
Thanks a lot! I think I'll use your solution although I understand almost nothing. Can I ask you for some additions? Because original string begin with STRING: " and end with ". I forgot to mention that I need to remove them. It's constant 9 characters on the beginning and one char. from the end of the string.
– user67524
May 13 '14 at 21:12
@user67524: Read about Parameter Expansion inman bash
. To remove the parts, usestr=$str#STRING: "; str=$str%"
.
– choroba
May 13 '14 at 21:23
add a comment |
Thanks a lot! I think I'll use your solution although I understand almost nothing. Can I ask you for some additions? Because original string begin with STRING: " and end with ". I forgot to mention that I need to remove them. It's constant 9 characters on the beginning and one char. from the end of the string.
– user67524
May 13 '14 at 21:12
@user67524: Read about Parameter Expansion inman bash
. To remove the parts, usestr=$str#STRING: "; str=$str%"
.
– choroba
May 13 '14 at 21:23
Thanks a lot! I think I'll use your solution although I understand almost nothing. Can I ask you for some additions? Because original string begin with STRING: " and end with ". I forgot to mention that I need to remove them. It's constant 9 characters on the beginning and one char. from the end of the string.
– user67524
May 13 '14 at 21:12
Thanks a lot! I think I'll use your solution although I understand almost nothing. Can I ask you for some additions? Because original string begin with STRING: " and end with ". I forgot to mention that I need to remove them. It's constant 9 characters on the beginning and one char. from the end of the string.
– user67524
May 13 '14 at 21:12
@user67524: Read about Parameter Expansion in
man bash
. To remove the parts, use str=$str#STRING: "; str=$str%"
.– choroba
May 13 '14 at 21:23
@user67524: Read about Parameter Expansion in
man bash
. To remove the parts, use str=$str#STRING: "; str=$str%"
.– choroba
May 13 '14 at 21:23
add a comment |
A perl
solution:
$ perl -pe 'sS+:$seen++ ? "n$&" : "$&"ge' file
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Explanation
S+:
matches string end with:
.- With all matched strings, we insert the newline before them
("n$&")
except the first one($seen++)
.
add a comment |
A perl
solution:
$ perl -pe 'sS+:$seen++ ? "n$&" : "$&"ge' file
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Explanation
S+:
matches string end with:
.- With all matched strings, we insert the newline before them
("n$&")
except the first one($seen++)
.
add a comment |
A perl
solution:
$ perl -pe 'sS+:$seen++ ? "n$&" : "$&"ge' file
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Explanation
S+:
matches string end with:
.- With all matched strings, we insert the newline before them
("n$&")
except the first one($seen++)
.
A perl
solution:
$ perl -pe 'sS+:$seen++ ? "n$&" : "$&"ge' file
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Explanation
S+:
matches string end with:
.- With all matched strings, we insert the newline before them
("n$&")
except the first one($seen++)
.
answered May 13 '14 at 14:16
cuonglmcuonglm
106k25211309
106k25211309
add a comment |
add a comment |
With GNU sed, you can match each contiguous string (i.e. without whitespace) terminated by :
and then place a newline before all but the first one:
sed 's/[^[:space:]]+:/n&/g2'
If your version of sed does not support the gn
extension, you can use a plain g
modifier
sed 's/[^[:space:]]1,:/
&/g'
which will work the same except for printing an additional newline before the first key. You could use perl -pe 's/S+:/n$&/g'
with the same proviso (there may be a perl equivalent of the GNU sed g2
but I don't know it).
While you're at using GNUsed
, you could also make it:sed -r 's/S+:/n&/g2'
– Stéphane Chazelas
May 13 '14 at 15:24
add a comment |
With GNU sed, you can match each contiguous string (i.e. without whitespace) terminated by :
and then place a newline before all but the first one:
sed 's/[^[:space:]]+:/n&/g2'
If your version of sed does not support the gn
extension, you can use a plain g
modifier
sed 's/[^[:space:]]1,:/
&/g'
which will work the same except for printing an additional newline before the first key. You could use perl -pe 's/S+:/n$&/g'
with the same proviso (there may be a perl equivalent of the GNU sed g2
but I don't know it).
While you're at using GNUsed
, you could also make it:sed -r 's/S+:/n&/g2'
– Stéphane Chazelas
May 13 '14 at 15:24
add a comment |
With GNU sed, you can match each contiguous string (i.e. without whitespace) terminated by :
and then place a newline before all but the first one:
sed 's/[^[:space:]]+:/n&/g2'
If your version of sed does not support the gn
extension, you can use a plain g
modifier
sed 's/[^[:space:]]1,:/
&/g'
which will work the same except for printing an additional newline before the first key. You could use perl -pe 's/S+:/n$&/g'
with the same proviso (there may be a perl equivalent of the GNU sed g2
but I don't know it).
With GNU sed, you can match each contiguous string (i.e. without whitespace) terminated by :
and then place a newline before all but the first one:
sed 's/[^[:space:]]+:/n&/g2'
If your version of sed does not support the gn
extension, you can use a plain g
modifier
sed 's/[^[:space:]]1,:/
&/g'
which will work the same except for printing an additional newline before the first key. You could use perl -pe 's/S+:/n$&/g'
with the same proviso (there may be a perl equivalent of the GNU sed g2
but I don't know it).
edited May 13 '14 at 15:29
Stéphane Chazelas
314k57594953
314k57594953
answered May 13 '14 at 12:35
steeldriversteeldriver
37.8k45489
37.8k45489
While you're at using GNUsed
, you could also make it:sed -r 's/S+:/n&/g2'
– Stéphane Chazelas
May 13 '14 at 15:24
add a comment |
While you're at using GNUsed
, you could also make it:sed -r 's/S+:/n&/g2'
– Stéphane Chazelas
May 13 '14 at 15:24
While you're at using GNU
sed
, you could also make it: sed -r 's/S+:/n&/g2'
– Stéphane Chazelas
May 13 '14 at 15:24
While you're at using GNU
sed
, you could also make it: sed -r 's/S+:/n&/g2'
– Stéphane Chazelas
May 13 '14 at 15:24
add a comment |
It's easier using a tool that supports lookarounds:
$ s="battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500"
$ grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
If you wanted the result in an array:
$ IFS=$'n' foo=($(grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"))
$ for i in "$!foo[@]"; do echo "$i<==>$foo[i]"; done
0<==>battery.charge: 90
1<==>battery.charge.low: 30
2<==>battery.runtime: 3690
3<==>battery.voltage: 230.0
4<==>device.mfr: MGE UPS SYSTEMS
5<==>device.model: Pulsar Evolution 500
EDIT: Explanation of the regex:
'S+:s+.*?(?=s+S+:|$)'
S+
matches one or more non-whitespace characters:
matches:
s+
matches one or more spaces after the:
.*?
denotes a non-greedy match(?=s+S+:|$)
is a lookahead assertion to determine if there is:- one or more space followed by a string (non-whitespace charaters) and a colon, or
- end of string
So the string is split into parts like battery.charge: 90
, ... device.mfr: MGE UPS SYSTEMS
, ...
Below are links to a couple of online regular expression analyzers:
- http://rick.measham.id.au/paste/explain.pl
- http://xenon.stanford.edu/~xusch/regexp/
+1 - very nice approach! If you can, please explain so that everyone understands what you're doing in thegrep
. This is a common problem in other Q's and a good explanation would be helpful here.
– slm♦
May 13 '14 at 13:38
add a comment |
It's easier using a tool that supports lookarounds:
$ s="battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500"
$ grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
If you wanted the result in an array:
$ IFS=$'n' foo=($(grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"))
$ for i in "$!foo[@]"; do echo "$i<==>$foo[i]"; done
0<==>battery.charge: 90
1<==>battery.charge.low: 30
2<==>battery.runtime: 3690
3<==>battery.voltage: 230.0
4<==>device.mfr: MGE UPS SYSTEMS
5<==>device.model: Pulsar Evolution 500
EDIT: Explanation of the regex:
'S+:s+.*?(?=s+S+:|$)'
S+
matches one or more non-whitespace characters:
matches:
s+
matches one or more spaces after the:
.*?
denotes a non-greedy match(?=s+S+:|$)
is a lookahead assertion to determine if there is:- one or more space followed by a string (non-whitespace charaters) and a colon, or
- end of string
So the string is split into parts like battery.charge: 90
, ... device.mfr: MGE UPS SYSTEMS
, ...
Below are links to a couple of online regular expression analyzers:
- http://rick.measham.id.au/paste/explain.pl
- http://xenon.stanford.edu/~xusch/regexp/
+1 - very nice approach! If you can, please explain so that everyone understands what you're doing in thegrep
. This is a common problem in other Q's and a good explanation would be helpful here.
– slm♦
May 13 '14 at 13:38
add a comment |
It's easier using a tool that supports lookarounds:
$ s="battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500"
$ grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
If you wanted the result in an array:
$ IFS=$'n' foo=($(grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"))
$ for i in "$!foo[@]"; do echo "$i<==>$foo[i]"; done
0<==>battery.charge: 90
1<==>battery.charge.low: 30
2<==>battery.runtime: 3690
3<==>battery.voltage: 230.0
4<==>device.mfr: MGE UPS SYSTEMS
5<==>device.model: Pulsar Evolution 500
EDIT: Explanation of the regex:
'S+:s+.*?(?=s+S+:|$)'
S+
matches one or more non-whitespace characters:
matches:
s+
matches one or more spaces after the:
.*?
denotes a non-greedy match(?=s+S+:|$)
is a lookahead assertion to determine if there is:- one or more space followed by a string (non-whitespace charaters) and a colon, or
- end of string
So the string is split into parts like battery.charge: 90
, ... device.mfr: MGE UPS SYSTEMS
, ...
Below are links to a couple of online regular expression analyzers:
- http://rick.measham.id.au/paste/explain.pl
- http://xenon.stanford.edu/~xusch/regexp/
It's easier using a tool that supports lookarounds:
$ s="battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500"
$ grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
If you wanted the result in an array:
$ IFS=$'n' foo=($(grep -oP 'S+:s+.*?(?=s+S+:|$)' <<< "$s"))
$ for i in "$!foo[@]"; do echo "$i<==>$foo[i]"; done
0<==>battery.charge: 90
1<==>battery.charge.low: 30
2<==>battery.runtime: 3690
3<==>battery.voltage: 230.0
4<==>device.mfr: MGE UPS SYSTEMS
5<==>device.model: Pulsar Evolution 500
EDIT: Explanation of the regex:
'S+:s+.*?(?=s+S+:|$)'
S+
matches one or more non-whitespace characters:
matches:
s+
matches one or more spaces after the:
.*?
denotes a non-greedy match(?=s+S+:|$)
is a lookahead assertion to determine if there is:- one or more space followed by a string (non-whitespace charaters) and a colon, or
- end of string
So the string is split into parts like battery.charge: 90
, ... device.mfr: MGE UPS SYSTEMS
, ...
Below are links to a couple of online regular expression analyzers:
- http://rick.measham.id.au/paste/explain.pl
- http://xenon.stanford.edu/~xusch/regexp/
edited May 13 '14 at 14:24
answered May 13 '14 at 12:38
devnulldevnull
8,73113042
8,73113042
+1 - very nice approach! If you can, please explain so that everyone understands what you're doing in thegrep
. This is a common problem in other Q's and a good explanation would be helpful here.
– slm♦
May 13 '14 at 13:38
add a comment |
+1 - very nice approach! If you can, please explain so that everyone understands what you're doing in thegrep
. This is a common problem in other Q's and a good explanation would be helpful here.
– slm♦
May 13 '14 at 13:38
+1 - very nice approach! If you can, please explain so that everyone understands what you're doing in the
grep
. This is a common problem in other Q's and a good explanation would be helpful here.– slm♦
May 13 '14 at 13:38
+1 - very nice approach! If you can, please explain so that everyone understands what you're doing in the
grep
. This is a common problem in other Q's and a good explanation would be helpful here.– slm♦
May 13 '14 at 13:38
add a comment |
Here's a naive approach that should work assuming you don't care that tabs and newlines in the input (if any) are converted to plain spaces.
The idea is simple: split the input on whitespace, and print every token except that you prepend tokens that end with :
with a newline (and re-add a space in front of the others). The $count
variable and related if
are only useful to prevent an initial empty line. Could be removed if that's not a problem. (The script assumes the input is in a file called intput
in the current directory.)
#! /bin/bash
count=0
for i in $(<input) ; do
fmt=
if [[ $i =~ :$ ]] ; then
if [[ $count -gt 0 ]] ; then
fmt="n%s"
else
fmt="%s"
fi
((count++))
else
fmt=" %s"
fi
printf "$fmt" "$i"
done
echo
echo "Num items: $count"
I hope someone can come up with a nicer alternative though.
$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6
add a comment |
Here's a naive approach that should work assuming you don't care that tabs and newlines in the input (if any) are converted to plain spaces.
The idea is simple: split the input on whitespace, and print every token except that you prepend tokens that end with :
with a newline (and re-add a space in front of the others). The $count
variable and related if
are only useful to prevent an initial empty line. Could be removed if that's not a problem. (The script assumes the input is in a file called intput
in the current directory.)
#! /bin/bash
count=0
for i in $(<input) ; do
fmt=
if [[ $i =~ :$ ]] ; then
if [[ $count -gt 0 ]] ; then
fmt="n%s"
else
fmt="%s"
fi
((count++))
else
fmt=" %s"
fi
printf "$fmt" "$i"
done
echo
echo "Num items: $count"
I hope someone can come up with a nicer alternative though.
$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6
add a comment |
Here's a naive approach that should work assuming you don't care that tabs and newlines in the input (if any) are converted to plain spaces.
The idea is simple: split the input on whitespace, and print every token except that you prepend tokens that end with :
with a newline (and re-add a space in front of the others). The $count
variable and related if
are only useful to prevent an initial empty line. Could be removed if that's not a problem. (The script assumes the input is in a file called intput
in the current directory.)
#! /bin/bash
count=0
for i in $(<input) ; do
fmt=
if [[ $i =~ :$ ]] ; then
if [[ $count -gt 0 ]] ; then
fmt="n%s"
else
fmt="%s"
fi
((count++))
else
fmt=" %s"
fi
printf "$fmt" "$i"
done
echo
echo "Num items: $count"
I hope someone can come up with a nicer alternative though.
$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6
Here's a naive approach that should work assuming you don't care that tabs and newlines in the input (if any) are converted to plain spaces.
The idea is simple: split the input on whitespace, and print every token except that you prepend tokens that end with :
with a newline (and re-add a space in front of the others). The $count
variable and related if
are only useful to prevent an initial empty line. Could be removed if that's not a problem. (The script assumes the input is in a file called intput
in the current directory.)
#! /bin/bash
count=0
for i in $(<input) ; do
fmt=
if [[ $i =~ :$ ]] ; then
if [[ $count -gt 0 ]] ; then
fmt="n%s"
else
fmt="%s"
fi
((count++))
else
fmt=" %s"
fi
printf "$fmt" "$i"
done
echo
echo "Num items: $count"
I hope someone can come up with a nicer alternative though.
$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6
answered May 13 '14 at 12:28
MatMat
40k8124128
40k8124128
add a comment |
add a comment |
You can use awk(1) with the following script split.awk:
BEGIN RS=" "; first=1;
first first=0; printf "%s", $1; next;
/[a-z]+.[^:]+:/ printf "n%s", $1; next;
printf " %s", $1
END printf "n"
When you run
awk -f split.awk input.dat
you will get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
The idea is to let awk split the input when it sees a space (setting record separator RS in line 1). Then it matches xxx.yy.zz: values in line 2 and 3 (distinguishing the very first match from subsequent ones), while line 4 matches whenever line 2 and 3 do not match. Line 5 just print the last newline.
add a comment |
You can use awk(1) with the following script split.awk:
BEGIN RS=" "; first=1;
first first=0; printf "%s", $1; next;
/[a-z]+.[^:]+:/ printf "n%s", $1; next;
printf " %s", $1
END printf "n"
When you run
awk -f split.awk input.dat
you will get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
The idea is to let awk split the input when it sees a space (setting record separator RS in line 1). Then it matches xxx.yy.zz: values in line 2 and 3 (distinguishing the very first match from subsequent ones), while line 4 matches whenever line 2 and 3 do not match. Line 5 just print the last newline.
add a comment |
You can use awk(1) with the following script split.awk:
BEGIN RS=" "; first=1;
first first=0; printf "%s", $1; next;
/[a-z]+.[^:]+:/ printf "n%s", $1; next;
printf " %s", $1
END printf "n"
When you run
awk -f split.awk input.dat
you will get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
The idea is to let awk split the input when it sees a space (setting record separator RS in line 1). Then it matches xxx.yy.zz: values in line 2 and 3 (distinguishing the very first match from subsequent ones), while line 4 matches whenever line 2 and 3 do not match. Line 5 just print the last newline.
You can use awk(1) with the following script split.awk:
BEGIN RS=" "; first=1;
first first=0; printf "%s", $1; next;
/[a-z]+.[^:]+:/ printf "n%s", $1; next;
printf " %s", $1
END printf "n"
When you run
awk -f split.awk input.dat
you will get
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
The idea is to let awk split the input when it sees a space (setting record separator RS in line 1). Then it matches xxx.yy.zz: values in line 2 and 3 (distinguishing the very first match from subsequent ones), while line 4 matches whenever line 2 and 3 do not match. Line 5 just print the last newline.
answered May 13 '14 at 12:39
tkrennwatkrennwa
2,64511013
2,64511013
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f129191%2fhow-to-split-a-string-into-an-array-in-bash%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
-bash, split, string