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

how do I read printf escape chars from a file?

62 views
Skip to first unread message

Ed Morton

unread,
Apr 1, 2013, 10:42:44 AM4/1/13
to
Maybe I just need another coffee but I cannot figure out how to expand escape
chars for printing when I read my printf formatting string from a file:

$ cat file1
\n%s\n
$ awk '{printf $0, "foo"}' file1
\nfoo\n$

I was expecting to see the "\n"s turned into newlines! I tried
$ awk '{printf sprintf($0,""), "foo"}' file1
\n\n$

but as you can see that just somewhat predictably evaluated the %s to the null
string I specified and left the "\n"s while this:

$ awk '{printf sprintf("%s",$0), "foo"}' file1
\nfoo\n$

just copied my formatting string as is.

The point of the exercise is to count how many characters are in the string that
results from the print so I'd want this:

$ awk '{print length(sprintf($0,"foo")) }' file1
7

to tell me 5 ("foo" plus 2 newlines), not 7 ("foo" plus 2 backslashes plus 2 "n"s).

I could pipe the output of my printf to shell and read it back in with
co-processes but I think there's got to be a better staying alternative within awk.

Ed.

Ed Morton

unread,
Apr 1, 2013, 10:48:06 AM4/1/13
to
By the way, "\n" is one example. I know I could do a bunch of gsub()s for every
backslash-character combination but IMHO that's a much worse and far more
complicated (e.g. dealing with "\n" vs "\\n" vs "\\\n") solution than piping to
and reading back from shell.

Ed.

Kenny McCormack

unread,
Apr 1, 2013, 11:03:13 AM4/1/13
to
In article <kjc6ip$f5v$3...@dont-email.me>,
Ed Morton <morto...@gmail.com> wrote:
...
>By the way, "\n" is one example. I know I could do a bunch of gsub()s for
>every backslash-character combination but IMHO that's a much worse and far
>more complicated (e.g. dealing with "\n" vs "\\n" vs "\\\n") solution than
>piping to and reading back from shell.

As I think you've probably figured out by now, the answer is "No."

In both C and AWK (and probably other "C-like" languages), the \n thing is
entirely a source code level thing. The program never actually sees it.

One thing you could do is to machine-generate AWK code to the equivalent of:

sub("\\\\""n","\n")

(for all values of n - heh heh)

--
"That's the eternal flame."[7] The single became another worldwide hit.[8]

Hoffs was actually naked when she recorded the song, after being convinced
by Sigerson that Olivia Newton-John got her amazing performances by
recording everything while naked.[9]

(From: http://en.wikipedia.org/wiki/The_Bangles)

pk

unread,
Apr 1, 2013, 11:04:55 AM4/1/13
to
If awk hasn't had a chance of seeing the literal string in the body of the
program, your only option is to pass literals. Examples:

# works, because awk sees the "%s\n%s\n" string while parsing the
# program text and interpolates it

awk 'BEGIN{ esc="%s\n%s\n"; printf esc, "foo", "bar" }'

# does not work, because awk never sees the \n in full

awk 'BEGIN{ esc="%s\\"; esc=esc"n%s\\"; esc=esc"n"; printf esc, "foo","bar" }'

when reading the first argument to printf from the outside, you're in the
same situation as the second example.

In other words, it seems like it's not printf that understands \n and
other escape sequences, but the awk interpreter itself (if it has a chance
to see them).

Joe User

unread,
Apr 1, 2013, 11:43:55 AM4/1/13
to
On Mon, 01 Apr 2013 09:42:44 -0500, Ed Morton wrote:

> Maybe I just need another coffee but I cannot figure out how to expand
> escape chars for printing when I read my printf formatting string from a
> file:
>
> $ cat file1
> \n%s\n
> $ awk '{printf $0, "foo"}' file1
> \nfoo\n$
>
> I was expecting to see the "\n"s turned into newlines! I tried $ awk
> '{printf sprintf($0,""), "foo"}' file1 \n\n$

Interesting. I would have made the same mistake. The '\n' notation only
works in string constants. It looks like you will have to interpret the
input format string first. That would be complicated, because of various
levels of escaping (like '\\n').

Something like this seems to work:

while read -r l ; do echo -e "$l" ; done <file1 | \
awk '{print ; printf $0, "foo"}'

That may not be what you need, though. You could filter the string
through 'echo', using a system() call.

--
When I'm called off
I got a sawed off
Squeeze the trigger and
Bodies are hauled off

-- Ice Cube, NWA

Ed Morton

unread,
Apr 1, 2013, 12:23:16 PM4/1/13
to
On 4/1/2013 9:42 AM, Ed Morton wrote:
Thanks for all the replies so far. So it sounds like I need to use a coprocess.

Fine, now can anyone help me understand why this works:

---------
$ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print "" | cmd }' file1

foo
----------

but the equivalent (AFAIK) coprocess produces empty output:

----------
$ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print "" |& cmd;
cmd |& getline rslt; close(cmd); print rslt }' file1

---------

The constructed cmd and gawk versions are:

$ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print cmd}' file1
printf "\n%s\n" foo
$
$ awk --version
GNU Awk 4.0.0

Regard,
Ed.

Ed Morton

unread,
Apr 1, 2013, 12:48:47 PM4/1/13
to
FYI I solved my original problem of figuring out the length of the string by
just replacing all double-backslashes with a single char and then stripping all
remaining backslashes, e.g.:

$ cat file1
\n%s\n
\n%s\\n
\n%s\\\n

$ awk '{str=tmp=sprintf($0,"foo"); gsub(/[\\][\\]/,"X",tmp);
gsub(/[\\]/,"",tmp); print str, length(tmp) }' file1
\nfoo\n 5
\nfoo\\n 6
\nfoo\\\n 6

so now it's just bloody-mindedness trying to figure out why the coprocess
solution isn't working.

Ed.

Joe User

unread,
Apr 1, 2013, 1:22:40 PM4/1/13
to
On Mon, 01 Apr 2013 11:23:16 -0500, Ed Morton wrote:

> Fine, now can anyone help me understand why this works:
>
> ---------
> $ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print "" | cmd }'
> file1
>
> foo
> ----------
>
> but the equivalent (AFAIK) coprocess produces empty output:
>
> ----------
> $ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print "" |& cmd; cmd
> |& getline rslt; close(cmd); print rslt }' file1
>
> ---------

Because the first line of the cmd output is null (from '\n'). Loop on
the getline to get all of the output.



--
"Ubi non accusator, ibi non judex." (Where there is
no police, there is no law.)

-- Roman Law

Ed Morton

unread,
Apr 1, 2013, 1:27:43 PM4/1/13
to
On 4/1/2013 12:22 PM, Joe User wrote:
> On Mon, 01 Apr 2013 11:23:16 -0500, Ed Morton wrote:
>
>> Fine, now can anyone help me understand why this works:
>>
>> ---------
>> $ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print "" | cmd }'
>> file1
>>
>> foo
>> ----------
>>
>> but the equivalent (AFAIK) coprocess produces empty output:
>>
>> ----------
>> $ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print "" |& cmd; cmd
>> |& getline rslt; close(cmd); print rslt }' file1
>>
>> ---------
>
> Because the first line of the cmd output is null (from '\n'). Loop on
> the getline to get all of the output.

Doh! Of course! Thanks!

Ed.

Ed Morton

unread,
Apr 1, 2013, 1:33:13 PM4/1/13
to
On 4/1/2013 11:23 AM, Ed Morton wrote:
Thanks to Joe User for the tip. Here's how to do what i wanted (for anyone who
cares):

----------
$ awk '{ str="foo"; cmd="printf \"" $0 "\" " str; print "" |& cmd;
RS="\0"; cmd |& getline rslt; RS="\n"; close(cmd); printf "%s",rslt }' file1

foo
---------

Using RS="\0" will work in GNU awk, you might need to pick some different
control char in other awks to get the whole input read as a single string.
Obviously you can use a tmp variable to save/restore the original RS rather than
RS="\n" if you need to.

Ed.

Ed Morton

unread,
Apr 1, 2013, 1:34:28 PM4/1/13
to
but of course co-processes are gawk-specific anyway so I'll shut up now...

Bob Harris

unread,
Apr 30, 2013, 8:19:11 PM4/30/13
to
In article <kjc68o$f5v$1...@dont-email.me>,
cat -vte file1
0 new messages