I have a file called data
that looks like this:
orange
apple
pair
mango
grape
by using
awk -i inplace '{if (NR==4) $0=$0" is sweet"}{print}' data
, I get:
orange
apple
pair
mango is sweet
grape
I'm trying to achieve the same result through system()
but I can't figure out how.
echo is sweet | xclip
xclip -o
outputs is sweet
. I'm trying
awk -i inplace '{if (NR==4) $0=$0(system("xclip -o"))}{print}' data
and cat -n data
gives me
1 orange
2 apple
3 pair
4 is sweet
5 mango0
6 grape
Mango and grape have been pushed down one line, is sweet
is on its own on line 4 and mango
has a 0
in the end.
How can I change the command, and still use system("xclip -o")
so that line 4 says mango is sweet
, no appended 0
anywhere and no extra line, i.e. as with the first example?
I've super new with awk, and any help would be greatly appreciated.
system("foo")
just calls a program foo
and returns foo
s exit status (i.e. 0 for success, non-zero for failure). You want something that will capture the output of foo
(i.e. the text that foo
writes to stdout), not it's exit status, so calling system()
is the wrong approach. What you're seeing in your question is the output of xclip -o
mixed in with the output of your awk script.
This is how to do what you want (using date
instead of xclip
since it's a command everyone has available):
$ awk '
NR==4 {
cmd = "date +%F"
$0 = $0 " " ( (cmd | getline line) > 0 ? line : "N/A" )
close(cmd)
}
{ print }
' file
orange
apple
pair
mango 2021-09-06
grape
See https://www.gnu.org/software/gawk/manual/gawk.html#Getline_002fVariable_002fPipe for more information on the above and make sure to read http://awk.freeshell.org/AllAboutGetline to understand why I'm testing getline
's exit status before using it's output and why it's not something to be used in many situations (but this one is appropriate).
As a "one-liner" the above would be:
awk 'NR==4 { cmd = "date +%F"; $0 = $0 " " ( (cmd | getline line) > 0 ? line : "N/A" ); close(cmd) } { print }' file
In case it's useful, here's a function that would do the equivalent for awk as command substitution for shell:
$ cat cmdsub.awk
function cmdsub(cmd, line, n, out, cmd_stat, gl_stat) {
ERRNO = ""
while ( ( gl_stat=(cmd | getline line) ) > 0 ) {
out = (n++ ? out ORS : "") line
}
cmd_stat = close(cmd)
# If the above pipeline loop succeeded then all 3 error/status
# variables will be 0 but if the above pipeline loop failed then
#
# a) if cmd failed then
# i) ERRNO will be null
# ii) gl_stat will be 0 if 1st iteration, 1 otherwise.
# iii) cmd_stat will be non-zero if GNU awk,
# poorly defined for POSIX awk so YMMV.
#
# b) if cmd succeeded and getline failed then
# i) ERRNO will be non-null if GNU awk, null otherwise.
# ii) gl_stat will be less than zero
# iii) cmd_stat will be 0
if ( (ERRNO == "") && ( (gl_stat != 0) || (cmd_stat != 0) ) ) {
ERRNO = sprintf("CMD_STAT: %d, GL_STAT: %d", cmd_stat, gl_stat)
}
return out
}
BEGIN {
print "foo", cmdsub("date +%F"), "bar"
print (ERRNO ? "Failure: " ERRNO : "Success")
exit (ERRNO ? 1 : 0)
}
$ awk -f cmdsub.awk
foo 2021-09-06 bar
Success
$ echo $?
0
It uses a global variable named ERRNO
, the same as gawk already uses for getline failures (note: despite it's name, ERRNO
is a string, not a number), and relies on close(cmd)
returning a failure exit status when cmd
fails which is what happens in gawk but in other awks YMMV as the POSIX standard is vague on that point.
For more info on ERRNO
and close()
see: