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
.