줄 끝을 유지하면서 동시에 파일을 읽고 추가합니다.

줄 끝을 유지하면서 동시에 파일을 읽고 추가합니다.

개념적으로는 간단한 작업이 있습니다. 파일에 [느슨하게] 구조화된 데이터가 있습니다.

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'줄 끝이 손실되어 큰 연결이 발생합니다.

* 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

주요 문제는 quote 를 잊어버렸기 때문에 분할+glob 연산자를 호출한다는 것 $(echo...)입니다 $ESCAPED. 여기서 확장은 의 문자를 기준으로 분할되고 $IFS결과 단어는 와일드카드의 적용을 받습니다( *예: 현재에서 숨겨지지 않은 파일 이름 목록으로의 확장). 디렉토리), 이는 큰 문제입니다. 대부분의 Bourne 유사 쉘에서 발견되는 일반적인 오류 특성입니다.

# 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이는 명확한 확장 기능이 포함되어 있지 않으므로 모든 버전에서 작동합니다 . 변수를 올바르게 인용하는 한, 단어 분리를 방지하기 위해 명시적으로 줄바꿈을 처리할 필요는 없습니다.

관련 정보