여러 줄의 텍스트 패턴과 일치하는 스크립트?

여러 줄의 텍스트 패턴과 일치하는 스크립트?

내 변수에 여러 줄 문자열이 있습니다 $PAT. $PAT파일 내에서 검색해야 합니다 $FILE. 그렇다면 삭제된 파일 $PAT$FILE인쇄해야 합니다 $PAT. 찾을 수 없으면 $PAT아무것도 인쇄되지 않습니다. $PAT특수 문자가 포함되어 있고 문자 그대로 일치해야 하는지 알 수 없습니다 . 예를 들어 $PAT이면 //\/\\|*에서 정확히 동일한 8자 문자열을 검색해야 합니다 $FILE.

실제 사용은 기존 파일/스크립트에 텍스트를 설치하고 제거하는 것입니다. $PAT에 추가하려면 $FILE이전에 추가되었는지 알고 싶습니다. $PAT이미 있는 경우 $FILE, 없이 출력하면 $PAT쉽게 제거할 수 있습니다.

(Android 장치)에 이러한 스크립트가 필요한 유일한 시스템은 BusyBox입니다. Perl이나 기타 스크립팅 언어가 없습니다.

답변1

완전한 라인을 일치시키고 싶다면 $PAT해결책이 있습니다. 완전한 라인이란 일치하는 경우 $FILE세 개의 하위 파일(f1, f2 및 f3)로 분할할 수 있음을 의미합니다.

  • cat f1 f2 f3$FILE,
  • f2는 $PAT.

f1 및/또는 f3은 비어 있을 수 있습니다.

먼저 f2 파일을 만듭니다.

cat << EOF > f2
$PAT
EOF

그런 다음 $FILE과 f2를 비교하고 결과를 저장합니다.

diff $FILE f2 > diff_res
res=$?

0 이면 $resf1과 f3은 비어 있고 $FILE은 $PAT와 같습니다. 이 경우 빈 파일을 원한다고 가정합니다.

diff_res"" 로 시작하는 행이 포함된 경우 >f2에는 최소한 $FILE에 없는 행이 포함됩니다. 테스트를 받아보세요:

grep -q '^> ' diff_res
test $? -eq 0 && echo "PAT not found"

diff_res" "로 시작하는 행이 포함 되지 않으면 f2 >의 모든 행은 $FILE에 있지만 연속되지 않을 수 있습니다. 연속형인 경우 diff_res다음이 포함됩니다.

  • ""로 시작하지 않는 한 줄 <(f1 또는 f3이 비어 있는 경우)
  • 두 줄은 " "로 시작하지 않으며 <첫 번째 줄은 항상 1d" " 또는 "1,"로 시작합니다.

이를 테스트하기 위해 다음이 있습니다.

nb=$(grep -v "^< " diff_res | wc -l)
if test $nb -gt 2; then
  pat_found=0
elif test $nb -eq 1; then
  pat_found=1
else
  pat_found=$(sed -n -e '1{/^1d/p;/^1,/p}' diff_res | wc -l)
fi

그런 다음 pat_found가 1이면 $PAT가 없는 파일은 diff 결과이며, 여기에는 <" "로 시작하고 다음 두 문자가 없는 줄만 포함됩니다.

grep '^< ' diff_res | cut -c 3-

완전하고 재구성된 스크립트는 다음과 같습니다.

# Output the desired result on stdin.

f2=/tmp/f2              # Use of PID or mktmp would be better'
diff_res=/tmp/diff_res  # Use of PID or mktmp would be better'

cat << EOF > $f2
$PAT
EOF

diff $FILE $f2 > $diff_res
if test $? -ne 0; then
  grep -q '^> ' $diff_res
  if test $? -ne 0; then
    nb=$(grep -v "^< " $diff_res | wc -l)
    if test $nb -eq 1; then
      grep '^< ' $diff_res | cut -c 3-
    elif test $nb -eq 2; then
      pat_found=$(sed -n -e '1{/^1d/p;/^1,/p}' $diff_res | wc -l)
      test $pat_found -eq 1 && grep '^< ' $diff_res | cut -c 3-
    fi
  fi
fi

rm -f $f2 $diff_res

답변2

나는 당신이 메모리에 맞는 텍스트 파일을 다시 작성한다고 가정합니다(구성 파일을 다시 작성하는 것처럼 보입니다).

다음 스크립트는 쉘 내장 및 cat. 파일 내용에서 첫 번째 발생을 뺀 내용을 인쇄합니다 $PAT. $PAT그렇지 않으면 아무것도 인쇄되지 않습니다.

contents=$(cat "$FILE")
case $contents in
  *"$PAT"*)
    echo "${contents%%$PAT*}${contents#*$PAT}";;
esac

이 코드 조각은 파일에 null 바이트가 포함되어 있지 않고 단일 개행 문자로 끝나며 대시로 시작하지 않는다고 가정합니다. 또한 패턴이 개행 문자로 끝나는 경우 파일 끝에서 패턴을 찾을 수 없습니다. 다음의 보다 복잡한 코드 조각은 임의의 텍스트 파일을 처리합니다.

contents=$(cat "$FILE"; echo a)
contents=${contents%a}
case $contents in
  *"$PAT"*)
    contents="${contents%%$PAT*}${contents#*$PAT}"
    dashes=${contents%%[!-]*}
    echo -n "$dashes"
    echo -n "${contents#$dashes}";;
esac

(당신이 제안한 동작은 패턴 전체를 포함하는 파일과 빈 파일을 구별하는 것을 불가능하게 만듭니다.)

실제로 제안된 중간 기능을 사용하는 것보다 추가/제거 스크립트를 직접 구현하는 것이 더 쉽습니다.

contents=$(cat "$FILE"; echo a)
contents=${contents%a}
append=
case $contents in
  *"$PAT"*) contents="${contents%%$PAT*}${contents#*$PAT}";;
  *) contents="$contents$PAT"
esac
dashes=${contents%%[!-]*}
{ echo -n "$dashes"; echo -n "${contents#$dashes}"; } >"$FILE.new"
mv -- "$FILE.new" "$FILE"

답변3

파일을 문자별로 읽습니다. 해당 문자가 변수의 첫 번째 문자와 일치하면 다음 문자가 비교됩니다. 전체 변수가 일치하지 않는 경우 반환됩니다. 구현할 수도 있습니다더욱 발전된 알고리즘더 빠르게 실행하려면 언어가 쉘이기 때문에 어쨌든 매우 느릴 것입니다.

관련 정보