기본적으로 파일에서 입력 텍스트로 가져 와서 해당 파일에서 한 줄을 제거하고 출력을 동일한 파일로 다시 보내고 싶습니다. 이 선을 따라 가면 더 명확 해집니다.
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 moreutils
Debian 기반 시스템에서.
대신 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을 참조하십시오 .