print block of text to file from awk script [banner like]

拥有回忆 提交于 2019-12-11 11:48:51

问题


I have awk script doing some processing and sending it's output to a file. How would I writeout in BEGIN block of my awk program a banner-like message to that file first, something like bash heredoc.

I know I could use multiple print commands, but is there some way of having one print command but preserving multiline text with newlines etc.

So the output should look something like this:

#########################################
#      generated by some author         #
#        ENVIRON["VAR"]
#########################################

Additional problem of nice formatting is that ENVIRON["VAR"] should be expanded there in a middle of string.


回答1:


The simple way is to use a heredoc and save it in an awk variable:

VAR="whatever"
awk -v var="\
#########################################
#      generated by some author         #
#        $VAR
#########################################" '
BEGIN{ print var }
'
#########################################
#      generated by some author         #
#        whatever
#########################################

Alternatively, this may be more than you wanted, but below is the command I use to provide something a bit better than just here docs in awk. I find it absolutely invaluable when adding template text to multiple files..

It's a shell script which takes an awk script with slightly extended syntax (to facilitate here documents) as input, invokes gawk to transform that extended syntax to normal awk print statements, and then calls gawk again to execute the resulting script.

I call it "epawk" for "extended print" awk and what follows is the tool plus several examples of how to use it. When you invoke it instead of invoking awk directly you can write scripts that include blocks of pre-formatted text for printing like you'd want to with a here-doc (the space before each # is a tab character):

$ export VAR="whatever"
$ epawk 'BEGIN {
    print <<-!
        #########################################
        #      generated by some author         #
        #        "ENVIRON["VAR"]"
        #########################################
    !
}'
#########################################
#      generated by some author         #
#        whatever
#########################################

It works by creating an awk script from your awk script and then executing it. If you'd just like to see the script that is being generated, epawk will print the generated script instead of executing it if you give it the -X argument, e.g.:

$ epawk -X 'BEGIN {
    print <<-!
        #########################################
        #      generated by some author         #
        #        "ENVIRON["VAR"]"
        #########################################
    !
}'
BEGIN {
print "#########################################"
print "#      generated by some author         #"
print "#        "ENVIRON["VAR"]""
print "#########################################"
}

THE SCRIPT:

#!/bin/bash
# The above must be the first line of this script as bash or zsh is
# required for the shell array reference syntax used in this script.

##########################################################
# Extended Print AWK
#
# Allows printing of pre-formatted blocks of multi-line text in awk scripts.
#
# Before invoking the tool, do the following IN ORDER:
#
# 1) Start each block of pre-formatted text in your script with
#       print << TERMINATOR
#    on it's own line and end it with 
#   TERMINATOR
#    on it's own line. TERMINATOR can be any sequence of non-blank characters
#    you like. Spaces are allowed around the symbols but are not required.
#    If << is followed by -, e.g.:
#       print <<- TERMINATOR
#    then all leading tabs are removed from the block of pre-formatted
#    text (just like shell here documents), if it's followed by + instead, e.g.:
#       print <<+ TERMINATOR
#    then however many leading tabs are common across all non-blank lines
#    in the current pre-formatted block are removed.
#    If << is followed by =, e.g.
#       print <<= TERMINATOR
#    then whatever leading white space (tabs or blanks) occurs before the
#    "print" command will be removed from all non-blank lines in
#    the current pre-formatted block.
#    By default no leading spaces are removed. Anything you place after
#    the TERMINATOR will be reproduced as-is after every line in the
#    post-processed script, so this for example:
#   print << HERE |"cat>&2"
#       foo
#   HERE
#    would cause "foo" to be printed to stderr.
#
# 2) Within each block of pre-formatted text only:
#   a) Put a backslash character before every backslash (\ -> \\).
#   b) Put a backslash character before every double quote (" -> \").
#   c) Enclose awk variables in double quotes without leading
#      backslashes (awkVar -> "awkVar").
#   d) Enclose awk record and field references ($0, $1, $2, etc.)
#      in double quotes without leading backslashes ($1 -> "$1").
#
# 3) If the script is specified on the command line instead of via
#    "-f script" then replace all single quote characters (') in or out
#    of the pre-formatted blocks with their ANSI octal escape sequence (\047)
#    or the sequence '\'' (tick backslash tick tick). This is normal and is
#    required because command-line awk scripts cannot contain single quote
#    characters as those delimit the script. Do not use hex \x27, see
#    http://awk.freeshell.org/PrintASingleQuote.
#
# Then just use it like you would gawk with the small caveat that only
# "-W <option>", not "--<option>", is supported for long options so you
# can use "-W re-interval" but not "--re-interval" for example.
#
# To just see the post-processed script and not execute it, call this
# script with the "-X" option.
#
# See the bottom of this file for usage examples.
##########################################################

expand_prints() {

    gawk '

        !inBlock {
        if ( match($0,/^[[:blank:]]*print[[:blank:]]*<</) ) {

        # save any blanks before the print in case 
        # skipType "=" is used.
        leadBlanks = $0
        sub(/[^[:blank:]].*$/,"",leadBlanks)

        $0 = substr($0,RSTART+RLENGTH)

            if      ( sub(/^[-]/,"") )  { skipType = "-" }
            else if ( sub(/^[+]/,"") )  { skipType = "+" }
            else if ( sub(/^[=]/,"") )  { skipType = "=" }
            else                { skipType = ""  }

            gsub(/(^[[:blank:]]+|[[:blank:]]+$)/,"")

            if (/[[:blank:]]/) {
                terminator = $0
                    sub(/[[:blank:]].*/,"",terminator)

            postprint = $0
                sub(/[^[:blank:]]+[[:blank:]]+/,"",postprint)
            }
            else {
                terminator = $0
            postprint = ""
            }

            startBlock()

            next
        }
        }

        inBlock {

        stripped=$0
        gsub(/(^[[:blank:]]+|[[:blank:]]+$)/,"",stripped)

        if ( stripped"" == terminator"" ) {
            endBlock()
        }
        else {
            updBlock()
        }

        next
        }

        { print }

    function startBlock() { inBlock=1; numLines=0  }

    function updBlock()   { block[++numLines] = $0 }

    function endBlock(  i,numSkip,indent) {

        if (skipType == "") {
        # do not skip any leading tabs
        indent = ""
        }
        else if (skipType == "-") {
        # skip all leading tabs
        indent = "[\t]+"
        }
        else if (skipType == "+") {

        # skip however many leading tabs are common across
        # all non-blank lines in the current pre-formatted block

            for (i=1;i<=numLines;i++) {

            if (block[i] ~ /[^[:blank:]]/) {

                match(block[i],/^[\t]+/)

                if ( (numSkip == "") || (numSkip > RLENGTH) ) {
                numSkip = RLENGTH
                }
            }
            }

            for (i=1;i<=numSkip;i++) {
            indent = indent "\t"
            }
        }
        else if (skipType == "=") {
        # skip whatever pattern of blanks existed
        # before the "print" statement
        indent = leadBlanks
        }


        for (i=1;i<=numLines;i++) {
                sub(indent,"",block[i])
        print "print \"" block[i] "\"\t" postprint
        }

        inBlock=0
    }

    ' "$@"

}

unset awkArgs
unset scriptFiles
expandOnly=0
while getopts "v:F:W:f:X" arg
do
        case $arg in
    f ) scriptFiles+=( "$OPTARG" ) ;;
        [vFW] ) awkArgs+=( "-$arg" "$OPTARG" ) ;;
    X ) expandOnly=1 ;;
        * )     exit 1 ;;
        esac
done
shift $(( OPTIND - 1 ))

if [ -z "${scriptFiles[*]}" -a "$#" -gt "0" ]
then
    # The script cannot contain literal 's because in cases like this:
    #   'BEGIN{ ...abc'def... }'
    # the args parsed here (and later again by gawk) would be:
    #   $1 = BEGIN{ ...abc
    #   $2 = def... }
    # Replace 's with \047 or '\'' if you need them:
    #   'BEGIN{ ...abc\047def... }'
    #   'BEGIN{ ...abc'\''def... }'
    scriptText="$1"
    shift
fi

# Remaining symbols in "$@" must be data file names and/or variable
# assignments that do not use the "-v name=value" syntax.

if [ -n "${scriptFiles[*]}" ]
then
    if (( expandOnly == 1 ))
    then
    expand_prints "${scriptFiles[@]}"
    else
    gawk "${awkArgs[@]}" "$(expand_prints "${scriptFiles[@]}")" "$@"
    fi

elif [ -n "$scriptText" ]
then
    if (( expandOnly == 1 ))
    then
    printf '%s\n' "$scriptText" | expand_prints
    else
    gawk "${awkArgs[@]}" "$(printf '%s\n' "$scriptText" | expand_prints)" "$@"
    fi
else
    printf '%s: ERROR: no awk script specified.\n' "$toolName" >&2
    exit 1
fi

USAGE EXAMPLES:

$ cat data.txt
abc def"ghi

.

#######
$ cat script.awk
{
    awkVar="bar" 

    print "----------------"

    print << HERE
    backslash: \\

        quoted text: \"text\"

    single quote as ANSI sequence: \047

    literal single quote (ONLY works when script is in a file): '

    awk variable: "awkVar"

    awk field: "$2"
    HERE

    print "----------------"

    print <<-!
        backslash: \\

            quoted text: \"text\"

        single quote as ANSI sequence: \047

        literal single quote (ONLY works when script is in a file): '

        awk variable: "awkVar"

        awk field: "$2"
    !

    print "----------------"

    print <<+           whatever
        backslash: \\

    quoted text: \"text\"

        single quote as ANSI sequence: \047

        literal single quote (ONLY works when script is in a file): '

        awk variable: "awkVar"

        awk field: "$2"
    whatever

    print "----------------"
}

.

$ epawk -f script.awk data.txt
----------------
    backslash: \

        quoted text: "text"

    single quote as ANSI sequence: '

    literal single quote (ONLY works when script is in a file): '

    awk variable: bar

    awk field: def"ghi
----------------
backslash: \

    quoted text: "text"

single quote as ANSI sequence: '

literal single quote (ONLY works when script is in a file): '

awk variable: bar

awk field: def"ghi
----------------
    backslash: \

quoted text: "text"

    single quote as ANSI sequence: '

    literal single quote (ONLY works when script is in a file): '

    awk variable: bar

    awk field: def"ghi
----------------

.

$ epawk -F\" '{
print <<!
    ANSI-tick-surrounded quote-separated field 2 (will work): \047"$2"\047
!
}' data.txt
    ANSI-tick-surrounded quote-separated field 2 (will work): 'ghi'

.

epawk -F\" '{
print <<!
    Shell-escaped-tick-surrounded quote-separated field 2 (will work): '\''"$2"'\''
    "
}' data.txt
    Shell-escaped-tick-surrounded quote-separated field 2 (will work): 'ghi'

.

$ epawk -F\" '{
print <<!
    Literal-tick-surrounded quote-separated field 2 (will not work): '"$2"'
!
}' data.txt
    Literal-tick-surrounded quote-separated field 2 (will not work): 

.

$ epawk -X 'BEGIN{
print <<!
    foo
    bar
!
}'
BEGIN{
print "    foo"
print "    bar"
}

.

$ cat file
a
b
c

.

$ epawk '{
    print <<+! |"cat>o2"
        numLines="NR"
                numFields="NF", $0="$0", $1="$1"
    !
}' file

.

$ cat o2
numLines=1
        numFields=1, $0=a, $1=a
numLines=2
        numFields=1, $0=b, $1=b
numLines=3
        numFields=1, $0=c, $1=c

.

$ epawk 'BEGIN{

    cmd = "sort"
    print <<+! |& cmd
        d
        b
        a
        c
    !
    close(cmd, "to")

    while ( (cmd |& getline line) > 0 ) {
        print "got:", line
    }
    close(cmd)

}' file
got: a
got: b
got: c
got: d



回答2:


$ cat a.awk
BEGIN {
    print "\
#########################################\n\
#      generated by some author         #\n\
#########################################"
}
$ awk -f a.awk
#########################################
#      generated by some author         #
#########################################



回答3:


Is this some that you look for?

var="Peter Hanson"

awk -v auth="$var" '
BEGIN {print "#########################################"
    print "#      generated by some author         #"
    printf "#";
    l=int((41-length(auth))/2)
    r=((41-length(auth))/2-l)*2
        for (i=1;i<=l;i++) 
        printf " "
    printf "%s",auth
    for (i=1;i<=l+r-2;i++) 
        printf " "
    print "#"
    print "#########################################"
    }' file
#########################################
#      generated by some author         #
#              Peter Hanson             #
#########################################

This will take the data in the variable var and print it as the second line.
It does adjust the field with, so it its centered.
You need to enter your code for the rest after the last print



来源:https://stackoverflow.com/questions/24596514/print-block-of-text-to-file-from-awk-script-banner-like

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!