【问题标题】:print block of text to file from awk script [banner like]将文本块从 awk 脚本打印到文件 [banner like]
【发布时间】:2014-08-27 02:30:29
【问题描述】:

我有awk 脚本进行一些处理并将其输出发送到文件。 我将如何在我的awk 程序的 BEGIN 块中写出类似横幅的消息 首先到那个文件,比如 bash heredoc

我知道我可以使用多个 print 命令,但有什么方法可以让 一个print 命令,但保留多行文本和换行符等。

所以输出应该是这样的:

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

漂亮格式的另一个问题是ENVIRON["VAR"] 应该是 在字符串的中间展开。

【问题讨论】:

    标签: file redirect awk message


    【解决方案1】:

    简单的方法是使用heredoc并将其保存在awk变量中:

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

    另外,这可能比你想要的要多,但下面是我用来提供比 awk 中的文档更好的东西的命令。在将模板文本添加到多个文件时,我发现它绝对是无价的..

    它是一个 shell 脚本,它接受一个带有略微扩展语法的 awk 脚本(以方便此处的文档)作为输入,调用 gawk 将该扩展语法转换为普通的 awk 打印语句,然后再次调用 gawk 以执行生成的脚本。

    我称它为“epawk”,表示“扩展打印”awk,接下来是该工具以及如何使用它的几个示例。当您调用它而不是直接调用 awk 时,您可以编写包含用于打印的预格式化文本块的脚本,就像您希望使用 here-doc 一样(每个 # 之前的空格是一个制表符):

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

    它的工作原理是从您的 awk 脚本创建一个 awk 脚本,然后执行它。如果您只想查看正在生成的脚本,epawk 将打印生成的脚本而不是执行它,如果您给它提供 -X 参数,例如:

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

    脚本:

    #!/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
    

    使用示例:

    $ 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         #
      #########################################
      

      【讨论】:

      • 有什么方法可以避免\ns 吗?
      • 我不知道使用这种方法。
      • @branquito 不,没有。 awk 字符串不能包含未转义的换行符,因为换行符终止语句,因此在字符串中获取换行符的唯一方法是使用 \n
      【解决方案3】:

      这是你要找的吗?

      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             #
      #########################################
      

      这将获取变量var 中的数据并将其打印为第二行。
      它确实调整了字段,因此它居中。
      最后一个print之后需要输入你的代码

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        • 2012-01-23
        • 1970-01-01
        • 2021-03-23
        相关资源
        最近更新 更多