同时读取文件并附加到文件,同时保留行尾

同时读取文件并附加到文件,同时保留行尾

从概念上讲,我有一个简单的任务......我在文件中有[松散]结构化数据:

Testing: debug, default CXXFLAGS
<100's of additional output lines>
Testing: release, default CXXFLAGS
<100's of additional output lines>
...

我尝试将其总结在日志文件中:

echo "Configurations tested:" | tee -a "$TEST_RESULTS"
echo $($GREP 'Testing: ' "$TEST_RESULTS" | $SED 's/Testing: /    * /g') | tee -a "$TEST_RESULTS"

代替:

Configurations tested:
    * debug, default CXXFLAGS
    * release, default CXXFLAGS

我得到:

Configurations tested:
1 3way.cpp 3way.h CMakeLists.txt CMakeLists.txt.diff Doxyfile Filelist.txt GNUmakefile
GNUmakefile-cross Install.txt License.txt Readme.txt TestData TestVectors adhoc.cpp.proto
adler32.cpp adler32.h aes.h algebra.cpp algebra.h ...

我认为我对文件缓冲区造成了严重破坏,$TEST_RESULTS因为它是从 中读取的grep,并用tee.

当我尝试将结果$GREP 'Testing: ' "$TEST_RESULTS" | $SED 's/Testing: / * /g'放入 shell 变量时,我丢失了行结尾,这导致了一个大的串联:

* debug, default CXXFLAGS    * release, default CXXFLAGS ... <30 additional configs>

如何同时读取文件和附加到文件,同时保留行尾?


我在以下方面取得了一些进展:

ESCAPED=$($GREP 'Testing: ' "$TEST_RESULTS" | $AWK -F ": " '{print "  -" $2 "$"}')
echo $ESCAPED | tr $ '\n' | tee -a "$TEST_RESULTS"

但是,它不能用作*项目符号点,而且似乎会删除前导空格:

Configurations tested:
-debug, default CXXFLAGS
 -release, default CXXFLAGS

我没有使用,sed因为跨平台换入新线绝对是痛苦的。平台包括 BSD、Cygwin、Linux、OS X、Solaris。

答案1

假设$TEST_RESULTS是包含测试输出的文件的名称。如果我理解正确,您希望将测试配置列表附加到同一文件中。

因此,使用临时文件。然后将该文件附加到原始文件中。

tmpfile="$( mktemp )"
{
    echo "Configurations tested:"
    sed -ne 's/^Testing: \(.*\)/  * \1/p' -- "$TEST_RESULTS"
} >"$tmpfile"

cat <"$tmpfile" >>"$TEST_RESULTS"
rm -f -- "$tmpfile"

如果你没有mktemp,你可以使用

tmpfile="$TEST_RESULTS.tmp"

或者

tmpfile=~/tmp/"$(basename -- "$TEST_RESULTS").tmp"

或类似的东西......假设该文件尚不存在(在这种情况下,它(或它指向符号链接的文件)将被覆盖然后删除)。无论如何,请避免使用全局可写目录来写入具有固定名称的临时文件,因为从安全角度来看,这是不好的做法。


在可用的系统上sponge

{
    echo "Configurations tested:"
    sed -ne 's/^Testing: \(.*\)/  * \1/p' -- "$TEST_RESULTS"
} | sponge -a -- "$TEST_RESULTS"

此处,sponge -a会将前面的复合命令 ( ) 的输出附加{ ...; }到给定的输出文件中。该sponge实用程序会将输出写入临时位置,然后用该位置替换指定的文件。该选项的作用-a是将输出附加到原始文件,而不是覆盖原始文件。

答案2

您的主要问题是您忘记引用$(echo...)$ESCAPED因此调用 split+glob 运算符,其中扩展根据 的字符进行分割$IFS,并且结果单词受到通配符的影响(*例如扩展到当前目录中的非隐藏文件名列表),这是大多数类似 Bourne 的 shell 中常见的错误特征。

# get a $PATH where to find standard utilities rather having to hardcode
# the path of each (you also forgot the quotes around those `$SED`/`$GREP`...)
PATH=$(command -p getconf PATH)${PATH:+:$PATH}
export PATH

printf '%s\n' 'Configurations tested:' \
              "$(sed -ne 's/^Testing: \(.*\)/  * \1/p' -- "$TEST_RESULTS")" |
  tee -a -- "$TEST_RESULTS"

(另请记住,您不能用于echo输出任意数据)。

答案3

我会尝试这样的事情:

awk -v pattern="Testing:" '$0 ~ pattern { sub(pattern, "  *"); print }' 

这应该适用于任何版本的sed,因为它不包含明显的扩展。只要您注意正确引用变量,就不必显式处理换行符,以防止分词。

相关内容