2개의 파일을 입력으로 사용하는 스크립트가 있습니다.
처리를 시작하려면 먼저 파일 준비가 필요합니다.
내 생각은 원본 파일을 건드리지 않고 모든 것을 복사본으로 만들고 출력으로 필요한 것을 인쇄하고 복사본을 삭제하는 것입니다.
그러나 이 접근 방식을 사용하면 스크립트에 변수가 많아지고 오류가 발생하기 쉽습니다.
예:
#!/bin/bash
[[ -z $1 ]] && echo 'We need input file a' && exit 1;
[[ -z $2 ]] && echo 'We need input file b' && exit 1;
A_CSV=$1;
B_CSV=$2;
A_FILE="$A_CSV.tmp";
B_FILE="$B_CSV.tmp";
[ -f $A_FILE ]] && rm $A_FILE;
[[ -f $B_FILE ]] && rm $B_FILE;
tr -d "\r" < $A_CSV > $A_FILE;
tr -d "\r" < $B_CSV > $B_FILE;
awk '{ if(NR == 1) sub(/^\xef\xbb\xbf/,""); print }' $A_FILE > "$A_FILE.bck";
awk '{ if(NR == 1) sub(/^\xef\xbb\xbf/,""); print }' $B_FILE > "$B_FILE.bck";
rm $A_FILE && mv "$A_FILE.bck" $A_FILE;
rm $B_FILE && mv "$B_FILE.bck" $B_FILE;
# extra logic following the same pattern
계속해서 업데이트하고 이름을 바꾸기 위해 복사본이 어떻게 생성되는지 확인할 수 있습니다.
스크립트 오류를 줄이기 위해 이를 개선할 수 있는 방법이 있습니까?
답변1
이는 |
파이프()를 통해 달성됩니다. 세상에는 좋은 튜토리얼이 많이 있습니다. 이것.
#!/bin/bash
[[ -z $1 ]] && echo 'We need input file a' && exit 1;
[[ -z $2 ]] && echo 'We need input file b' && exit 1;
A_CSV=$1;
B_CSV=$2;
A_FILE="$A_CSV.tmp";
B_FILE="$B_CSV.tmp";
[ -f $A_FILE ]] && rm $A_FILE;
[[ -f $B_FILE ]] && rm $B_FILE;
tr -d "\r" < $A_CSV | awk '{ if(NR == 1) sub(/^\xef\xbb\xbf/,""); print }' > $A_FILE
tr -d "\r" < $B_CSV | awk '{ if(NR == 1) sub(/^\xef\xbb\xbf/,""); print }' > $B_FILE
개인적으로 저는 두 파일에 대해 동일한 작업을 수행하므로 단일 작업을 처리하는 함수를 만들고 싶습니다. rm -f $A_FILE $B_FILE
제 생각엔 보기에도 더 좋을 것 같아요.
답변2
원본 파일을 그대로 두고 복사본으로 작업하는 것이 매우 좋습니다. 한 단계 더 나아가 중간 파일도 재사용하지 않아야 합니다. 중간 파일을 재사용하고 프로세스가 중단되면 어느 지점에서 중단되었는지 알 수 없습니다.
두 파일 모두에 동일한 변환을 적용하고 있습니다. 코드를 두 번 작성하지 마세요! 코드를 한 번 작성하고, 필요에 따라 변수를 사용하고, 각 파일에 대해 한 번씩 코드를 호출하세요. 쉘 스크립트에서 이를 수행하는 도구는 다음과 같습니다.기능(또는 여러 스크립트에서 이 코드 조각을 호출해야 하는 경우 별도의 스크립트로 만드세요.)
사용하는 모든 텍스트 처리 도구는 표준 입력에서 읽고 표준 출력에 쓸 수 있습니다. 배치하여 결합할 수 있습니다.관로한 도구의 출력과 다음 도구의 입력 사이. 이렇게 하면 중간 파일이 많이 필요하지 않습니다. 실제로 이 경우 중간 파일이 필요하지 않습니다. 파이프는 Unix의 기본 디자인 기능입니다.
추가 쉘 프로그래밍 팁:변수 확장 주위에는 항상 큰따옴표를 사용하세요., 즉 $foo
.
#!/bin/bash
preprocess_csv () {
<"$1" \
tr -d '\r' |
awk '{ if(NR == 1) sub(/^\xef\xbb\xbf/,""); print }' >"${1%.csv}.clean"
}
preprocess_csv "$1"
preprocess_csv "$2"
do_stuff_with_preprocessed_file "${1%.csv}.clean" "${2%.csv}.clean" >global_output
내가 사용하는 것은매개변수 확장예를 들어 ${1%.csv}
로 변환하여 이 변환의 출력 파일이 가 되도록 구성합니다 .foo.csv
foo
foo.clean
이 스크립트는 귀하의 스크립트보다 간단하지만 여전히 개선될 수 있습니다. 파일 처리 명령 체인을 설명하는 데 쉘 스크립트보다 더 나은 도구가 있습니다.자동화 도구 구축클래식 같은만들다. 바라보다체크포인트를 사용하여 명령 목록을 실행하시겠습니까?유사한 사용 사례를 소개합니다. make를 사용하여 변환을 표현하는 방법은 다음과 같습니다. 이 파일을 호출하십시오 Makefile
. 다음 줄은 8개의 공백으로 들여쓰기되어 있으며 이를 탭으로 바꿔야 합니다. 이는 make의 특이한 점입니다.
default: global_output
%.clean: %.csv
<'$<' tr -d '\r' | awk '{ if(NR == 1) sub(/^\xef\xbb\xbf/,""); print }' >'$@'
global_output: input1.clean input2.clean
do_stuff_with_preprocessed_files input1.clean input2.clean >$@
$<
명령에서 종속성(위 오른쪽 파일 target: dependency
) 을 나타내고 $@
대상을 나타냅니다. 위의 makefile을 사용하여 명령을 실행하면 (또는 시작 부분의 행 덕분에 make global_output
명령만 실행하면 ) 변환을 실행하여 파일을 생성한 다음 ( 파일이 이미 존재해야 함) 생성을 위해 실행됩니다. .make
default:
.clean
.csv
do_stuff_with_preprocessed_files
global_output
이 메이크파일은 중간에 중단되면 부분적으로 처리된 파일을 남기기 때문에 취약합니다. 이 문제를 해결하려면 다음 설명에 따라 각 규칙에 임시 파일을 사용하세요.체크포인트를 사용하여 명령 목록을 실행하시겠습니까?.
default: global_output
%.clean: %.csv
<'$<' tr -d '\r' | awk '{ if(NR == 1) sub(/^\xef\xbb\xbf/,""); print }' >'[email protected]'
mv '[email protected]' '$@'
global_output: input1.clean input2.clean
do_stuff_with_preprocessed_files input1.clean input2.clean >'[email protected]'
mv '[email protected]' '$@'