Why Is "Echo Foo | Read a ; Echo $A" Not Working as Expected

Why is echo foo | read a ; echo $a not working as expected?

Due to the piping the read is executed in its own subshell.

echo foo | while read a; do echo $a; done

will do what you expect it to.

Why echo EOF not working as expected

You want:

cat <<EOF
abc
EOF

Otherwise, what you're doing is just running echo with its stdin connected to a temporary file having abc in it. Since echo doesn't read stdin, it never finds out if there's contents waiting to be read from there or not.

I just assigned a variable, but echo $variable shows something else

In all of the cases above, the variable is correctly set, but not correctly read! The right way is to use double quotes when referencing:

echo "$var"

This gives the expected value in all the examples given. Always quote variable references!


Why?

When a variable is unquoted, it will:

  1. Undergo field splitting where the value is split into multiple words on whitespace (by default):

    Before: /* Foobar is free software */

    After: /*, Foobar, is, free, software, */

  2. Each of these words will undergo pathname expansion, where patterns are expanded into matching files:

    Before: /*

    After: /bin, /boot, /dev, /etc, /home, ...

  3. Finally, all the arguments are passed to echo, which writes them out separated by single spaces, giving

    /bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/

    instead of the variable's value.

When the variable is quoted it will:

  1. Be substituted for its value.
  2. There is no step 2.

This is why you should always quote all variable references, unless you specifically require word splitting and pathname expansion. Tools like shellcheck are there to help, and will warn about missing quotes in all the cases above.

tee command not working as expected (with read and echo)

read -p is writing it's output to stderr and echo is writing to stdout. stdout is typically buffered while stderr is not, and so it's not uncommon to see stderr things show up before stdout.

You could have your echo also go to stderr if you like by doing echo "string" >&2 or you could run it in the unbuffer command or similar tools if you have them available

bash shell builtin read not working as expected

Pipes create a subshell, and a variable created in a subshell does not exist in the main environment.

You can do:

$ read line < <(echo 'my text')
$ echo "$line"
my text

Bash -n argument not recognized by script when using $@

The -n or -e are valid options for the echo command.

Therefore it's consumed by the echo command and only the remaining text is shown.

That's a known problem of echo and to avoid this you can use printf.

printf "%s\n" "$*"

Why does the read command in bash work with a herestring but not with piped output?

The read works, but you need to ask for the values in the same subshell:

echo "1 2 3" | (read -r one two three && echo "one: $one, two: $two, three: $three")

An alternative is

read -r one two three < <( echo "1 2 3")


Related Topics



Leave a reply



Submit