从概念上讲,我有一个简单的任务......我在文件中有[松散]结构化数据:
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
,因为它不包含明显的扩展。只要您注意正确引用变量,就不必显式处理换行符,以防止分词。