bash 스크립트의 서로 다른 디렉터리에 있는 2개의 기본 파일 이름 비교

bash 스크립트의 서로 다른 디렉터리에 있는 2개의 기본 파일 이름 비교

2개의 파일 디렉토리를 매개변수로 사용하는 bash 스크립트를 작성하려고 합니다. 그런 다음 첫 번째 디렉터리의 모든 파일을 반복하여 두 번째 디렉터리에 해당 파일이 있는지 확인합니다. 동일한 기본 이름을 가진 파일이 존재하지 않으면 파일이 삭제됩니다. 이제 이것이 내가 가지고 있는 것이지만, 어떤 이유에서인지 모두 일치한다고 생각하기 때문에 아무것도 하지 않게 됩니다.

echo "$directory1"
echo "$directory2"
for f1 in "$directory1/*"
do
    echo $f1
    match=no
    for f2 in "$directory2/*"
    do
        echo $f2
        name1="$(basename "$f1")"
        name2="$(basename "$f2")"
        if [ "$name1" == "$name2" ]
        then
            match=yes
            break
        fi
    done
    echo "$match"
    if [[ "$match" == no ]]
    then
        rm "$f2"
    fi
done

답변1

다음은 find를 사용하여 각 디렉토리와 기본 이름의 파일을 나열하고 정렬하여 정렬하는 방법입니다. 그런 다음 각 목록을 comm에 입력하고 두 번째 디렉터리가 아닌 첫 번째 디렉터리에 있는 파일만 인쇄합니다. 목록이 예상한 대로 나타나면 rm으로 파이프할 수 있습니다.

comm -2 -3  <(find "$directory1" -maxdepth 1 -type f | xargs -n1 -d\\n basename | sort) <(find "$directory2" -maxdepth 1 -type f | xargs -n1 -d\\n basename | sort)  | xargs -d\\n printf "$directory1/%s\n" | xargs -d\\n rm

참고: @LeviUzodike가 지적했듯이 두 번째 디렉터리에는 없는 첫 번째 디렉터리의 파일을 삭제하려고 하므로 적절하게 변경했습니다. 또한 기본 이름에 -a 매개변수를 사용하지 않습니다.

답변2

원래 솔루션을 수정하려면 다음을 수행하세요.

  1. @Kusalananda가 말했듯 "$directory1/*"이 and 를 별도로 "$directory2/*"sum 으로 바꿔야 합니다. 그렇지 않으면 for 루프가 문자 그대로 " "라는 파일을 찾으려고 시도합니다 ."$directory1"/*"$directory2"/**
  2. 귀하의 목표를 올바르게 이해했다면 해당 줄은 rm "$f2"이어야 합니다 rm "$f1". 그렇지 않으면 프로그램은 일치 $directory2하지 않는 마지막 파일을 $f1삭제할 것입니다 .$f1$directory2

이 두 가지 수정 사항만 적용하면 스크립트가 작동할 것입니다( directory1및 에 directory2올바른 값이 할당되었다고 가정).

그러나 @Kusalananda가 말했듯이 다른 루프를 수행할 필요는 없습니다. @Kusalananda가 다음과 같은 작업을 제안했다고 생각합니다( echos를 제거 하고 이미 언급한 오류를 수정하세요).

for f1 in "$directory1"/*
do
    # ls "$directory2"/$(basename "$f1") &>/dev/null
    # if [[ $? != 0 ]]
    # EDIT below from @Kusalananda
    if [[ -e "$directory2/$(basename "$f1")" ]]
    then
        rm "$f1"
    fi
done

기술적으로 이것은 또 다른 for 루프이지만(왜냐하면 for 루프가 뒤에서 수행되고 있다고 믿기 때문입니다 ls) 훨씬 더 깔끔해 보입니다. 추가 견적이 있었다면 죄송합니다. 나는 그것들을 사용하는 방법을 가장 잘 알지 못합니다.

답변3

zsh배열 빼기 연산자를 사용하는 것이 더 쉽습니다 ${array1:|array2}.

#! /bin/zsh -
dir1=${1:?} dir2=${2:?}

dir1_files=(${dir1%/}/*(DN:t))
dir2_files=(${dir2%/}/*(DN:t))

only_in_dir2=(${dir2:|dir1})

(($#only_in_dir2 == 0)) ||
  rm -rf -- $dir2/$^only_in_dir2

GNU 유틸리티를 사용하면 bash동등한 작업이 다음과 같이 나타날 수 있습니다.

#! /bin/bash -
dir1=${1:?} dir2=${2:?}
export LC_ALL=C

chdir() {
  case $1 in
    (/*) CDPATH= cd -P "$1";;
    ("") echo >&2 invalid empty directory; false;;
    (*)  CDPATH= cd -P "./$1";;
  esac
}
list_files() (
  shopt -s nullglob dotglob # equivalent of zsh's ND
  chdir "$1" &&
    set -- * &&
    (($# > 0)) &&
    printf '%s\0' "$@"
)
    
comm -z13 <(list_files "$dir1") <(list_files "$dir2") |
  (chdir "$dir2" && xargs -r0 rm -rf --)

예, 믿거나 말거나, 이 chdir기능은 POSIX 셸의 디렉터리로 변경하는 방법이며POSIX는 표준의 다음 개정판에서 무엇을 권장할까요?.

zsh경우 해당 파일을 읽을 수 없으면 dir1그 안의 모든 파일이 삭제된다는 점에 유의하시기 바랍니다.dir2

하나를 사용하면 읽을 수 없으면 bash이런 일이 발생합니다dir1또는 검색할 수 없습니다.cd( 필요 에 따라찾다허용하다).

일치하는 파일 목록을 작성하는 동안 오류가 발생했을 때 문제를 해결하려면 다음을 수행할 수 있습니다.

#! /bin/zsh -
dir1=${1:?} dir2=${2:?}
zmodload zsh/system || exit

ERRNO=0
dir1_files=(${dir1%/}/*(DN:t))
dir2_files=(${dir2%/}/*(DN:t))

if ((ERRNO)); then
  syserror -p "An error occurred when trying to read the directories: "
  exit 1
fi

only_in_dir2=(${dir2:|dir1})

(($#only_in_dir2 == 0)) ||
  rm -rf -- $dir2/$^only_in_dir2

관련 정보