如何使用 awk 向多个文件添加标头

如何使用 awk 向多个文件添加标头

我想向多个文件添加包含空格的标题行。

这是我到目前为止所拥有的:

#!/bin/bash
# script name is "add_header.sh"
# ARG1 = HEADER STRING
# ARG2,3,... = ARRAY OF FILES TO ADD HEADER TO, RELATIVE DIRECTORY

HEADER=$1 
shift 

for FILE in $@; do
    awk -v HEADER=$HEADER FILE=$FILE 'BEGIN{print HEADER} {print}' FILE > FILE.new
done

不幸的是,当我在我的用例上运行它时,由于空格而失败:

touch file1 file2 file3
./add_header.sh "some header with spaces" file1 file2 file3

这给出了以下错误:

awk: fatal: cannot open file `with' for reading (No such file or directory)
awk: fatal: cannot open file `with' for reading (No such file or directory)
awk: fatal: cannot open file `with' for reading (No such file or directory)

有没有办法转义 bash 变量中的空格?我尝试在每个空格之前使用 \ ,但错误现在更改为:

./add_header.sh "some\ header\ with\ spaces" file1 file2 file3
awk: fatal: cannot open file `with\' for reading (No such file or directory)
awk: fatal: cannot open file `with\' for reading (No such file or directory)
awk: fatal: cannot open file `with\' for reading (No such file or directory)

这意味着空格没有被转义。

答案1

#!/bin/sh

header=$1; shift

for pathname do
    { printf '%s\n' "$header"; cat -- "$pathname"; } >"$pathname.new"
done

这里没有真正的需要,awk因为我们想要连接标头和旧文件内容。我们通过简单地输出标题字符串,printf然后使用cat来输出文件的内容来做到这一点。我们将printf和的输出重定向cat到一个新文件。

你会真的想要使用 来执行此操作awk,则可以像上面的代码一样循环遍历文件,或者让awk处理每个文件而不需要显式的 shell 循环。

第一个带有显式 shell 循环的变体:

#!/bin/sh

header=$1; shift

for pathname do
    header=$header awk 'BEGIN { print ENVIRON["header"] }; 1' "$pathname" >"$pathname.new"
done

上述解决方案将是此答案中所有变体中最慢的变体,因为它awk每个文件调用一次。

没有 shell 循环的第二种变体(需要像 GNU 那样awk理解):BEGINFILEawk

#!/bin/sh

header=$1; shift

header=$header awk '
    BEGINFILE { fname = FILENAME ".new"; print ENVIRON["header"] >(fname) }
    { print >(fname) }' "$@"

第三种变体(最后一段代码的可移植变体):

#!/bin/sh

header=$1; shift

header=$header awk '
    FNR == 1 { fname = FILENAME ".new"; print ENVIRON["header"] >(fname) }
    { print >(fname) }' "$@"

答案2

假设您没有空输入文件,正确的方法是:

#!/usr/bin/env bash
header=$1 
shift 

awk -v header="$header" '
    FNR==1 { close(out); out=FILENAME ".new"; $0=header ORS $0 }
    { print > out }
' "$@"

上面的代码可以在每个 Unix 机器上的任何 shell 中使用任何 awk 来工作。如果您可以有空的输入文件,则需要进行调整。

答案3

这是对我有用的修改版本:

#!/bin/bash

header=$1
shift

for file in $@; do 
    awk -v HEADER="$header" 'BEGIN{print HEADER} {print}' "$file" > "$file".new
done

我尝试在 awk 表达式内部使用{print > [FILE].new},但它不起作用。它刚刚打印到stdout.可能是因为 awk 中无法创建新文件。

相关内容