$(cat list.txt)의 fn에 대한 도움말 스크립트/IFS/

$(cat list.txt)의 fn에 대한 도움말 스크립트/IFS/

다음 형식의 파일 목록이 있습니다.

file1.jpg
file2.jpg
file3.jpg
newline
newline
file4.jpg
file5.jpg
file6.jpg
newline
newline
file7.jpg
file8.jpg
file9.jpg
etc

내 bash 스크립트는 IFS=$"\n" 으로 설정되어 있습니다. 첫 번째 파일을 건너뛰고 나머지 파일을 삭제한 다음 두 개의 줄 바꿈이 나타나면 개수를 0으로 재설정하고 다음 배치에 대해 다시 수행하고 싶습니다. IFS를 단일 줄 바꿈으로 설정하면 예상치 못한 결과가 발생합니다. 이미지 파일이 더 이상 올바르게 구문 분석되지 않습니다. 스크립트에서 IFS를 제거하면 스크립트는 두 개의 줄 바꿈을 감지하지 못합니다. 돕다! 그리고 미리 감사드립니다.

암호:

#!/bin/bash
#
# MASS DELETE
#
IFS=$"\n\n"
count=0
deleted=0
saved=0
for fn in $(cat list.txt)
do
        length=${#fn}
        ext=${fn:length-3:3}
        echo "**$fn**"

        if [ $ext != "jpg" ]; then
                echo "**Newline**"
                count=0
        else
#               (( ++count ))
#               if [ $count -ge 1 ]; then
#                       echo "Removing $fn..."
#                       #rm $fn
#               else
#                       echo "Saving $fn..."
#               fi
                echo "Do Stuff"
        fi
done

출력(오류!)

Rigel@Minty-VirtualBox:~/data/comics/2020$ ./mass_del.sh
**12-Dec/miltpriggee-2020-12-10.jpg
12-Dec/miltpriggee-2020-12-11.jpg
12-Dec/miltpriggee-2020-12-30.jpg
12-Dec/miltpriggee-2020-12-17.jpg
12-Dec/miltpriggee-2020-12-21.jpg
12-Dec/miltpriggee-2020-12-28.jpg
12-Dec/miltpriggee-2020-12-01.jpg
12-Dec/miltpriggee-2020-12-03.jpg
12-Dec/miltpriggee-2020-12-12.jpg
12-Dec/miltpriggee-2020-12-15.jpg
12-Dec/miltpriggee-2020-12-20.jpg
12-Dec/miltpriggee-2020-12-25.jpg
12-Dec/miltpriggee-2020-12-07.jpg
12-Dec/miltpriggee-2020-12-27.jpg
12-Dec/miltpriggee-2020-12-29.jpg
12-Dec/miltpriggee-2020-12-16.jpg
12-Dec/miltpriggee-2020-12-26.jpg
12-Dec/miltpriggee-2020-12-02.jpg
12-Dec/miltpriggee-2020-12-18.jpg
12-Dec/miltpriggee-2020-12-06.jpg
12-Dec/miltpriggee-2020-12-19.jpg
12-Dec/miltpriggee-2020-12-13.jpg
12-Dec/miltpriggee-2020-12-04.jpg
12-Dec/miltpriggee-2020-12-31.jpg
12-Dec/miltpriggee-2020-12-22.jpg
12-Dec/miltpriggee-2020-12-24.jpg
12-Dec/miltpriggee-2020-12-14.jpg
12-Dec/miltpriggee-2020-12-05.jpg
12-Dec/miltpriggee-2020-12-09.jpg
12-Dec/miltpriggee-2020-12-08.jpg
12-Dec/miltpriggee-2020-12-23.jpg


12-Dec/kevi**
**Newline**

답변1

awk작은따옴표가 포함된 파일 이름 없이 이 작업을 수행 할 수 있습니다 .

awk -v q="'" '
    $0 == "" { count=0; next }
    count++ { print "Delete:", $0; system("echo rm -f -- " q $0 q) }
' list.txt

정말로 쉘 루프를 사용하고 싶다면 다음과 같이 할 수 있습니다:

while IFS= read -r line
do
    # Blank line resets the skip counter
    if [ -z "$line" ]
    then
        count=0

    # Skip the first non-blank line (count==0) then delete others
    elif [ $((count++)) -gt 0 ]
    then
        echo "Delete: $line"
        echo rm -f -- "$line"
    fi
done <list.txt

두 경우 모두 선행을 제거하여 파일 삭제 작업을 수행합니다 echo.echo rm

답변2

일반적으로 bash 및 쉘 스크립트는 이 작업에 끔찍한 도구입니다. 이를 수행하려면 awk 또는 Perl과 같은 것을 사용하는 것이 더 나을 것입니다. 예를 들어:

perl -00 -F'\n' -ae 'shift @F; push @del, @F; END {unlink @del}' list.txt

-00Perl에게 단락 모드에서 입력을 읽도록 지시합니다 list.txt(단락은 하나 이상의 빈 줄로 구분됩니다). 이 -a옵션을 사용하면 Perl이 자동으로 각 입력 단락을 이름이 지정된 배열로 분할합니다 @F( -F'\n'이 옵션으로 인해 줄 바꿈을 구분 기호로 사용). 그런 다음 스크립트는 @F의 첫 번째 요소(with shift)를 버리고 @F의 나머지 부분을 @delwith 라는 다른 배열 에 추가합니다 push. 모든 입력을 읽고 처리한 후 END블록이 실행되어 @del배열의 모든 파일 이름을 제거(링크 해제)합니다.

원하는 경우 "nnn 파일을 삭제하시겠습니까(y/n)?"와 같은 확인 질문을 쉽게 추가할 수 있으며, 삭제하기 전에 삭제할 모든 파일을 나열할 수도 있습니다. 아니면 삭제된 파일 수를 인쇄해 보세요.

어떤 이유로 bash에서 삭제를 수행하려는 경우 @del대신 END 블록(파일 이름 사이의 구분 기호로 NUL 사용)에 배열을 인쇄 하도록 할 수 unlink @del있으며 bash 스크립트는 출력을 다음과 같이 파이프할 수 있습니다 xargs -0r rm. 예를 들어

perl -00 -F'\n' -ae '
    shift @F; push @del, @F;
    END { print join("\0", @del), "\0" }' list.txt |
  xargs -0r rm

마지막에 한꺼번에 파일 링크를 해제하는 대신 각 단락을 읽은 후 파일 링크를 해제하는 또 다른 짧은 버전이 있습니다. 이 버전은 삭제할 파일의 누적 목록을 유지하는 데 신경 쓰지 않습니다.

perl -00 -F'\n' -ae 'shift @F; unlink @F' list.txt

이러한 스크립트의 작동 방식을 보여주기 위해 아무것도 삭제하지 않는 약간 다른 버전이 있습니다. 대신, 수행할 작업만 인쇄합니다.

$ perl -00 -F'\n' -ae '
  push @keep, shift @F;
  push @del, @F;
  END {
    printf "Keep   %i: %s\n", scalar @keep, join(", ", @keep);
    printf "Delete %i: %s\n", scalar @del, join(", ", @del)
  }' list.txt 
Keep   3: file1.jpg, file4.jpg, file7.jpg
Delete 6: file2.jpg, file3.jpg, file5.jpg, file6.jpg, file8.jpg, file9.jpg

@F의 첫 번째 요소를 버리는 대신 이를 배열에 추가합니다 @keep. 나머지 요소는 @del이전과 같이 추가됩니다. END 블록은 유지되거나 삭제될 파일 수와 함께 두 개의 배열을 인쇄합니다.

답변3

IFS=$"\n\n"설정과 동일한 설정을 IFS='\n\n'백슬래시, 문자 n, 백슬래시, 문자 n으로 설정합니다. 백슬래시 이스케이프를 해석하려면 국제화(iirc)에 사용되는 를 $'...'대신 사용해야 합니다 .$"..."

어쨌든, 여기서는 도움이 되지 않습니다. 단어 분할은 연속된 공백 구분 기호를 하나로 처리하므로 and 는 와 동일하게 foo<newline><newline>bar처리됩니다 . (공백이 아닌 구분 기호의 경우에는 해당되지 않습니다. 예를 들어 with는 빈 필드를 유지하지만 그것도 도움이 되지 않습니다.)foobarfoo<newline>barfoo::barIFS=:

파일을 한 줄씩 읽는 것이 더 쉬울 수도 있습니다. 이렇게 하면 빈 줄 하나가 구분 기호로 처리됩니다. 훨씬 쉽고 빈 줄을 어떻게 처리할지 알 수 없기 때문입니다.

first=1
while IFS= read -r line; do
    # skip leading empty lines and the first non-empty one
    if [ "$first" ]; then
        if ! [ -z "$line" ]; then
            echo "skipping $line"
            first=
        fi
        continue
    fi
    # if line is not empty, remove the file
    # if empty, go back to first line processing
    if [ "$line" ]; then
        echo rm -- "$line"
    else
        first=1
    fi
done

다음과 같은 것을 입력하세요

file1.jpg
file2.jpg
file3.jpg


file4.jpg
file5.jpg
file6.jpg

file7.jpg
file8.jpg
file9.jpg

그것은 줄 것이다

skipping file1.jpg
rm -- file2.jpg
rm -- file3.jpg
skipping file4.jpg
rm -- file5.jpg
rm -- file6.jpg
skipping file7.jpg
rm -- file8.jpg
rm -- file9.jpg

전면에는 보안 잠금 장치가 echo있으며 rm이를 제거하면 실제로 파일이 삭제됩니다.


물론 Perl에서도 동일한 작업을 수행할 수 있는데, rm각 파일을 분기할 필요 없이 파일을 삭제하므로 속도가 더 빨라집니다. @roaima의 답변에서 논리가 제거되었습니다.

$ perl -lne 'chomp; if (/^$/) { $count=0; next; }; 
             next if ($count++ == 0); 
             print "delete: $_"; 
             next; 
             unlink($_) or warn "unlink ($_): $!"' < foo.txt
delete: file2.jpg
delete: file3.jpg
delete: file5.jpg
delete: file6.jpg
delete: file8.jpg
delete: file9.jpg

next과 사이에는 print보안 unlink잠금 장치가 있으며 이를 제거하면 실제로 파일이 삭제됩니다.

답변4

awk+GNU 사용 xargs:

$ awk 'NF&&p;{p=NF}' list.txt | xargs -rd'\n' echo rm --
rm -- file2.jpg file3.jpg file5.jpg file6.jpg file8.jpg file9.jpg etc

echo출력이 정확하면 삭제하세요.

관련 정보