한 파일의 누락된 콘텐츠를 다른 파일에 추가

한 파일의 누락된 콘텐츠를 다른 파일에 추가
FILE1=$1   
FILE2=$2

if [ ! -e "$FILE2" ]
then
   touch $FILE2
fi

while read line; do
  if grep -q "$line" $FILE2
  then
    echo
  else
    echo $line >> $FILE2
  fi
done < $FILE1

FILE1의 행이 FILE2에 없으면 FILE2에 추가하고 싶습니다. 내 목적은 FILE1의 행을 나열하고 해당 행이 FILE2에 없으면 FILE2에 추가하는 것입니다.

뭐가 문제 야?

답변1

awk 'NR == FNR {first[$0];next}; ! ($0 in first)' "$file2" "$file1" >> "$file2"

파일 이름에 =문자가 없다고 가정합니다. 가능하다면 /dev/fd/n을 지원하는 시스템에서 다음을 수행할 수 있습니다.

awk 'NR == FNR {first[$0];next}; ! ($0 in first)' \
  /dev/fd/3 3< "$file2" /dev/fd/4 4< "$file1" >> "$file2"

$file1행이 에서 여러 번 발생하고 에서는 발생하지 않는 경우 $file2모든 발생은 에 추가됩니다 $file2. 이것이 원하는 것이 아니라면 다음을 수행하십시오.

awk 'NR != FNR && ! ($0 in seen); {seen[$0]}' \
  /dev/fd/3 3< "$file2" /dev/fd/4 4< "$file1" >> "$file2"

행이 고유하고 출력 정렬에 문제가 없으면 다음을 수행할 수도 있습니다.

LC_ALL=C sort -uo "$file2" "$file1" "$file2"

코드에 어떤 문제가 있는지:

FILE1=$1   
FILE2=$2

관례적으로 모든 대문자 변수는 예약되어 있습니다.환경변수(공유 네임스페이스로 환경에 내보낸 변수)이므로 쉘 변수에만 대문자 변수를 모두 사용하지 않는 것이 가장 좋습니다.

if [ ! -e "$FILE2" ]
then
   touch $FILE2
fi

의 매개변수에서는 참조했지만 [의 매개변수에서는 참조하지 않았습니다 touch. 이유는 무엇입니까?

또한 그러하니 참고해주세요

touch "$file_or_option"

변수가 항상 파일 매개변수로 처리되도록 하려면 다음을 수행해야 합니다.

touch -- "$file2"

그리고 이는 if ! condition; then do-something-to-make-it-true일반적으로 나쁜 습관이며 경쟁 조건의 영향을 받습니다. 이렇게 하는 것이 더 좋습니다 enforce-the-condition || die-if-couldn't. 그래서 여기 있습니다:

touch -- "$file2" || exit

그러나 이에 관계없이 >>리디렉션은 파일을 생성합니다(만약 생성할 수 없는 경우 오류를 반환합니다).

while read line; do

쉘에서 루프를 사용하기 시작할 때특히 while read루프, 이는 일반적으로 잘못된 접근 방식을 취하고 있음을 의미합니다. 파일의 각 줄에 대해 여러 명령을 순차적으로 실행하게 되는데, 이는 쉘 스크립트가 작성되는 방식이 아닙니다. 대신, 특정 특수 명령을 한 번 실행하고 싶습니다.협력작업에.

먼저 POSIX 쉘에서 a 를 읽으려면 line구문은 다음과 같습니다.

IFS= read -r line

아니요 read line

  if grep -q "$line" $FILE2

다시 말하지만, 그것은 다음과 같습니다:

grep -q "$option_or_regexp"

또는:

grep -q -- "$regexp"

또는:

grep -qe "$regexp"

정규식을 일치시키는 대신 문자열을 검색하려는 경우 또는 -F전체 줄을 일치시키려는 경우 이 옵션이 필요합니다 -x.

grep -qxFe "$line" < "$file2"
  then
    echo
  else
    echo $line >> $FILE2

마찬가지로, Split+glob 연산자를 적용하는 대상 ( $linesh 모드에서 $FILE2사용 되지 않는 경우 bash).

또한 echo임의의 데이터와 함께 사용할 수 없습니다. 다음을 수행해야 합니다.

printf '%s\n' "$line" >> "$file2"

여기. 그러나 $file2전체 루프에 대해 한 번만 수행할 수 있었는데 루프의 각 패스에 대해 다시 열면 낭비입니다 .

  fi
done < $FILE1

다시:

done < "$file1"

사용된 경우 bash.

관련 정보