Question: Let's assume I have the following string: `x="number 1;number 2;number 3"`.

Access to the *first* substring is successfull via `${x%%";"*}`, access to the *last* substring is via `${x##*";"}`:

```
$ x="number 1;number 2;number 3"
$ echo "front : ${x%%";"*}"  #front-most-part
number 1
$ echo "back  : ${x##*";"}"  #back-most-part
number 3
$

```

How do I access the *middle part:* (eg. `number 2`)?  

Is there a better way to do this if I have (many...) more parts then just three?  

**In other words: Is there a generic way of accessing substring No. *`n`* of string *`yyy`*, delimited by string *`xxx`* where *`xxx`* is an arbitraty string/delimiter?**

---

I have read [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?rq=1), but I specifically do not want to iterate over the string but rather directly access a given substring.

This specifically does not ask or a split into arrays, but into sub-strings.

Answer: 
With a fixed index: 

```
x="number 1;number 2;number 3"

# Split input into fields by ';' and read the 2nd field into $f2
# Note the need for the *2nd* `unused`, otherwise f2 would 
# receive the 2nd field *plus the remainder of the line*.
IFS=';' read -r unused f2 unused <<<"$x"

echo "$f2"

```

Generically, using an array:

```
x="number 1;number 2;number 3"

# Split input int fields by ';' and read all resulting fields
# into an *array* (-a).
IFS=';' read -r -a fields <<<"$x"

# Access the desired field.
ndx=1
echo "${fields[ndx]}"

```

---

**Constraints**:

Using `IFS`, the special variable specifying the **I**nternal **F**ield **S**eparator characters, *invariably* means:

* Only *single*, literal characters can act as field separators.

	+ However, you can specify *multiple* characters, in which case *any* of them is treated as a separator.
* The *default* separator characters are `$' \t\n'` - i.e., space, tab, and newline, and *runs* of them (multiple contiguious instances) are always considered a *single* separator; e.g., `'a b'` has *2* fields - the multiple space count as a single separator.
* By contrast, *with any other character*, characters in a run are considered separately, and thus separate *empty* fields; e.g., `'a;;b'` has *3* fields - each `;` is its own separator, so there's an empty field between `;;`.

The `read -r -a ... <<<...` technique **generally works well, as long as**: 

* the input is *single-line*
* you're not concerned about *a trailing empty field getting discarded*

If you need a **fully generic, robust solution** that addresses the issues above, 
use the following variation, which is explained in [@gniourf\_gniourf answer here](https://stackoverflow.com/a/24426608/45375):

```
sep=';' 
IFS="$sep" read -r -d '' -a fields < <(printf "%s${sep}\0" "$x")    

```

Note the need to use `-d ''` to read multi-line input all at once, and the need to terminate the input with another separator instance to preserve a trailing empty field; the trailing `\0` is needed to ensure that `read`'s exit code is `0`.
Don't use:

~~Create an array with a delimiter of `;`:~~

```
x="number 1;number 2;number 3"
_IFS=$IFS; IFS=';'
arr=($x)
IFS=$_IFS

echo ${arr[0]} # number 1
echo ${arr[1]} # number 2
echo ${arr[2]} # number 3

```