Take the following bash example (I should mention that my real output comes from vboxmanage list vms)

TEST='"ubuntu server" foo bar'
echo $TEST
"ubuntu server" foo bar

How do I get the three values separated (in a loop or separate variable)? Every solution I've found relies on space delimiter and will break the first value.

  • the best way would be to avoid having to do that, if it would be possible to get the input in some other format, with the values actually separated. Where does the input string come from?
    – ilkkachu
    Jul 9 at 10:40
  • This output is not under my control (vboxmanage list vms)
    – Marco
    Jul 9 at 10:52
  • What is it from the output of vboxmanage list vms that you want to have? The names of the VMs in a list? To iterate over?
    – Kusalananda
    Jul 9 at 11:03
  • 3
    Can you edit your post to add a verbatim example of the output you want to separate? Also what do you want to do with the individual tokens? The answer may depend on these factors.
    – AdminBee
    Jul 9 at 11:04
  • 1
    Also note that a line of output from vmboxmanage list vms does not contain three words, it always seems to contain two words, a quoted virtual machine name and an UUID. It would be very nice if you could show real data. There will be two fields for each available VM.
    – Kusalananda
    Jul 9 at 11:14

You may iterate over the names of the VirtualBox machines:

vboxmanage list -l vms | sed -n 's/^Name:[[:blank:]]*//p' |
while IFS= read -r vm_name; do
   printf 'There is one VM called "%s"\n' "$vm_name"

This lists the machines using the long format and parses out the names. The names are then read in a shell loop and a short message is printed for each.

You may also read the names into an array:

readarray -t vm_names < <(vboxmanage list -l vms | sed -n 's/^Name:[[:blank:]]*//p')
printf 'There is one VM called "%s"\n' "${vm_names[@]}"

Here, the list of names are red into the array vm_names. You may loop over this array like so:

for vm_name in "${vm_names[@]}"; do
   # Use "$vm_name" here

To also use the UUID:

vboxmanage list -l vms |
sed -n \
    -e '/^Name:[[:blank:]]*/{ s///; h; }' \
    -e '/^UUID:[[:blank:]]*/{ s///; G; y/\n/@/; p; }' |
while IFS=@ read -r vm_uuid vm_name; do
    printf 'VM "%s" has UUID "%s"\n' "$vm_name" "$vm_uuid"
  • This doesn't really help if what they have is just a string with the values and quotes embedded, e.g. from the output of some other command
    – ilkkachu
    Jul 9 at 10:56
  • @ilkkachu I trust them to mention this in the question if that is the case.
    – Kusalananda
    Jul 9 at 11:00
  • mm, it's right there in the title line, "how to split string" etc.
    – ilkkachu
    Jul 9 at 11:01
  • @ilkkachu Note that the question contains a piece of code. In my answer, I'm suggesting changing that code.
    – Kusalananda
    Jul 9 at 11:01
  • 1
    @Marco Yes you should. I have now fixed my initial answer. But it's still unclear what you data actually looks like as that command does not result in three words like you show.
    – Kusalananda
    Jul 9 at 11:10

Assuming you have it just as a string, in the format shown in the code block in your initial question (i.e. "ubuntu server" foo bar), and the data is known to be sufficiently nice, you could use eval to evaluate it is as part of a shell command:

str='"ubuntu server" foo bar'
eval "arr=($str)"

With that input, this would give the array arr with the three elements ubuntu server, foo, and bar. You can use the array in the usual ways, e.g. "${arr[@]}" to expand to a list of words, one for each element.

But note that eval also processes expansions, so if the string contains e.g. $(whatever), eval will run the command whatever. Also, glob characters will processed, so a lone * would expand to all filenames in the directory, etc.

To stop most expansions, you could replace any $ signs in the string with \$, and to disable globbing, use set -f:

str='"ubuntu server" $foo *'
set -f
eval "arr=(${str//\$/\\\$})"
# print it
printf ":%s:\n" "${arr[@]}"

(set -f will of course stay in effect after this, you'd undo it with set +f. In addition to dollar signs, backticks also start expansions, but they can be dealt with similarly. That's left as exercise in case it's necessary.)

  • Note that their data is not the output of the command that they mention. The output of that command looks like "some name" {19e47e6e-a030-4b25-9926-fc6258542fba} (one line of these per VM). It is likely that they want to iterate over the VMs.
    – Kusalananda
    Jul 9 at 11:18
  • @Kusalananda, yes, that's why the command substitution was just in a comment, and why the "or whatever" was there. I wrote "Assuming you have it just as a string, in the format shown in your question" and they showed TEST='"ubuntu server" foo bar', so that's what I'm going with.
    – ilkkachu
    Jul 9 at 11:22

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.