단일 파일을 전체 파이프라인의 입력 및 출력으로 처리 [중복]

단일 파일을 전체 파이프라인의 입력 및 출력으로 처리 [중복]

좋은 저녁이에요,

일부 파이프 명령을 사용하여 파일 내용을 필터링한 다음 결과를 동일한 파일에 다시 쓰고 싶습니다. 내가 쓴 대로 할 수 없다는 걸 알아요. 기다리다…

이것은 내 bash 스크립트입니다.

grep '^[a-zA-Z.:]' "$filepath" \
    | sed -r '/^(rm|cd)/d' \
    | uniq -u \
    > "$filepath"

그래서 대신 프로세스 대체를 성공적으로 사용할 수 있다고 생각합니다. 그런 다음 나는 다음과 같이 썼습니다.

grep '^[a-zA-Z.:]' < <(cat "$filepath") | …

이것도 아무것도 해결되지 않았습니다. 임시 파일과 같은 어딘가에 입력 파일의 내용을 "저장"하기 위한 프로세스 교체를 원합니다. 프로세스 대체도 이해가 안되는 것 같습니다.

"현재 위치" 버전에 대한 스레드를 읽었지만 해당 기사에서는 sed -i또는 같은 일부 바이너리에 대한 특수 옵션을 강조했지만 sort -o일반적인 솔루션이 필요합니다(모든 파이프라인 명령에 맞아야 함을 의미합니다).

첫째, "표준 파이프라인 방식"이 이를 수행하지 않는 이유는 무엇입니까? 그 아래에서는 무슨 일이 일어나고 있습니까? :/문제를 어떻게 해결해야 하나요? 누구든지 제발설명하다나에게 무슨 일이 일어났나요?

감사해요.

답변1

이미 언급했듯이 스펀지는 다음에서 유래합니다.더 많은 유틸리티중대하다. moreutils 종속성을 피하기 위해 조롱에 이 스크립트를 사용합니다.

#!/bin/sh -e
#Soak up input and tee it to arguments
st=0; tmpf=
tmpf="`mktemp`" && exec 3<>"$tmpf" || st="$?"
rm -f "$tmpf" #remove it even if exec failed; noop if mktemp failed
[ "$st" = 0 ] || exit "$st"
cat >&3
</dev/fd/3 tee "$@" >/dev/null

다음과 같이 사용할 수 있습니다.

grep '^[a-zA-Z.:]' "$filepath" \
| sed -r '/^(rm|cd)/d' \
| uniq -u | sponge "$filepath" 

명령이 시작되기 전에 리디렉션이 발생하고 출력 리디렉션은 출력 파일을 자르기 때문에 단순 출력 리디렉션을 사용하여 이 작업을 수행할 수 없습니다.

즉, grep(파이프라인의 첫 번째 단순 명령)이 시작되면 마지막 리디렉션에서 이미 입력/출력 파일이 잘렸습니다.

내가 아는 한 실제로 내부 편집을 수행하는 표준 UNIX 유틸리티는 없습니다. sed -i시뮬레이션하려면 임시 파일만 사용하십시오. 그 이유는 파이프라인 단계가 실패할 경우 실제 내부 필터링이 파일을 쉽게 손상시킬 수 있기 때문인 것 같습니다.

아래에서 무슨 일이 일어나고 있는지에 관해서는 둘 다 시스템 파이프를 |사용 <()하고 한 번에 하나의 버퍼를 IO에 전달합니다. 이 메커니즘은 임시 파일(실제 파일 시스템 파일이 아님)을 생성하지 않으며 전체 입력을 메모리에 한 번에 보관하는 것을 방지하려고 합니다.

답변2

동일한 파일에서 입력과 출력을 원하면 시도해 볼 수 있습니다스펀지. 설명에 따르면 다음과 같습니다.

sponge reads standard input and writes it out to the specified file. 
Unlike a shell redirect, sponge soaks up all its input before writing 
the output file. This allows constructing pipelines that read from and 
write to the same file.

그래서 당신은 다음 과 같은 sed '...' file | grep '...' | sponge [-a] file입력을 가질 수 있습니다문서그리고 동일하게 출력문서.


반면, 임시 파일을 사용하는 것도 동일한 파일을 입력과 출력에 사용하는 좋은 방법입니다. 다음과 같이 임시 파일을 초기화할 수 있습니다.

tempfile=`mktemp tempFile.XXXX` # You can replace "tempFile" with any name you want

그러면 스크립트가 실행되는 디렉터리에 확장자가 "XXXX"인 "tempFile"이라는 임시 파일이 생성됩니다. 여기서 x는 현재 프로세스 번호와 임의 문자의 조합(예: tempFile.AVm7)으로 대체됩니다.

이제 다음과 같이 파이프(또는 파이프 명령)를 수정할 수 있습니다.

grep '^[a-zA-Z.:]' "$filepath" \
    | sed -r '/^(rm|cd)/d' \
    | uniq -u \
    > "$tempfile"

필터링 후 다음과 같이 임시 파일을 원본 파일로 이동할 수 있습니다.

mv "$tempfile" "$filepath"

이렇게 하면 임시 파일이 제거되고 필터링된 원본 파일은 그대로 유지됩니다. 그러나 때로는 필요하지 않고 아직 삭제되지 않은 임시 파일을 많이 생성하게 될 수 있으므로 스크립트가 끝난 후 임시 파일이 없으면 모든 임시 파일을 삭제하여 디렉터리를 정리하는 것이 좋습니다. 더 이상 필요합니다. 이에 대한 루틴을 다음과 같이 작성할 수 있습니다.

remove_temp_files() {
    rm `find . -name "tempFile.????"`
}

remove_temp_files그런 다음 위 형식으로 생성된 모든 임시 파일을 제거하는 스크립트 끝 부분의 루틴을 호출하면 됩니다.

답변3

사용여기 문서그리고명령 대체이 경우 표준 접근 방식은 다음과 같습니다.

grep '^[a-zA-Z.:]' <<IN \
    | sed -r '/^(rm|cd)/d' \
    | uniq -u \
    > "$filepath"
$(cat -- "$filepath")
IN

다른 질문에 대해서는 이전의 많은 질문에 설명이 있습니다.

관련 정보