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

[rc] read a line?

24 views
Skip to first unread message

Ian Zimmerman

unread,
Sep 24, 2015, 9:55:29 PM9/24/15
to
Does rc have a built-in analogous to POSIX sh's "read" ?

It looks like it doesn't, so what is the common / conventional way of
achieving the same effect?

--
Please *no* private copies of mailing list or newsgroup messages.
Rule 420: All persons more than eight miles high to leave the court.

Stephane Chazelas

unread,
Sep 25, 2015, 5:30:17 AM9/25/15
to
2015-09-24 18:43:17 -0700, Ian Zimmerman:
> Does rc have a built-in analogous to POSIX sh's "read" ?
>
> It looks like it doesn't, so what is the common / conventional way of
> achieving the same effect?
[...]

You generally don't want to process files line by line in
shells. See for instance:

https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice

If you want to prompt one line from the user on the terminal,
you can use:

var = ``(){head -n1}

For the same without the trailing newline:

nl = '
'
var = ``($nl){head -n1}

Replace "head -n1" with "line" (unfortunately, that command was
deprecated from the Unix standard a long time ago and not often
available on Unix-like systems), or "sed q" or "awk
'{print;exit}'"...

Except for "line", those commands will generally consume more that
one line of input if stdin is not a tty or is not seekable (will
read a full buffer, some of them will not seek back to the end
of the line).

Contrary to "read", those however won't post-process the input
before storing into $var (var = ``($nl){line} is like IFS= read
-r line).

If you want the special processing of "read" without -r and with
blanks in $FS, you'll probably want to do something like:

var = ``(){sh -c 'read var && printf %s "$var"'}

--
Stephane

Ian Zimmerman

unread,
Sep 26, 2015, 2:11:21 PM9/26/15
to
Hello, thanks for the very detailed and interesting response. I ended
up using "line" this time, because I needed to fix the script before the
next cron run and because I don't care very much about portability, or
even performance :) Nonetheless, I'm thinking about improvements.

On 2015-09-25 10:27 +0100, Stephane Chazelas wrote:

> You generally don't want to process files line by line in
> shells. See for instance:

[Read that.] Hmm. A lot of my tasks seems to involve the following
pattern: Read stdin, for each $line of stdin do foobar($line), where
foobar includes _multiple_ external programs (so connecting and
checking after them is most easily and naturally done with the shell).
Of course I am _not_ talking about text processing or formatting
programs like cut or sed. I know those cases are best handled with awk.

For instance, the script this time takes over after an existing program
foo which dumps many files in RFC 822 format into ~/.foo/. For each
file ~/.foo/bar, I need to add a header or two (most naturally done with
printf and cat in a sequence), and mail the result of that using
sendmail -i to myself.

The number of the files is not bounded, so I cannot just match them all
with a glob.

How would you do that without the evil "read -r" or "line"? I also
don't want to scatter the job among multiple script files, so please
don't suggest rewriting just the loop part in python.

Stephane Chazelas

unread,
Sep 28, 2015, 7:30:15 AM9/28/15
to
2015-09-26 10:59:11 -0700, Ian Zimmerman:
[...]
> For instance, the script this time takes over after an existing program
> foo which dumps many files in RFC 822 format into ~/.foo/. For each
> file ~/.foo/bar, I need to add a header or two (most naturally done with
> printf and cat in a sequence), and mail the result of that using
> sendmail -i to myself.
>
> The number of the files is not bounded, so I cannot just match them all
> with a glob.
>
> How would you do that without the evil "read -r" or "line"? I also
> don't want to scatter the job among multiple script files, so please
> don't suggest rewriting just the loop part in python.
[...]

globs are not limited other than by available memory. It's just
the number of arguments to commands being executed that are (on
some systems). (If you worry about the whole file list being
stored in memory, note that ls also does it to be able to sort
the list).

So you can do:

for (file in $HOME/.foo/bar/*) do-something-with $file

(see the "formail" command to reliably add headers).

Also note that rc functions are exported via the environment, so
you could do:

fn foo {
for (arg) do-something with $1
}

xargs rc -c 'foo $*' < list.txt

(assuming list.txt is in the format expected by xargs).

Or

fn quote-lines {
sed 's/''/&&/g;s/^/foo ''/;s/$/''/'
}

fn foo {
do-something with $1
}

quote-lines < list.txt | rc

(assuming one arg per line).

--
Stephane
0 new messages