다음 형식의 파일 목록이 있습니다.
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
-00
Perl에게 단락 모드에서 입력을 읽도록 지시합니다 list.txt
(단락은 하나 이상의 빈 줄로 구분됩니다). 이 -a
옵션을 사용하면 Perl이 자동으로 각 입력 단락을 이름이 지정된 배열로 분할합니다 @F
( -F'\n'
이 옵션으로 인해 줄 바꿈을 구분 기호로 사용). 그런 다음 스크립트는 @F의 첫 번째 요소(with shift
)를 버리고 @F의 나머지 부분을 @del
with 라는 다른 배열 에 추가합니다 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는 빈 필드를 유지하지만 그것도 도움이 되지 않습니다.)foo
bar
foo<newline>bar
foo::bar
IFS=:
파일을 한 줄씩 읽는 것이 더 쉬울 수도 있습니다. 이렇게 하면 빈 줄 하나가 구분 기호로 처리됩니다. 훨씬 쉽고 빈 줄을 어떻게 처리할지 알 수 없기 때문입니다.
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
출력이 정확하면 삭제하세요.