70

What does the triple-less-than-sign bash operator, <<<, mean, as inside the following code block?

LINE="7.6.5.4"
IFS=. read -a ARRAY <<< "$LINE"
echo "$IFS"
echo "${ARRAY[@]}"

Also, why does $IFS remain to be a space, not a period?

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
gsklee
  • 4,774
  • 4
  • 39
  • 55
  • Another use of this operator (instead of named pipes with mknod) when you need multiple pipes in a single | pipline: eg. echo data | command --data-on-stdin --other-data-on-fd-3 3<<<'other-data' – mosh Jan 08 '18 at 16:19

4 Answers4

51

It redirects the string to stdin of the command.

Variables assigned directly before the command in this way only take effect for the command process; the shell remains untouched.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • How do I modify it to use a custom multichar delimiter? Say, `==>`? How should I quote the string? – gsklee Oct 31 '11 at 05:50
  • 1
    You can't. `$IFS` is always interpreted as a series of single characters. – Ignacio Vazquez-Abrams Oct 31 '11 at 05:55
  • But when I want to split the array using newline. Only the first item is getting added to the hostArray. But the host has 4 items. `host=$(mysql -S $socket $catalog --skip-column-names -e "select host from mysql.user); echo "Host: $host"; IFS=$'\n';read -ra hostArray <<< "$host"; echo "HostArray:$hostArray" #This prints only 1 item. But there are 4 items;` What is wrong here? – Guna Jul 30 '18 at 12:46
  • 1
    @Guna I'm a few years too late but anyway others might wonder so...in this part `<<< "$host"` , when you reference an array like `"$host"` it implies the default index of zero so it's really doing `"$host[0]"`. What you need there is `"${host[@]}"` – Davos Jun 10 '21 at 14:07
  • Please note that this syntax is [not in POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07). – Totor Apr 29 '23 at 13:22
16

From man bash

Here Strings A variant of here documents, the format is:

     <<<word

The word is expanded and supplied to the command on its standard input.

The . on the IFS line is equivalent to source in bash.

Update: More from man bash (Thanks gsklee, sehe)

IFS The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is "<space><tab><new‐line>".

yet more from man bash

The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described above in PARAMETERS. These assignment statements affect only the environment seen by that command.

mtk
  • 13,221
  • 16
  • 72
  • 112
ryanbraganza
  • 16,863
  • 3
  • 17
  • 24
3

The reason that IFS is not being set is that bash isn't seeing that as a separate command... you need to put a line feed or a semicolon after the command in order to terminate it:

$ cat /tmp/ifs.sh
LINE="7.6.5.4"
IFS='.'  read -a ARRAY <<< "$LINE"
echo "$IFS"
echo "${ARRAY[@]}"

$ bash /tmp/ifs.sh 


7 6 5 4

but

$ cat /tmp/ifs.sh 
LINE="7.6.5.4"
IFS='.';  read -a ARRAY <<< "$LINE"
echo "$IFS"
echo "${ARRAY[@]}"

$ bash /tmp/ifs.sh 
.
7 6 5 4

I'm not sure why doing it the first way wasn't a syntax error though.

Barton Chittenden
  • 4,238
  • 2
  • 27
  • 46
  • 6
    Because the first way is how you temporarily set env vars for a single command. They automatically reset back to their previous values when that command is complete. I use this all the time to set debug flags or what have you but only temporarily. – Tanktalus Oct 31 '11 at 14:16
1

Just to provide a cleaner example of <<< without considering read, when you do:

mycmd <<< $myvar

seems to be equivalent to:

printf '%s\n' "$myvar" | mycmd

which is similar to:

echo "$myvar" | mycmd

except that echo interprets some backslash escapes and therefore could mess up your string.

So we see that <<< is a convenient and concise way to pass a variable (or other expansions) to the stdin of a command.

As a concrete example:

myvar="$(printf '01\n23\n45')"
sed 's/./A/' <<< $myvar

or:

sed 's/./A/' <<< $'01\n23\n45'

or:

myvar="$(printf '01\n23\n45')"
printf '%s\n' "$myvar" | sed 's/./A/'

all use sed to replace the first letter of each line with A giving output:

A1
A3
A5
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985