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 Internal Field Separator 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:
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.