기본적으로 파일에서 입력 텍스트로 가져 와서 해당 파일에서 한 줄을 제거하고 출력을 동일한 파일로 다시 보내고 싶습니다. 이 선을 따라 가면 더 명확 해집니다.
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name
그러나 이렇게하면 빈 파일이 생깁니다. 이견있는 사람?
답변:
bash가 먼저 리디렉션을 처리 한 다음 명령을 실행하기 때문에 그렇게 할 수 없습니다. 따라서 grep이 file_name을 볼 때 이미 비어 있습니다. 그래도 임시 파일을 사용할 수 있습니다.
#!/bin/sh
tmpfile=$(mktemp)
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > ${tmpfile}
cat ${tmpfile} > file_name
rm -f ${tmpfile}
이와 같이 tmpfilemktemp 을 만드는 데 사용 하는 것을 고려 하지만 POSIX가 아니라는 점에 유의하십시오.
>리디렉션이 파일을 열고 쉘이 시작 되기 전에 잘립니다 grep.
sponge 받아 들여야합니다.
이런 종류의 작업 에는 스폰지 를 사용하십시오 . moreutils의 일부입니다.
다음 명령을 시도하십시오.
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name
brew install moreutils.
sudo apt-get install moreutilsDebian 기반 시스템에서.
대신 sed를 사용하십시오.
sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name
-i는 GNU 전용 확장입니다.
-i ''에서는 확장이 엄격하게 필수는 아니지만 -i옵션에 몇 가지 인수 가 필요 하다고 말할 수 있습니다 .
동일한 파일에 대한 리디렉션 연산자 ( >또는 >>)를 사용할 수 없습니다. 우선 순위가 더 높고 명령이 호출되기 전에 파일을 생성 / 자르기 때문입니다. 그것을 방지하기 위해 다음과 같은 적절한 도구를 사용해야합니다 tee, sponge, sed -i또는 파일 (예에 대한 결과를 쓸 수있는 다른 도구 sort file -o file).
기본적으로 입력을 동일한 원본 파일로 리디렉션하는 것은 의미가 없으며이를 위해 적절한 내부 편집기 (예 : Ex 편집기 (Vim의 일부))를 사용해야합니다.
ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name
어디:
'+cmd'/ -c-Ex / Vim 명령 실행g/pattern/d- 전역 (help :g )을-s-무음 모드 ( man ex)-c wq-실행 :write및 :quit명령당신은 사용할 수 sed있지만, (이미 다른 답변에서와 같이) 동일을 달성하기 위해 자리에서 ( -i(유닉스 / 리눅스 사이에 다르게 작동 할 수 있습니다) 표준이 아닌 FreeBSD의 확장은) 기본적으로 그것은이다 의 tream 에드 당사 홈페이지가 아닌 파일 편집기 . 참조 : Ex 모드는 실용적인 용도가 있습니까?
이 질문이 검색 엔진의 상위 결과이므로 다음은 대신 하위 셸을 사용하는 https://serverfault.com/a/547331 을 기반으로 한 한 줄 입니다 sponge(종종 OS X와 같은 바닐라 설치의 일부가 아님). :
echo "$(grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name)" > file_name
일반적인 경우는 다음과 같습니다.
echo "$(cat file_name)" > file_name
위의 솔루션에는 몇 가지주의 사항이 있습니다.
printf '%s' <string>대신에를 사용 echo <string>하여 포함 된 파일 -n이 원치 않는 동작을 일으키지 않도록해야합니다 .x출력과를 통해 외부에 제거 임시 변수의 매개 변수 확장 과 같은${v%x} . .$v에있는 기존 변수의 값이 스톰 핑 $v되므로 이전 값을 유지하려면 전체 표현식을 괄호로 묶어야합니다.null가 출력에서 와 같이 인쇄 할 수없는 문자를 제거한다는 것 입니다. 나는 이것을 호출 dd if=/dev/zero bs=1 count=1 >> file_name하고 cat file_name | xxd -p. 그러나 echo $(cat file_name) | xxd -p벗겨집니다. 따라서이 답변은 Lynch가 지적했듯이 이진 파일이나 인쇄 할 수없는 문자를 사용하는 모든 항목에 사용 해서는 안됩니다 .일반적인 솔루션 (약간 느리고 메모리 집약적이며 여전히 인쇄 할 수없는 문자 제거)은 다음과 같습니다.
(v=$(cat file_name; printf x); printf '%s' ${v%x} > file_name)
https://askubuntu.com/a/752451 에서 테스트 :
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do (v=$(cat file_uniquely_named.txt; printf x); printf '%s' ${v%x} > file_uniquely_named.txt); done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
다음을 인쇄해야합니다.
hello
world
cat file_uniquely_named.txt > file_uniquely_named.txt현재 쉘에서 호출하는 반면 :
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do cat file_uniquely_named.txt > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
빈 문자열을 인쇄합니다.
대용량 파일 (아마 2GB 또는 4GB 이상)에서는 테스트하지 않았습니다.
나는 Hart Simha 와 kos 에게서이 대답을 빌렸다 .
cat첫 번째 인수로 넣습니다 echo. 물론 인쇄 할 수없는 변수는 제대로 출력되지 않고 데이터를 손상시킵니다. 파일을 자신에게 다시 리디렉션하지 마십시오. 좋지 않을 수 있습니다.
프로세스 대체 를 사용하여 수행 할 수 있습니다. .
bash가 모든 파이프를 비동기식으로 열고 다음을 사용하여 해결해야하기 때문에 약간의 해킹입니다. sleep YMMV 하므로 입니다.
귀하의 예에서 :
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
>(sleep 1 && cat > file_name) grep에서 출력을받는 임시 파일을 만듭니다.sleep 1 입력 파일을 구문 분석하는 grep 시간을 제공하기 위해 1 초 동안 지연cat > file_name출력을 씁니다.POSIX Awk와 함께 slurp를 사용할 수 있습니다.
!/seg[0-9]\{1,\}\.[0-9]\{1\}/ {
q = q ? q RS $0 : $0
}
END {
print q > ARGV[1]
}
이것은 매우 가능합니다. 출력을 쓸 때 다른 파일에 쓰고 있는지 확인하기 만하면됩니다. 파일 설명자를 연 후 쓰기 전에 파일을 제거하면됩니다.
exec 3<file ; rm file; COMMAND <&3 >file ; exec 3>&-
또는 한 줄씩 더 잘 이해하려면 :
exec 3<file # open a file descriptor reading 'file'
rm file # remove file (but fd3 will still point to the removed file)
COMMAND <&3 >file # run command, with the removed file as input
exec 3>&- # close the file descriptor
COMMAND가 제대로 실행되지 않으면 파일 내용을 잃게되기 때문에 여전히 위험한 일입니다. COMMAND가 0이 아닌 종료 코드를 반환하면 파일을 복원하여 완화 할 수 있습니다.
exec 3<file ; rm file; COMMAND <&3 >file || cat <&3 >file ; exec 3>&-
사용하기 쉽도록 쉘 함수를 정의 할 수도 있습니다.
# Usage: replace FILE COMMAND
replace() { exec 3<$1 ; rm $1; ${@:2} <&3 >$1 || cat <&3 >$1 ; exec 3>&- }
예 :
$ echo aaa > test
$ replace test tr a b
$ cat test
bbb
또한 이렇게하면 원본 파일의 전체 복사본이 유지됩니다 (세 번째 파일 설명자가 닫힐 때까지). Linux를 사용하고 있고 처리중인 파일이 너무 커서 디스크에 두 번 넣을 수없는 경우 이미 처리 된 파일을 할당 해제하면서 파일을 지정된 명령에 블록별로 파이프하는 이 스크립트 를 확인할 수 있습니다. 블록. 항상 그렇듯이 사용 페이지의 경고를 읽으십시오.
이 시도
echo -e "AAA\nBBB\nCCC" > testfile
cat testfile
AAA
BBB
CCC
echo "$(grep -v 'AAA' testfile)" > testfile
cat testfile
BBB
CCC
다음은 sponge요구하지 않고 수행하는 것과 동일한 작업을 수행합니다 moreutils.
shuf --output=file --random-source=/dev/zero
이 --random-source=/dev/zero부분 shuf은 셔플 링을 전혀하지 않고 작업을 수행하도록 속이기 때문에 입력을 변경하지 않고 버퍼링합니다.
그러나 성능상의 이유로 임시 파일을 사용하는 것이 가장 좋습니다. 그래서 여기에 제가 작성한 함수가 있습니다. 이것은 당신을 위해 일반화 된 방식으로 할 것입니다 :
# Pipes a file into a command, and pipes the output of that command
# back into the same file, ensuring that the file is not truncated.
# Parameters:
# $1: the file.
# $2: the command. (With $3... being its arguments.)
# See https://stackoverflow.com/a/55655338/773113
function siphon
{
local tmp=$(mktemp)
local file="$1"
shift
$* < "$file" > "$tmp"
mv "$tmp" "$file"
}
저는 보통 티 프로그램을 사용합니다 .
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
자체적으로 임시 파일을 만들고 제거합니다.
tee. 작동이 보장되지는 않습니다. askubuntu.com/a/752451/335781을 참조하십시오 .