두 디렉터리를 비교해서 한쪽에만 존재하는 폴더를 제거할 수 있는 기능이 필요한데, 이게 우분투해서 할려니 쉽지 않더라구요. (윈도우에는 WinMerge가 그 기능을 가지고 있구요.)
find, grep, comm 등 여러 명령어를 응용해보려 해도 제머리로는 도저히 안됩니다. (meld도 도움이 되긴 한데 지금 하려는 작업이 너무 양이 많아서요…)
두 디렉터리 중 원본에만 존재하는 폴더를 제거할수 있는 명령어나,
두 디렉터리 중 작업본에만 존재하는 폴더를 특정 폴더로 이동할수 있는 명령어, 어디 없을까요?
user531
(별님)
1월 29, 2014, 10:54오전
2
저는 이런식으로 해결했습니다.
두 곳에 존재하는 파일을 목록으로 작성한뒤, 두 목록에서 중복되지 않는 행을 찾아내고, 이 대상에 대해 작업을 했습니다.
위의 경우, 반복문에서 폴더인 경우가 아니면 작업하지 않도록 하면 되겠네요.
bash로 만든 작업의 대강입니다.
[code:2mzr62l3]#!/usr/bin/env bash
IFS=$‘\n’
if [ $# == 0 ]; then echo -e ‘첫번째 인자는 원본, 두번쨰 인자는 목적지입니다.\nex) "’"$0"‘" ~/source ~/target\n만약 세번째 인자가 존재한다면, 파일을 대상으로 이동시킵니다.’; exit; fi
source=$1
target=$2
backup=$3
if [ -z "$bakup" ]; then #삭제하는 경우
work(){ rm -v – "$source/$i" "$target/$i" 2>/dev/null; }
else #이동하는 경우
work(){ mv -Tv – "$source/$i" "$target/$i" "$bakup" 2>/dev/null; }
fi
for i in $(sort <(ls -A1 – "$source") <(ls -A1 – "$target") | uniq -u); do
[ -d "$i" ] || contine
work
done
exit[/code:2mzr62l3]
rsync 명령에 --delete
옵션이 있습니다.
[code:hr86k813]➜ tmp ls src
file2 file1 file3 file5 file4
➜ tmp ls dst
file2 file1 file3 file5 file4 file6 file7 file9 file8 file10
➜ tmp rsync -av --delete src/ dst/
sending incremental file list
deleting file9
deleting file8
deleting file7
deleting file6
deleting file10
./
file1
file2
file3
file4
file5
sent 332 bytes received 160 bytes 984.00 bytes/sec
total size is 0 speedup is 0.00
➜ tmp ls src
file2 file1 file3 file5 file4
➜ tmp ls dst
file2 file1 file3 file5 file4[/code:hr86k813]
제 경우 rsync를 사용할때는 일단 -avs 옵션은 무조건 줍니다.
rsync -avs src dst
src, dst를 적을 때 끝에 /의 유무에 따라 동작이 틀려 집니다.
src/ dst/
src dst/
이 둘은 의미가 많이 다릅니다. 후자의 경우 dst/src 경로로 복사가 됩니다.
잘 모르겠다 싶을 때는 일단 어떻게 될지 실험만 해 봅니다.
rsync -avs src dst -nih
-n(–dry-run) 옵션이 실제 수행하지는 않고 어떻게 될지만 미리 보여주는 역할을 합니다. -i 옵션과 -h 옵션은 출력 결과를 약간 더 상세하고 보기좋게 해줍니다.
rsync는 일종의 백업 및 동기화 도구입니다.
목표하는 바가 원본의 백업이고, 이에 따라, 백업과 원본을 비교해서 똑같게 만드는 것을 목표로 하는 것입니다.
그렇기 때문에, 원본에만 있는 파일을 백업디렉토리가 아닌 다른 곳에 복사한다던가,
백업본에만 있는 것을 다른 곳으로 옮긴다던가 하는 기능은 없습니다.
즉, 작업은 원본/백업 이 두가지에 대해서만 이루어 집니다.
샘플을 만들어 봤습니다.
[code:3nuvr1sm]#!/bin/bash
IFS="
"
repl() {
perl -e ’
#!/usr/bin/env perl
use IO::Handle;
autoflush STDOUT 1;
autoflush STDERR 1;
my $i=0;
if ("$ARGV[0]" eq "-") {
$_ = <STDIN>;
chomp;
} else {
$_ = @ARGV [0];
}
my $cnt = 0;
if ($#ARGV>2) {
$cnt = $ARGV[3];
}
if ($cnt<1) {
$cnt = 0;
}
my $idx=0;
my $a=$;
while ( ($idx=index($ ,$ARGV[1])) >= 0) {
substr($, $idx, length($ARGV[1]), $ARGV[2]);
$idx = $idx + length($ARGV[2]);
next if $cnt eq 0;
if ($cnt > 0) {
$cnt = $cnt - 1;
}
last if $cnt eq 0;
}
if ($cnt eq 0 or $idx>=0) {
print "$ \n";
}
else {
print STDERR "$_\n";
}
’ – "$@"
}
diff_dir() {
SRC="$1"
DEST="$2"
diff -rq "$1" "$2"|sed -r ’
s/^Only in (.): (. )$/+ \1/\2/
s/^Binary files (.) and (. ) differ$/! \1/
s/^Files (.) and (. ) differ$/! \1/
'|sort -k2|{
while read LINE; do
SYMB=${LINE%% }
LINE=${LINE# }
LEN=${#1 }
if [ "$SYMB" = "+" ]; then
if [ "${LINE:0:$LEN}" = "$1" ]; then
echo 1 "$LINE"
else
echo 2 "$LINE"
fi
else
echo 0 "$LINE"
fi
done
}
}
to_pathname() {
while read LINE;
do
SYMB="${LINE%% }"
FILE="${LINE# }"
FILE="${FILE%/}"
[ -d "$FILE" ] && echo "$SYMB $FILE/" || echo $SYMB "$FILE"
done|uniq
}
to_dirname() {
while read LINE;
do
SYMB="${LINE%% }"
FILE="${LINE# }"
FILE="${FILE%/}"
[ -d "$FILE" ] && echo "$SYMB ${FILE}" || echo $SYMB $(dirname "$FILE"|sed ‘s/^. ;$//’)
done
}
to_parent_dirname() {
to_dirname|awk ‘{
f=substr($0,3)
if (prev=="") {
print $0
prev=f
}
else if (index(f,prev) != 1) {
print $0
prev=f
}
}’
}
find_dir() {
find "$1" -mindepth 1 -type d
}
diff_dir_only() {
SRC="$1"
DEST="$2"
TMP1=$(mktemp)
TMP2=$(mktemp)
diff -Nur <(find_dir "$1") <(find_dir "$2")|tail -n +4|sed ‘/^ /d;s/^-/1 /;s/^+/2 /’
/bin/rm -f "$TMP1" "$TMP2"
}
to_relpath() {
sed -r "s|^0 .{${#SRC }}/|0 |;te;s|^1 .{${#SRC }}/|1 |;te;s|^2 .{${#DEST }}/|2 |;:e"
}
to_abspath() {
while read LINE; do
N="${LINE:0:1}"
TARGET=${LINE:2}
if [ "$N" = "2" ]; then
TARGET="$DEST/$TARGET"
else
TARGET="$SRC/$TARGET"
fi
echo "$N $TARGET"
done
}
make_cmd() {
while read LINE; do
N="${LINE:0:1}"
TARGET=${LINE:2}
CMD=$(repl "$1" "{*}" "$TARGET" 2>/dev/null)
repl "${CMD:-$1}" "{$N}" "$TARGET" |sed ‘/{[0-9]}/d’ 2>/dev/null
done
}
디렉토리만 비교
echo diff_dir_only "$1" "$2"
echo "-------------------------"
디렉토리 비교 - 디렉토리내 다른 파일/디렉토리
echo diff_dir "$1" "$2"
diff_dir "$1" "$2"
echo "-------------------------"
디렉토리 비교 - 디렉토리내 다른 파일/디렉토리(디렉토리명에 /붙임)
echo diff_dir "$1" "$2"|to_pathname
diff_dir "$1" "$2"|to_pathname
echo "-------------------------"
디렉토리 비교 - 디렉토리내 다른 파일은 포함하는 디렉토리로 변환
echo diff_dir "$1" "$2"|to_dirname
diff_dir "$1" "$2"|to_dirname
echo "-------------------------"
diff_dir "$1" "$2"|to_dirname |to_relpath
echo "-------------------------"
디렉토리 비교 후 명령실행
#diff_dir "$1" "$2"|to_parent_dirname|to_relpath|make_cmd "echo mv $SRC/{1} $DEST/"| sh
echo diff_dir "$1" "$2" |to_parent_dirname and execute command
diff_dir_only "$1" "$2"|to_parent_dirname|make_cmd "echo mv {1} $DEST"| sh
[/code:3nuvr1sm]