짧은 질문:
sed가 파일을 변경하지 않는 이유는 무엇입니까? 확인할 방법이 있습니까?
긴 질문:
이전에 내 파일로 작업했던 sed 명령을 실행해 보았습니다. 나는 이것을 배웠다여기9월에 다시 오세요. 매 분기마다 나는 많은 공백과 하나여야 하지만 두 개로 분할된 열이 포함된 4개의 거대한 파일을 받습니다. 다음 명령을 실행하여 공백을 살펴보고 열 41과 42를 병합했습니다.
sudo sed -i -e 's/ \{1,\}"/"/g' -e 's/" \{1,\}/"/g' -e 's/","//41' original_file.txt
어제 처음으로 아무 일도 일어나지 않았습니다. 약 3초 정도 기다린 후 아무 일도 일어나지 않는 반면, 일반적으로 20~30분 정도 걸립니다. 파일을 확인했는데 공백이 여전히 남아 있습니다. 내 시스템에서 사용할 수 있는 파일 크기는 여전히 3배이고 RAM(512GB 램)에서 사용할 수 있는 파일 크기는 두 배입니다. 램은 중요하지 않고 그냥 거기에 넣으려고 합니다.
다음을 사용하여 다른 파일에 쓰려고했습니다.
sudo sed -e 's/ \{1,\}"/"/g' -e 's/" \{1,\}/"/g' -e 's/","//41' original_file.txt > formatted_file.txt
이렇게 하면 생성되지만 formatted_file.txt
완전히 비어 있게 됩니다.
누구든지 내가 뭘 잘못하고 있는지 또는 문제를 확인하는 방법을 말해 줄 수 있습니까?
편집하다:
샘플 입력은 다음에서 찾을 수 있습니다.스택 오버플로300개 이상의 열이 있다는 점만 빼면요.
답변1
입력 파일이 있는 주석에서 발견됨빅엔디안 방식 UTF-16기존의 일반 7비트 ASCII 또는 8비트 확장 ASCII 대신 형식을 사용합니다. UTF-16은 문자당 2바이트 형식이며 일반 ASCII를 인코딩하는 데 사용되는 경우 "ASCII" 문자 0x00
(NUL 바이트 , A 바이트-바이트 쌍(빅 엔디안, 반대쪽 리틀 엔디안) ^@
으로cat -A
less
해결 방법은 파일을 일반 ASCII로 변환하는 것입니다. 예를 들어 표준 또는 유사한 유틸리티를 사용하여 CR-LF(dos/windows 줄 끝)를 LF(unix 줄 끝)로 변환하는 대신 fromdos
다음을 수행하여 텍스트를 나머지 부분에서 사용할 수 있는 형식으로 변환해야 합니다. 스크립트 sed
:
sed -e '1 s/^\xff\xfe|^\xfe\xff//; s/\x00//g; s/\x0d$//'
이 sed
스크립트는 다음과 같습니다.
0xfffe
첫 번째 줄의 시작 부분에서 바이트 순서 표시를 제거 하거나 삭제합니다.0xfeff
- 발생 위치에 관계없이 모든 입력 줄에서 모든 NUL 문자를 제거합니다.
0x0d
줄 끝의 캐리지 리턴 문자( )를 제거합니다.
참고: 이는 ASCII 문자만 포함하는 UTF-16 인코딩 텍스트에만 작동합니다. 다른 유형의 문자(예: 영어가 아닌 텍스트)가 포함된 UTF-16 텍스트 파일을 완전히 손상시킵니다.
마지막으로 perl
순수 ASCII, UTF-8, UTF-16 등을 포함한 다양한 일반 형식의 텍스트에 대한 탁월한 지원이 있습니다. 모든 형식을 처리하고 모든 형식 간 변환을 위한 라이브러리 모듈이 있습니다. 간단한 스크립트를 로 변환하는 것은 sed
매우 쉽기 perl
때문에 스크립트의 Perl 버전은 간단할 수 있습니다(테스트되지 않았지만 작동할 수도 있음).
#!/usr/bin/perl
use strict;
use feature 'unicode_strings';
while(<>) {
s/^\xff\xfe|^\xfe\xff// if ($. == 1); # strip Byte Order marker from 1st line
s/\x0d$//; # strip CR from each end-of-line
s/ *"/"/g; # get rid of all spaces immediately before " characters
s/" */"/g; # get rid of all spaces immediately after " characters
# A very primitive split(). Should use a real CSV parser here, like the
# Text::CSV module which properly copes with embedded quotes and commas etc
# in string fields. This would also allow proper processing of each field to
# remove any extra whitespace characters rather than the quick-and-dirty hack of
# global regexp substitutions above.
my @fields = split /,/;
# perl arrays start from zero. This appends the "fake" field 42 onto field 41,
# and then deletes field 42.
$fields[40] .= $fields[41];
delete $fields[41];
print join(',',@fields), "\n";
}
이전 답변에는 여전히 (IMO) 유용한 정보가 포함되어 있습니다.
awk
. 보다 작업에 더 나은 도구가 있습니다 sed
.
예를 들어, GNU (또는 및 와 같이 PCRE를 이해하는 awk
다른 도구 )를 사용하면 다음과 같습니다.awk
\s
\S
awk '{$0=gensub(/\s*(\S+)/,"\\1",42)}1' original > fixed
42열 바로 앞의 모든 공백을 제거하여 41열과 42열을 병합합니다.
PCRE가 아닌 경우 다음을 대신 사용 awk
하세요 .[[:space:]]
\s
[^[:space:]]
\S
awk '{$0=gensub(/[[:space:]]*(\[^[:space:]]+)/,"\\1",42)}1' original > fixed
또한 입력 파일의 정확한 특성에 따라 이 작업의 경우 . perl
보다 나을 수도 있습니다 awk
. 예를 들어 CSV 파일을 구문 분석하고 CSV 레코드의 개별 필드를 처리하는 모듈이 있습니다.
그런데, 제 생각에는 이 sed
스크립트가 끔찍하다고 생각합니다. 특히 명령 구분 기호로 단일 sed 스크립트 대신 여러 인수를 사용하고 있기 때문입니다 -e
. ;
사용하고 싶다면 sed
최소한 효과적이고 효율적으로 사용하십시오. 귀하의 sed
스크립트는 다음과 같이 작성하는 것이 가장 좋습니다.
sed -e 's/ \{1,\}"/"/g; s/" \{1,\}/"/g; s/","//41' original > fixed
심지어:
sed -e 's/ \{1,\}"/"/g
s/" \{1,\}/"/g
s/","//41' original > fixed
여전히 오류를 수정해야 하지만 최소한 디버그할 수 있는 더 읽기 쉬운 내용이 있으므로 문제를 더 쉽게 발견할 수 있습니다.
또한 당신이 생각하는 것처럼 "제자리" 편집이 아닌 경우도 있습니다 -i
. --in-place
임시 파일을 만든 다음 해당 위치로 이동하는 방식으로 작동합니다. 이렇게 하면 하드 링크를 포함하여 inode를 변경하지 않고 유지해야 하는 모든 항목이 중단됩니다.
cat temp.txt > original.txt; rm temp.txt
변경된 출력을 임시 파일(예: temp.txt)에 쓴 다음 동일한 inode를 유지하면서 원본 파일을 변경된 버전으로 덮어쓰는 것이 더 좋습니다 .