Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

$(< file)

110 views
Skip to first unread message

Kenny McCormack

unread,
Nov 2, 2019, 12:59:52 PM11/2/19
to
I just found out today that if you do:

$ foo=$(< file)

in bash, foo gets assigned the contents of "file". It is equivalent to:

$ foo=$(cat file)

But note that:

$ < file

is a no-op. So, somehow, when it is wrapped in $(), it does a different
thing than when run directly.

Why is this?

How does the above work?

Do any other shells implement this functionality?

Is this documented (in "man bash") ?

--
"Every time Mitt opens his mouth, a swing state gets its wings."

(Should be on a bumper sticker)

Chris Elvidge

unread,
Nov 2, 2019, 1:24:14 PM11/2/19
to
On 02/11/2019 16:59, Kenny McCormack wrote:
> I just found out today that if you do:
>
> $ foo=$(< file)
>
> in bash, foo gets assigned the contents of "file". It is equivalent to:
>
> $ foo=$(cat file)
>
> But note that:
>
> $ < file >
> is a no-op. So, somehow, when it is wrapped in $(), it does a different
> thing than when run directly.

It's not a no-op. It is making the file contents the input of a command.
You haven't given it a command! Try e.g. tail < $file.
>
> Why is this?
>
> How does the above work?
>
> Do any other shells implement this functionality?
>
> Is this documented (in "man bash") ?
>

See: https://www.tldp.org/LDP/abs/html/io-redirection.html
man bash - around line 1539 (IIRC).


--

Chris Elvidge, England

Eric

unread,
Nov 2, 2019, 2:10:05 PM11/2/19
to
On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:
> I just found out today that if you do:
>
> $ foo=$(< file)
>
> in bash, foo gets assigned the contents of "file". It is equivalent to:
>
> $ foo=$(cat file)

It has the same result, but it is not equivalent.

> But note that:
>
> $ < file
>
> is a no-op. So, somehow, when it is wrapped in $(), it does a different
> thing than when run directly.

No, it doesn't. "< file" in a command line means take input from file.
If there is nothing else on the command line, there is nowhere for that
input to go so it seems like a no-op, but the file is actually opened
and read (and it will cause an error if the file does not exist).

$( ... ) means take the stuff between the brackets as a command line,
run it, and substitute the output into the original command line. In
your case above, "run" means "take input from file", so foo gets assigned
the contents of "file"

> Why is this?
>
> How does the above work?

Those questions are covered by the above paragraphs.

> Do any other shells implement this functionality?

The functionality of "< file" is pretty much universal. Any shell which
implements $( ... ) will do the same as above. A shell that doesn't
implement $( ... ) will probably implement ` ... ` which will do the
same thing.

> Is this documented (in "man bash") ?

"<" is documented as meaning "read input from ...", and $( ... ) is also
documented, so yes. There is no unusual or illogical behaviour here.

Eric
--
ms fnd in a lbry

Janis Papanagnou

unread,
Nov 2, 2019, 2:28:45 PM11/2/19
to
On 02.11.2019 17:59, Kenny McCormack wrote:
> I just found out today that if you do:
>
> $ foo=$(< file)
>
> in bash, foo gets assigned the contents of "file". It is equivalent to:
>
> $ foo=$(cat file)
>
> But note that:
>
> $ < file
>
> is a no-op. So, somehow, when it is wrapped in $(), it does a different
> thing than when run directly.
>
> Why is this?

The first command above is a syntactical shortcut for the second one.
Moreover, the first one is not needing a separet cat(1) process (and
of course also needing no subshell process)...

> How does the above work?

..., the shell will open the file and read it directly and assign its
contents to the variable.

>
> Do any other shells implement this functionality?

All modern shells. (Ksh since ksh88, Bash, Zsh; probably more shells.)

>
> Is this documented (in "man bash") ?

I would think so. Haven't you checked?

Janis

Barry Margolin

unread,
Nov 2, 2019, 2:36:46 PM11/2/19
to
In article <slrnqrrg9...@bruno.deptj.eu>, Eric <er...@deptj.eu>
wrote:

> On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:
> > I just found out today that if you do:
> >
> > $ foo=$(< file)
> >
> > in bash, foo gets assigned the contents of "file". It is equivalent to:
> >
> > $ foo=$(cat file)
>
> It has the same result, but it is not equivalent.
>
> > But note that:
> >
> > $ < file
> >
> > is a no-op. So, somehow, when it is wrapped in $(), it does a different
> > thing than when run directly.
>
> No, it doesn't. "< file" in a command line means take input from file.
> If there is nothing else on the command line, there is nowhere for that
> input to go so it seems like a no-op, but the file is actually opened
> and read (and it will cause an error if the file does not exist).
>
> $( ... ) means take the stuff between the brackets as a command line,
> run it, and substitute the output into the original command line. In
> your case above, "run" means "take input from file", so foo gets assigned
> the contents of "file"

But "< file" doesn't produce any output, as you pointed out in your
first paragraph, so why is the file contents being substituted into the
command line?

This is almost certain a special case in the shell -- it treats the null
command differently when it's inside $(...). In the Bash Manual section
on Command Substitution it says:

The command substitution $(cat file) can be replaced by the equivalent
but faster $(< file).

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Janis Papanagnou

unread,
Nov 2, 2019, 2:47:56 PM11/2/19
to
On 02.11.2019 19:36, Barry Margolin wrote:
> In article <slrnqrrg9...@bruno.deptj.eu>, Eric <er...@deptj.eu>
> wrote:
>
>> On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:
>>> I just found out today that if you do:
>>>
>>> $ foo=$(< file)
>>>
>>> in bash, foo gets assigned the contents of "file". It is equivalent to:
>>>
>>> $ foo=$(cat file)
>>
>> It has the same result, but it is not equivalent.
>>
>>> But note that:
>>>
>>> $ < file
>>>
>>> is a no-op. So, somehow, when it is wrapped in $(), it does a different
>>> thing than when run directly.
>>
>> No, it doesn't. "< file" in a command line means take input from file.
>> If there is nothing else on the command line, there is nowhere for that
>> input to go so it seems like a no-op, but the file is actually opened
>> and read (and it will cause an error if the file does not exist).
>>
>> $( ... ) means take the stuff between the brackets as a command line,
>> run it, and substitute the output into the original command line. In
>> your case above, "run" means "take input from file", so foo gets assigned
>> the contents of "file"
>
> But "< file" doesn't produce any output, as you pointed out in your
> first paragraph, so why is the file contents being substituted into the
> command line?

I don't think that anything is "substituted into the command line" here.
Compare it with its sibling for output:

> file

will just create a (empty) file.

Janis

>
> [...]


Christian Weisgerber

unread,
Nov 2, 2019, 3:30:09 PM11/2/19
to
On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:

> I just found out today that if you do:
> $ foo=$(< file)
> in bash, foo gets assigned the contents of "file". It is equivalent to:
> $ foo=$(cat file)

Yes. It's originally a ksh88 feature. It's an optimization that
implements this common use directly in the shell without incurring
the overhead of a fork() and exec().

> But note that:
> $ < file
> is a no-op. So, somehow, when it is wrapped in $(), it does a different
> thing than when run directly.
>
> Why is this?

Plain <file is an I/O redirection, but if there is no command that
performs any I/O, then nothing much happens. For $(<file), the
shell reads the file content and substitutes it. These are entirely
different constructs.

> Is this documented (in "man bash") ?

Yes, it is mentioned in the section "Command Substitution".

--
Christian "naddy" Weisgerber na...@mips.inka.de

Aragorn

unread,
Nov 2, 2019, 4:23:38 PM11/2/19
to
On 02.11.2019 at 18:47, Eric scribbled:

> On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:
>
> > But note that:
> >
> > $ < file
> >
> > is a no-op. So, somehow, when it is wrapped in $(), it does a
> > different thing than when run directly.
>
> No, it doesn't. "< file" in a command line means take input from file.
> If there is nothing else on the command line, there is nowhere for
> that input to go so it seems like a no-op, but the file is actually
> opened and read (and it will cause an error if the file does not
> exist).

Likewise,...

$ > FILE

... will open FILE for writing, but since there is no command in front
of the redirect, FILE will be truncated, and if FILE doesn't exist yet,
it will be created as an empty file.

--
With respect,
= Aragorn =

thed...@whitehouse.gov

unread,
Nov 2, 2019, 6:38:54 PM11/2/19
to
> Followup to: newsgroup
>I just found out today that if you do:
>
>$ foo=$(< file)
>
>in bash, foo gets assigned the contents of "file". It is equivalent to:
>
>$ foo=$(cat file)
> ..

Same as

$ foo=$(tr '\n' ' ' <file)

as well.

Ed Morton

unread,
Nov 2, 2019, 6:47:18 PM11/2/19
to
That would be fake news.

Ed.

Ben Bacarisse

unread,
Nov 2, 2019, 10:18:54 PM11/2/19
to
Eric <er...@deptj.eu> writes:

> On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:
>> I just found out today that if you do:
>>
>> $ foo=$(< file)
>>
>> in bash, foo gets assigned the contents of "file". It is equivalent to:
>>
>> $ foo=$(cat file)
>
> It has the same result, but it is not equivalent.
>
>> But note that:
>>
>> $ < file
>>
>> is a no-op. So, somehow, when it is wrapped in $(), it does a different
>> thing than when run directly.
>
> No, it doesn't. "< file" in a command line means take input from file.
> If there is nothing else on the command line, there is nowhere for that
> input to go so it seems like a no-op, but the file is actually opened
> and read (and it will cause an error if the file does not exist).
>
> $( ... ) means take the stuff between the brackets as a command line,
> run it, and substitute the output into the original command line. In
> your case above, "run" means "take input from file", so foo gets assigned
> the contents of "file"

This can't explain the feature. $(< file) is a special case which
behaves differently to running no command with input redirected from
'file'.

<cut>
>> Do any other shells implement this functionality?
>
> The functionality of "< file" is pretty much universal. Any shell which
> implements $( ... ) will do the same as above.

No. $(< file) is not defined for a POSIX shell. dash, for example,
does not interpret $(< file) in a spacial way and simply tries to run
no command after redirecting the input from 'file'.

<cut>
> "<" is documented as meaning "read input from ...", and $( ... ) is also
> documented, so yes. There is no unusual or illogical behaviour here.

Since $(< file) is a special case it needs to be documented and it is,
at least for bash and zsh. dash says nothing about it because it's not
a special form in a POSIX shell. It means the same as $(<file :) does
in shell like bash ans zsh.

--
Ben.

Kenny McCormack

unread,
Nov 3, 2019, 1:09:32 AM11/3/19
to
In article <87r22pl...@bsb.me.uk>,
Ben Bacarisse <ben.u...@bsb.me.uk> wrote:
...
>>> Do any other shells implement this functionality?
>>
>> The functionality of "< file" is pretty much universal. Any shell which
>> implements $( ... ) will do the same as above.
>
>No. $(< file) is not defined for a POSIX shell. dash, for example,
>does not interpret $(< file) in a spacial way and simply tries to run
>no command after redirecting the input from 'file'.
>
><cut>
>> "<" is documented as meaning "read input from ...", and $( ... ) is also
>> documented, so yes. There is no unusual or illogical behaviour here.
>
>Since $(< file) is a special case it needs to be documented and it is,
>at least for bash and zsh. dash says nothing about it because it's not
>a special form in a POSIX shell. It means the same as $(<file :) does
>in shell like bash ans zsh.

This is a good answer. Well done.

Pretty much everyone else reponding to this thread, other than Barry, seems
to have completely missed the point.

See the following text, which appears in "man bash", in the section
labelled "Command Substitution":

Bash performs the expansion by executing command in a subshell environ-
ment and replacing the command substitution with the standard output of
the command, with any trailing newlines deleted. Embedded newlines are
not deleted, but they may be removed during word splitting. The com-
mand substitution $(cat file) can be replaced by the equivalent but
faster $(< file).

--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/Rorschach

Oğuz

unread,
Nov 3, 2019, 3:54:57 AM11/3/19
to
On Sat, 02 Nov 2019 16:59:48 +0000, Kenny McCormack wrote:

> I just found out today that if you do:
>
> $ foo=$(< file)
>
> in bash, foo gets assigned the contents of "file". It is equivalent to:
>
> $ foo=$(cat file)

I wonder what keeps them from making it work with file descriptors too.
Since neither /dev/fd/* nor /proc/self/fd/* is portable across all
platforms, such a feature would be cool to have

Janis Papanagnou

unread,
Nov 3, 2019, 4:14:34 AM11/3/19
to
Can you give an example what you mean, what you want to achieve?

Ksh and Zsh for example support
exec 3<file ; foo=$( <&3 ) ; echo "$foo"


Janis

Oğuz

unread,
Nov 3, 2019, 4:21:13 AM11/3/19
to
Yes, I was talking about this, I didn't know ksh and zsh have it

>
>
> Janis

Kenny McCormack

unread,
Nov 3, 2019, 4:26:45 AM11/3/19
to
In article <qpke3p$6f4$1...@dont-email.me>,
Chris Elvidge <ch...@mshome.net> wrote:
>On 02/11/2019 16:59, Kenny McCormack wrote:
>> I just found out today that if you do:
>>
>> $ foo=$(< file)
>>
>> in bash, foo gets assigned the contents of "file". It is equivalent to:
>>
>> $ foo=$(cat file)
>>
>> But note that:
>>
>> $ < file >

Your editing software somehow inserted the > at the end. It was not in
the original text.

>> is a no-op. So, somehow, when it is wrapped in $(), it does a different
>> thing than when run directly.
>
>It's not a no-op. It is making the file contents the input of a command.

It is a no-op. The command as given does nothing.

In fact, it could be argued that both:

$ > file
and
$ < file

are shorthands for:

$ > file :
and
$ < file :

However, note that the first one (with >) is not useless, since it can be
(and frequently is) used to create a zero length file. The second one
(with <) is quite useless.

Others have explained how, indeed, wrapping it inside $() changes its
behavior. Kudos to them.

--
> No, I haven't, that's why I'm asking questions. If you won't help me,
> why don't you just go find your lost manhood elsewhere.

CLC in a nutshell.

Kenny McCormack

unread,
Nov 3, 2019, 4:28:09 AM11/3/19
to
In article <barmar-E7D77A....@reader.eternal-september.org>,
Barry Margolin <bar...@alum.mit.edu> wrote:
...
>But "< file" doesn't produce any output, as you pointed out in your
>first paragraph, so why is the file contents being substituted into the
>command line?
>
>This is almost certain a special case in the shell -- it treats the null
>command differently when it's inside $(...). In the Bash Manual section
>on Command Substitution it says:
>
>The command substitution $(cat file) can be replaced by the equivalent
>but faster $(< file).

Yup. You got it. Kudos.

--
I'm building a wall.

Janis Papanagnou

unread,
Nov 3, 2019, 5:37:43 AM11/3/19
to
On 03.11.2019 10:26, Kenny McCormack wrote:
> [...]
>
> In fact, it could be argued that both:
>
> $ > file
> and
> $ < file

(As pointed out elsewhere '< file' doesn't seem to make sense, so let's
consider the other case.)

>
> are shorthands for:
>
> $ > file :
> and
> $ < file :

But, in the general case, only because : is a special builtin that behaves
differently than a command. Compare

X=foo /bin/true >bar ; echo $X

X=foo : >bar ; echo $X

X=foo >bar ; echo $X

The latter two behave similar and different from the first one.

Since there need not be a command - Ksh for example describes in section
"Executing a Simple Command" the case "No Command Names or Arguments" without
even referring to a command; it's an own case - so that there's no need to
introduce the notion of a "shorthand". YMMV, of course. The original point
of this thread $(< foo) however is a shorthand construct, even with positive
operational effects (in shells that haven't $(cat foo) optimizations built in.

Janis

Eric

unread,
Nov 3, 2019, 7:10:06 AM11/3/19
to
On 2019-11-03, Janis Papanagnou <janis_pa...@hotmail.com> wrote:
8>< --------
> But, in the general case, only because : is a special builtin that behaves
> differently than a command. Compare
>
> X=foo /bin/true >bar ; echo $X
>
> X=foo : >bar ; echo $X
>
> X=foo >bar ; echo $X
>
> The latter two behave similar and different from the first one.

Actually, it's the last one that is different from the other two.

8>< --------

Janis Papanagnou

unread,
Nov 3, 2019, 8:01:13 AM11/3/19
to
It's depending on the shell. Ksh output is "", "foo", and "foo", respectively.

Janis

> 8>< --------
>
> Eric
>

Janis Papanagnou

unread,
Nov 3, 2019, 8:04:10 AM11/3/19
to
To be more precise; Ksh and Dash output is "", "foo", and "foo". (Not Bash or
Zsh.)

> Janis
>
>> 8>< --------
>>
>> Eric
>>
>

Christian Weisgerber

unread,
Nov 3, 2019, 1:30:10 PM11/3/19
to
On 2019-11-03, Eric <er...@deptj.eu> wrote:

>> X=foo /bin/true >bar ; echo $X
>> X=foo : >bar ; echo $X
>> X=foo >bar ; echo $X
>>
>> The latter two behave similar and different from the first one.
>
> Actually, it's the last one that is different from the other two.

POSIX says:
variable assignments preceding the invocation of a special built-in
utility remain in effect after the built-in completes; this shall
not be the case with a regular built-in or other utility.

And : is listed as a special built-in.


bash violates this by default; bash --posix is conformant.

Stephane Chazelas

unread,
Nov 3, 2019, 3:40:19 PM11/3/19
to
2019-11-02 16:59:48 -0000, Kenny McCormack:
> I just found out today that if you do:
>
> $ foo=$(< file)
>
> in bash, foo gets assigned the contents of "file". It is equivalent to:
[...]

You'll find lots of details on that at
https://unix.stackexchange.com/questions/189749/understanding-bashs-read-a-file-command-substitution/368663#368663

reproduced below for convenience:

$(<file) (also works with `<file`) is a special operator of
the Korn shell copied by zsh and bash. It does look a lot like
command substitution but it's not really.

In POSIX shells, a simple command is:

< file var1=value1 > file2 cmd 2> file3 args 3> file4

All parts are optional, you can have redirections only, command
only, assignment only or combinations.

If there are redirections but no command, the redirections are
performed (so a > file would open and truncate file), but then
nothing happens. So

< file

Opens file for reading, but then nothing happens as there's no
command. So the file is then closed and that's it. If $(< file)
was a simple command substitution, then it would expand to
nothing.

In the [1]POSIX specification, in $(script), if script consists
only of redirections, that produces unspecified results. That's
to allow that special behaviour of the Korn shell.

In ksh (here tested with ksh93u+), if the script consists of one
and only one simple command (though comments are allowed before
and after) that consists only of redirections (no command, no
assignment) and if the first redirection is a stdin (fd 0) input
only (<, << or <<<) redirection, so:

* $(< file)
* $(0< file)
* $(<&3) (also $(0>&3) actually as that's in effect the same
operator)
* $(< file > foo 2> $(whatever))

but not:

* $(> foo < file)
* nor $(0<> file)
* nor $(< file; sleep 1)
* nor $(< file; < file2)

then

* all but the first redirection are ignored (they are parsed
away)
* and it expands to the content of the file/heredoc/herestring
(or whatever can be read from the file descriptor if using
things like <&3) minus the trailing newline characters.

as if using $(cat < file) except that

* the reading is done internally by the shell and not by cat
* no pipe nor extra process is involved
* as a consequence of the above, since the code inside is not
run in a subshell, any modification remain thereafter (as in
$(<${file=foo.txt}) or $(<file$((++n))))
* read errors (though not errors while opening files or
duplicating file descriptors) are silently ignored.

In zsh, it's the same except that that special behaviour is only
triggered when there's only one file input redirection (<file or
0< file, no <&3, <<<here, < a < b...)

However, except when emulating other shells, in:

< file
<&3
<<< here...

that is when there are only input redirections without commands,
outside of command substitution, zsh runs the $READNULLCMD (a
pager by default), and when there are both input and output
redirections, the $NULLCMD (cat by default), so even if $(<&3)
is not recognized as that special operator, it will still work
like in ksh though by invoking a pager to do it (that pager
acting like cat since its stdout will be a pipe).

However while ksh's $(< a < b) would expand to the content of a,
in zsh, it expands to the content of a and b (or just b if the
multios option is disabled), $(< a > b) would copy a to b and
expand to nothing, etc.

bash has a similar operator but with a few differences:

* comments are allowed before but not after:

echo "$(
# getting the content of file
< file)"

works but:

echo "$(< file
# getting the content of file
)"

expands to nothing.

* like in zsh, only one file stdin redirection, though there's
no fall back to a $READNULLCMD, so $(<&3), $(< a < b) do
perform the redirections but expand to nothing.

* for some reason, while bash does not invoke cat, it still
forks a process that feeds the content of the file through a
pipe making it much less of an optimisation than in other
shells. It's in effect like a $(cat < file) where cat would
be a builtin cat.
* as a consequence of the above, any change made within are
lost afterwards (in the $(<${file=foo.txt}), mentioned above
for instance, that $file assignment is lost afterwards).

In bash, IFS= read -rd '' var < file (also works in zsh) is a
more effective way to read the content of a text file into a
variable. It also has the benefit of preserving the trailing
newline characters. See also $mapfile[file] in zsh (in the
zsh/mapfile module and only for regular files) which also works
with binary files.

Note that the pdksh-based variants of ksh have a few variations
compared to ksh93. Of interest, in mksh (one of those
pdksh-derived shells), in

var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)

is optimised in that the content of the here document (without
the trailing characters) is expanded without a temporary file or
pipe being used as is otherwise the case for here documents,
which makes it an effective multi-line quoting syntax.

To be portable to all versions of ksh, zsh and bash, best is to
limit to only $(<file) avoiding comments and bearing in mind
that modifications to variables made within may or may not be
preserved.

References

Visible links
1. http://pubs.opengroup.org/onlinepubs/9699919799.2016edition/utilities/V3_chap02.html#tag_18_06_03

Stephane Chazelas

unread,
Nov 3, 2019, 3:40:19 PM11/3/19
to
2019-11-02 18:48:17 -0000, Christian Weisgerber:
> On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:
>
> > I just found out today that if you do:
> > $ foo=$(< file)
> > in bash, foo gets assigned the contents of "file". It is equivalent to:
> > $ foo=$(cat file)
>
> Yes. It's originally a ksh88 feature.
[...]

It was already there in ksh85.

--
Stephane

Helmut Waitzmann

unread,
Nov 3, 2019, 4:56:09 PM11/3/19
to
Janis Papanagnou <janis_pa...@hotmail.com>:
>On 03.11.2019 14:01, Janis Papanagnou wrote:
>> On 03.11.2019 12:57, Eric wrote:
>>> On 2019-11-03, Janis Papanagnou <janis_pa...@hotmail.com> wrote:
>>> 8>< --------
>>>> But, in the general case, only because : is a special builtin
>>>> that behaves differently than a command. Compare
>>>>
>>>> X=foo /bin/true >bar ; echo $X
>>>>
>>>> X=foo : >bar ; echo $X
>>>>
>>>> X=foo >bar ; echo $X
>>>>

> To be more precise; Ksh and Dash output is "", "foo", and
> "foo".

Ksh and Dash agree with POSIX: As ":" is a special built‐in
utility, the variable assignment shall occur in the current shell
environment.

> (Not Bash or Zsh.)
>

In a quite ancient Bash (I don't have a contemporary one at
hand), the behaviour depends on whether Bash runs in POSIX
compatibility mode (which it does if invoked using "sh" as the
invocation name).  In the following example, there are two bash
invocations.  The first one is in non-POSIX mode; the second one
is in POSIX mode:


(
execlp()
{
exec bash -c '
f="${1?}" && arg0="${2?}" && shift 2 &&
exec -a "$arg0" -- "$f" "$@"
' bash "$@"
}
unset X
# non-POSIX mode:
(
set execlp bash bash -c -- '
X=foo :
printf '\''X%s\n'\'' "${X+=}${X- undefined}"
' sh &&
printf ' %s' "$@" && echo &&
"$@"
)
# POSIX mode:
(
set execlp bash sh -c -- '
X=foo :
printf '\''X%s\n'\'' "${X+=}${X- undefined}"
' sh &&
printf ' %s' "$@" && echo &&
"$@"
)
)

Casper H.S. Dik

unread,
Nov 4, 2019, 7:33:25 AM11/4/19
to
Chris Elvidge <ch...@mshome.net> writes:

>It's not a no-op. It is making the file contents the input of a command.
>You haven't given it a command! Try e.g. tail < $file.

It certainly opens the file. You can also try:

$ < $file tail


Casper

Kaz Kylheku

unread,
Nov 4, 2019, 9:57:54 PM11/4/19
to
On 2019-11-02, Kenny McCormack <gaz...@shell.xmission.com> wrote:
> I just found out today that if you do:
>
> $ foo=$(< file)
>
> in bash, foo gets assigned the contents of "file". It is equivalent to:
>
> $ foo=$(cat file)
>
> But note that:
>
> $ < file
>
> is a no-op.

Well, I mean:

$ < abcdfw
bash: abcdfw: NO such file or directory

Detecting non-existence and producing a failed exit status is a bit
of an "op" rather than "no-op".

And of course, the inverse:

$ > file

creates file, if necessary, and truncates to zero.

> So, somehow, when it is wrapped in $(), it does a different
> thing than when run directly.
>
> Why is this?

Because the processing of the "command substitution" has the opportunity
to detect the special case of "there is no command here, only
redirections" and do something different.

> How does the above work?
>
> Do any other shells implement this functionality?

(Hopefully not; a better thing to do is to recognize $(cat ...)
and optimize *that* instead of documenting $(cat ...) as being less
performant and providing new syntax.)

> Is this documented (in "man bash") ?

Yes; the feature was evidently added to bash between bash-2.01.1 and
bash-2.0.2-alpha1. See the NEWS file:

http://git.savannah.gnu.org/cgit/bash.git/tree/NEWS

Though the entries in that file are undated, it looks like it was in
1998-ish, when some kids in senior year college were just born.

Kenny McCormack

unread,
Nov 5, 2019, 1:36:14 AM11/5/19
to
In article <201911041...@kylheku.com>,
Kaz Kylheku <493-87...@kylheku.com> wrote:
...
>> But note that:
>>
>> $ < file
>>
>> is a no-op.
>
>Well, I mean:
>
> $ < abcdfw
> bash: abcdfw: NO such file or directory
>
>Detecting non-existence and producing a failed exit status is a bit
>of an "op" rather than "no-op".

Heh heh. What is a no-op, indeed...?

I did realize somewhere along the way that it could be used as an
existence/readability test.

Keep in mind that it is readability, not just existence.
Try it with /etc/shadow...

>And of course, the inverse:
>
> $ > file
>
>creates file, if necessary, and truncates to zero.

Yep - that's a well-known trick.

>> So, somehow, when it is wrapped in $(), it does a different
>> thing than when run directly.
>>
>> Why is this?
>
>Because the processing of the "command substitution" has the opportunity
>to detect the special case of "there is no command here, only
>redirections" and do something different.

Yep.

>> How does the above work?
>>
>> Do any other shells implement this functionality?
>
>(Hopefully not; a better thing to do is to recognize $(cat ...)
>and optimize *that* instead of documenting $(cat ...) as being less
>performant and providing new syntax.)

Good point.

BTW, apparently a few other shells (ksh, zsh, maybe others) also have this
(according to some posters). Apparently, Zsh's implementation is less
weird than bash's.

--
The people who were, are, and always will be, wrong about everything, are still
calling *us* "libtards"...

(John Fugelsang)

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 7, 2019, 10:11:55 AM11/7/19
to
In article <qpkhsq$khk$1...@news-1.m-online.net>,
Janis Papanagnou <janis_pa...@hotmail.com> wrote:

>> Do any other shells implement this functionality?
>
>All modern shells. (Ksh since ksh88, Bash, Zsh; probably more shells.)
>

Sorry, but I cannot see this as an indication for a so called "modern shell".

--
EMail:jo...@schily.net (home) Jörg Schilling D-13353 Berlin
joerg.s...@fokus.fraunhofer.de (work) Blog: http://schily.blogspot.com/
URL: http://cdrecord.org/private/ http://sourceforge.net/projects/schilytools/files/

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 7, 2019, 10:19:00 AM11/7/19
to
In article <5dc01a91$0$10263$e4fe...@news.xs4all.nl>,
This is not in the POSIX shell syntax description.

dash e.g. does not work this way.

All shells that are based on the original Bourne Shell source support:

< file simplecommand

They do not support something like:

< file if ...

dash e.g. does not even support

< file simplecommand

Kenny McCormack

unread,
Nov 7, 2019, 10:39:14 AM11/7/19
to
In article <qq1ckv$ru$3...@news.albasani.net>,
<Joerg.S...@fokus.fraunhofer.de> wrote:
...
>dash e.g. does not even support
>
> < file simplecommand

Really?

% dash
$ < /etc/group wc
75 75 1021
$

--
People who want to share their religious views with you
almost never want you to share yours with them. -- Dave Barry

Kenny McCormack

unread,
Nov 7, 2019, 10:45:11 AM11/7/19
to
In article <qq1c7n$ru$2...@news.albasani.net>,
<Joerg.S...@fokus.fraunhofer.de> wrote:
...
>>> Do any other shells implement this functionality?
>>
>>All modern shells. (Ksh since ksh88, Bash, Zsh; probably more shells.)
>
>Sorry, but I cannot see this as an indication for a so called "modern shell".

I get what you're doing - that is, expressing disdain for the functionality
under discussion in this thread and implying that it is not "modern" - but,
really, no one ever claimed that it was a requirement of a shell to be
considered "modern".

By way of analogy, I could claim that all modern shells have more than
10,000 lines of source code, but no sane person would think that I am
asserting that that is a requirement for modernity.

--
"Insisting on perfect safety is for people who don't have the balls to live
in the real world."

- Mary Shafer, NASA Ames Dryden -

Janis Papanagnou

unread,
Nov 7, 2019, 12:33:41 PM11/7/19
to
On 07.11.2019 16:45, Kenny McCormack wrote:
> In article <qq1c7n$ru$2...@news.albasani.net>,
> <Joerg.S...@fokus.fraunhofer.de> wrote:
> ...
>>>> Do any other shells implement this functionality?
>>>
>>> All modern shells. (Ksh since ksh88, Bash, Zsh; probably more shells.)
>>
>> Sorry, but I cannot see this as an indication for a so called "modern shell".

(Strangely I didn't see your post, so answering on the followup.)

If you mean, with respect to $(<foo) , this feature's existance in a shell
to be an indication for a "modern shell", let me clarify...

No, that's not a dedicated "modern" feature of a "modern shell". First,
it's a very old feature[*], so it can hardly be called modern. But all
the "modern shells" (and even other contemporary shells) have it.[**]

Janis

[*] I was really astonished to hear that the OP did not know about it; I
remember to have used it myself about three decades ago with ksh88, and
thus assumed it was common knowledge, specifically if good performance or
terse code patterns are requested.

[**] I abstained from claiming that all contemporary shells would support
it because that is probably just not true.

Kaz Kylheku

unread,
Nov 7, 2019, 3:09:08 PM11/7/19
to
On 2019-11-07, Kenny McCormack <gaz...@shell.xmission.com> wrote:
> In article <qq1c7n$ru$2...@news.albasani.net>,
> <Joerg.S...@fokus.fraunhofer.de> wrote:
> ...
>>>> Do any other shells implement this functionality?
>>>
>>>All modern shells. (Ksh since ksh88, Bash, Zsh; probably more shells.)
>>
>>Sorry, but I cannot see this as an indication for a so called "modern shell".
>
> I get what you're doing - that is, expressing disdain for the functionality
> under discussion in this thread and implying that it is not "modern" - but,

Maybe the comment is more about the combination of "modern" and "ksh88".

Kaz Kylheku

unread,
Nov 7, 2019, 3:15:26 PM11/7/19
to
On 2019-11-07, Kenny McCormack <gaz...@shell.xmission.com> wrote:
> In article <qq1ckv$ru$3...@news.albasani.net>,
> <Joerg.S...@fokus.fraunhofer.de> wrote:
> ...
>>dash e.g. does not even support
>>
>> < file simplecommand
>
> Really?
>
> % dash
> $ < /etc/group wc
> 75 75 1021
> $

Furthermore, see the history:

https://www.in-ulm.de/~mascheck/various/ash/

There is a note suggesting that a bug was fixed in this area in 1993.
Under the heading "4.4BSD (06/'93)", one of the points says:
"redirections may precede non-simple commands".
That also seems to imply that redirections could precede simple
commands before this release. "wc" is a simple command.

(Maybe there was a regression dash since then and was fixed again?)

Janis Papanagnou

unread,
Nov 7, 2019, 4:27:42 PM11/7/19
to
Maybe. Only the OP can clarify.

But the "since ksh88" was anyway just an additional information; usually
we have ksh93 available today. That said; even [old] ksh88 had features
that e.g. Bash caught up only many years later, so one could have called
it modern even in the early third millennium. Ksh93, meanwhile also 25+
years old, supports and supported many powerful features that the popular
Bash (for example) still doesn't support.

Janis

Geoff Clare

unread,
Nov 8, 2019, 8:41:08 AM11/8/19
to
Joerg.Schilling wrote:

> In article <5dc01a91$0$10263$e4fe...@news.xs4all.nl>,
> Casper H.S. Dik <Caspe...@OrSPaMcle.COM> wrote:
>>
>>$ < $file tail
>
> This is not in the POSIX shell syntax description.

Yes is it. Relevant lines from the formal grammar:

simple_command : cmd_prefix cmd_word cmd_suffix

cmd_prefix : io_redirect

io_redirect : io_file

io_file : '<' filename

filename : WORD

--
Geoff Clare <net...@gclare.org.uk>

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 14, 2019, 9:31:06 AM11/14/19
to
In article <qq1dqu$qe$1...@news.xmission.com>,
Kenny McCormack <gaz...@shell.xmission.com> wrote:
>In article <qq1ckv$ru$3...@news.albasani.net>,
> <Joerg.S...@fokus.fraunhofer.de> wrote:
>...
>>dash e.g. does not even support
>>
>> < file simplecommand
>
>Really?
>
>% dash
>$ < /etc/group wc
> 75 75 1021

OK, this was a mistake ... but I just had to modify a change in my autoconf
implementation becaue of dash....

Try:

< /etc/passwd (cat)

with dash and other shells

It works with all Bourne Shell derived shells

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 14, 2019, 9:43:35 AM11/14/19
to
In article <ei7g9g-...@ID-313840.user.individual.net>,
Geoff Clare <net...@gclare.org.uk> wrote:
>Joerg.Schilling wrote:
>
>> In article <5dc01a91$0$10263$e4fe...@news.xs4all.nl>,
>> Casper H.S. Dik <Caspe...@OrSPaMcle.COM> wrote:
>>>
>>>$ < $file tail
>>
>> This is not in the POSIX shell syntax description.
>
>Yes is it. Relevant lines from the formal grammar:

The problem here is that too many claims from too many people make it hard to
decide what the statement for a specific post is.

Even if it looks obvious, the poster may have had something different in mind
while writing a reply.

What we need to distinct here is:

- < file simplecommand

- < file (simplecommand)

- < file compound command

- $(< file)

The original post was about the last one and I am sure you will confirm that
this is not supported by POSIX.

The dash problem is the difference between the first two...

The second example is supported by all shells, based on the Bourne Shell
source, which includes ksh.

Stephane Chazelas

unread,
Nov 14, 2019, 12:00:23 PM11/14/19
to
2019-11-14 14:43:31 +0000, Joerg.S...@fokus.fraunhofer.de:
[...]
> - < file (simplecommand)
>
> - < file compound command
>
> - $(< file)
>
> The original post was about the last one and I am sure you will confirm that
> this is not supported by POSIX.
[...]

The first and third are unspecified by POSIX, the third
*explicitely*.

So portable scripts mustn't have anything like that and
compliant implementations can do whatever they want on those
non-standard codes.

Note that (simplecommand) is a "compound command".

For:

< file for i

< file { ...

< file any-other-reserved-word

POSIX currently requires shells to interpret the "for" and "{"
or any other reserved word as commands instead of keywords,
which is not particularly useful. I made the case for it at
http://austingroupbugs.net/view.php?id=1276#c4509

There are further complications with the "time" keyword vs
command, and "export"/"readonly".

--
Stephane

Keith Thompson

unread,
Nov 14, 2019, 1:29:51 PM11/14/19
to
Joerg.S...@fokus.fraunhofer.de writes:
> In article <qq1dqu$qe$1...@news.xmission.com>,
> Kenny McCormack <gaz...@shell.xmission.com> wrote:
>>In article <qq1ckv$ru$3...@news.albasani.net>,
>> <Joerg.S...@fokus.fraunhofer.de> wrote:
>>...
>>>dash e.g. does not even support
>>>
>>> < file simplecommand
>>
>>Really?
>>
>>% dash
>>$ < /etc/group wc
>> 75 75 1021
>
> OK, this was a mistake ... but I just had to modify a change in my autoconf
> implementation becaue of dash....
>
> Try:
>
> < /etc/passwd (cat)
>
> with dash and other shells
>
> It works with all Bourne Shell derived shells

Apparently not all of them. (Using wc rather than cat because I don't
want to share my /etc/passwd file, but the results are the same.)

$ echo $BASH_VERSION
4.4.20(1)-release
$ < /etc/motd wc
19 73 657
$ < /etc/motd (wc)
bash: syntax error near unexpected token `('
$

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 20, 2019, 3:17:06 PM11/20/19
to
In article <lny2wi1...@kst-u.example.com>,
Keith Thompson <ks...@mib.org> wrote:
>Joerg.S...@fokus.fraunhofer.de writes:

>> It works with all Bourne Shell derived shells
>
>Apparently not all of them. (Using wc rather than cat because I don't
>want to share my /etc/passwd file, but the results are the same.)
>
>$ echo $BASH_VERSION
>4.4.20(1)-release
>$ < /etc/motd wc
> 19 73 657
>$ < /etc/motd (wc)
>bash: syntax error near unexpected token `('
>$

You did not read my last message :-(

bash is not a Bourne Shell derived shell

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 20, 2019, 3:25:52 PM11/20/19
to
In article <20191114165725....@chaz.gmail.com>,
Stephane Chazelas <stephane...@gmail.com> wrote:

>For:
>
>< file for i
>
>< file { ...
>
>< file any-other-reserved-word
>
>POSIX currently requires shells to interpret the "for" and "{"
>or any other reserved word as commands instead of keywords,
>which is not particularly useful. I made the case for it at
>http://austingroupbugs.net/view.php?id=1276#c4509

This is not a good idea since this behavior was not introduced by POSIX but
rather by the Bourne Shell and inherited by ksh.

So what you like to see is a change in the behavior that invalidates old
implementations.

Keith Thompson

unread,
Nov 21, 2019, 1:26:12 AM11/21/19
to
Joerg.S...@fokus.fraunhofer.de writes:
> In article <lny2wi1...@kst-u.example.com>,
> Keith Thompson <ks...@mib.org> wrote:
>>Joerg.S...@fokus.fraunhofer.de writes:
>
>>> It works with all Bourne Shell derived shells
>>
>>Apparently not all of them. (Using wc rather than cat because I don't
>>want to share my /etc/passwd file, but the results are the same.)
>>
>>$ echo $BASH_VERSION
>>4.4.20(1)-release
>>$ < /etc/motd wc
>> 19 73 657
>>$ < /etc/motd (wc)
>>bash: syntax error near unexpected token `('
>>$
>
> You did not read my last message :-(

I did.

> bash is not a Bourne Shell derived shell

I suppose that depends on what you mean by "derived". It's certainly
designed to emulate it, if not perfectly (as csh and fish are
definitely not). I don't think bash shares any source code with
the original Bourne shell, but I'm not sure which other shells do,
if any.

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 21, 2019, 1:11:45 PM11/21/19
to
In article <lnv9rdz...@kst-u.example.com>,
Keith Thompson <ks...@mib.org> wrote:
>Joerg.S...@fokus.fraunhofer.de writes:

>> bash is not a Bourne Shell derived shell
>
>I suppose that depends on what you mean by "derived". It's certainly
>designed to emulate it, if not perfectly (as csh and fish are
>definitely not). I don't think bash shares any source code with
>the original Bourne shell, but I'm not sure which other shells do,
>if any.

Derived means "based on" and bash is not based on the Bourne Shell.

Given that it implements some strange things that are not in the standard but
work the same way in the Bourne Shell, I however believe that the author of
bash had access to the Bourne Shell Source while writing bash.

What you may have in mind is "leaned on"....

BTW: both Bourne Shell and csh are based on the Thompson shell sources.

ksh is based on the Bourne Shell sources.
ksh sources up to ksh88 look vey similar to the Burne Shell sources.
ksh93 includes a major rewrite to implement virtual sub-shells but still has a
lot of code that is simliar if not identical.

"bosh" is also based on the Bourne Shell sources.

Keith Thompson

unread,
Nov 21, 2019, 2:38:20 PM11/21/19
to
Joerg.S...@fokus.fraunhofer.de writes:
> In article <lnv9rdz...@kst-u.example.com>,
> Keith Thompson <ks...@mib.org> wrote:
>>Joerg.S...@fokus.fraunhofer.de writes:
>
>>> bash is not a Bourne Shell derived shell
>>
>>I suppose that depends on what you mean by "derived". It's certainly
>>designed to emulate it, if not perfectly (as csh and fish are
>>definitely not). I don't think bash shares any source code with
>>the original Bourne shell, but I'm not sure which other shells do,
>>if any.
>
> Derived means "based on" and bash is not based on the Bourne Shell.

I don't think "based on" is any clearer than "derived".

bash was very clearly intended as a replacement for the Bourne shell.
The name "Bourne-Again SHell" was derived from the name of the Bourne
shell. Quoting its documentation:

Bash implements essentially the same grammar, parameter and
variable expansion, redirection, and quoting as the Bourne Shell.

Any disagreement here is about the meaning of "derived" or "based on",
not about the actual characteristics of any shell.

> Given that it implements some strange things that are not in the standard but
> work the same way in the Bourne Shell, I however believe that the author of
> bash had access to the Bourne Shell Source while writing bash.
>
> What you may have in mind is "leaned on"....
>
> BTW: both Bourne Shell and csh are based on the Thompson shell sources.

When you say "based on", do you mean that some of the Thompson shell
(no relation) source code was carried over? Bourne shell and csh
are different enough that I'd be mildly surprised if they share a
common ancestry, but it's certainly possible.

> ksh is based on the Bourne Shell sources.
> ksh sources up to ksh88 look vey similar to the Burne Shell sources.
> ksh93 includes a major rewrite to implement virtual sub-shells but
> still has a lot of code that is simliar if not identical.
>
> "bosh" is also based on the Bourne Shell sources.

--

Kaz Kylheku

unread,
Nov 22, 2019, 12:54:27 AM11/22/19
to
On 2019-11-21, Joerg.S...@fokus.fraunhofer.de <Joerg.S...@fokus.fraunhofer.de> wrote:
> In article <lnv9rdz...@kst-u.example.com>,
> Keith Thompson <ks...@mib.org> wrote:
>>Joerg.S...@fokus.fraunhofer.de writes:
>
>>> bash is not a Bourne Shell derived shell
>>
>>I suppose that depends on what you mean by "derived". It's certainly
>>designed to emulate it, if not perfectly (as csh and fish are
>>definitely not). I don't think bash shares any source code with
>>the original Bourne shell, but I'm not sure which other shells do,
>>if any.
>
> Derived means "based on" and bash is not based on the Bourne Shell.
>
> Given that it implements some strange things that are not in the standard but
> work the same way in the Bourne Shell, I however believe that the author of
> bash had access to the Bourne Shell Source while writing bash.

More plausibly, Bash is widely used and many users have thrown all sorts
of scripts at it, those scripts having all sorts of history of having
been run with other shells, and reported the problems.

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 22, 2019, 5:40:54 AM11/22/19
to
In article <ln1ru1y...@kst-u.example.com>,
Keith Thompson <ks...@mib.org> wrote:

>bash was very clearly intended as a replacement for the Bourne shell.
>The name "Bourne-Again SHell" was derived from the name of the Bourne
>shell. Quoting its documentation:

The fact that it uses this name does not imply anything on its code or internal
structures.

> Bash implements essentially the same grammar, parameter and
> variable expansion, redirection, and quoting as the Bourne Shell.
>
>Any disagreement here is about the meaning of "derived" or "based on",
>not about the actual characteristics of any shell.

A car that uses a steering wheel is not "based on" an older car that also uses
a steering wheel. It just implements a similar user interface for convenience.

A car that implements a servo steering may be based on either hydraulics or on
an electrical motor. Both cars feel smiliar when you drive them, they still do
not share the same technology and thus you cannot call one to be "based on"
the other.

>> BTW: both Bourne Shell and csh are based on the Thompson shell sources.
>
>When you say "based on", do you mean that some of the Thompson shell
>(no relation) source code was carried over? Bourne shell and csh
>are different enough that I'd be mildly surprised if they share a
>common ancestry, but it's certainly possible.

This is what Stephen Bourne said on a talk and I tend to believe him.

Joerg.S...@fokus.fraunhofer.de

unread,
Nov 22, 2019, 5:41:51 AM11/22/19
to
In article <201911212...@kylheku.com>,
Kaz Kylheku <493-87...@kylheku.com> wrote:

>> Given that it implements some strange things that are not in the standard but
>> work the same way in the Bourne Shell, I however believe that the author of
>> bash had access to the Bourne Shell Source while writing bash.
>
>More plausibly, Bash is widely used and many users have thrown all sorts
>of scripts at it, those scripts having all sorts of history of having
>been run with other shells, and reported the problems.

I do not tend to reimplement bugs from other software, do you?

Keith Thompson

unread,
Nov 22, 2019, 12:46:53 PM11/22/19
to
Joerg.S...@fokus.fraunhofer.de writes:
> In article <ln1ru1y...@kst-u.example.com>,
> Keith Thompson <ks...@mib.org> wrote:
>>bash was very clearly intended as a replacement for the Bourne shell.
>>The name "Bourne-Again SHell" was derived from the name of the Bourne
>>shell. Quoting its documentation:
>
> The fact that it uses this name does not imply anything on its code or
> internal structures.

I never said it did. As I wrote before:

>> Any disagreement here is about the meaning of "derived" or "based on",
>> not about the actual characteristics of any shell.

[...]
0 new messages