Список файлов, которые находятся в каталоге1, но НЕ в каталоге2 и наоборот?

Привет, я запустил сценарий оболочки bash, и я пытаюсь сделать script для назначения, когда при вводе двух каталогов он будет проверять, существуют ли они и отображать соответствующее сообщение об ошибке, и если оба каталога существуют, он перечислит различия между текущими каталогами.

$ cd dir-1
$ myshellscript . dir-2 (comparing . aka dir-1 against dir-2) 

Вывод:

Files that are in . but not in dir-2
-rw------- 1 ddddd users   1 2011-03-1 01:26 123123123

Files that are in dir-2 but not in .
-rw------- 1 ddddd users   1 2011-03-1 01:26 zzzzzzzzzzzz

Что я до сих пор не обнаружил, существует ли каталог или нет различий в списках:

dir-1=$1
dir-2=$2

if [ $# > 2  ]
   then
      echo "Usage: compdir dir-name1 dir-name 2"
      exit 1
   elif [ $# < 2 ]
      then
         echo "Usage: comdir dir-name1 dir-name 2"
   elif [ ! -d "[email protected]" ]
      then
         echo "/[email protected] is not a valid existing directory"
   else
      exit 0
fi

echo $dir-1
echo $dir-2

Список команд, с которыми мне нужно работать, иначе я бы использовал comm -32 <(ls -la dir-1) <(ls -la dir-2)

http://dl.dropbox.com/u/20930447/index.html

Ответ 1

немного грубо - но самый простой способ, который я всегда использую, - это (можно играть с параметрами diff, я обычно использую разные grep

diff -rcw DIR1 DIR2| grep ^Only

то вы можете сортировать и форматировать, как вам нравится

Пересмотренный формат (менее эффективен, поскольку мы запускаем diff здесь... легко решаем)

echo files only in $dir1
LST=$(diff ${dir1} ${dir2}| grep "^Only in ${dir1}"| sed '[email protected]^.*: @@')
(cd ${dir1}; ls -l ${LST})

echo files only in $dir2
LST=$(diff ${dir1} ${dir2}| grep "^Only in ${dir2}"| sed '[email protected]^.*: @@')
(cd ${dir2}; ls -l ${LST})

Расширение над выражением sed выше:
s = поиск и замена
три "@" разделяют выражения (это ТРАДИЦИОННО делается с помощью "/" )
^ соответствует началу строки (заставляет остальные не совпадать в другом месте), означает любой символ * означает предыдущее выражение (. == match any char) 0-N раз ":" - это то, что я сопоставил с выходом diff "Только в X:"

Посмотрите, мама, нет рук - теперь без "sed" ее начало становится все менее и менее грубым.

XIFS="${IFS}"
IFS=$'\n\r'
for DIFFLINE in $(diff ${dir1} ${dir2}|grep ^Only); do
  case "${DIFFLINE}" in
   "Only in ${dir1}"*)  
    LST1="${LST1} ${DIFFLINE#*:}"
    ;;
   "Only in ${dir2}"*)  
    LST2+="${DIFFLINE#*:}"
    ;;
  esac
done
IFS="${XIFS}"

echo files only in $dir1
(cd ${dir1}; ls -l ${LST1})

echo files only in $dir2
(cd ${dir2}; ls -l ${LST2})

Вы, вероятно, захотите узнать о IFS... ему нужно некоторое чтение в руководстве bash, но в основном это символы разделителя полей... по умолчанию они включают пробелы, и я не хочу, чтобы цикл был питается фракциями строк, просто завершает строки - поэтому в течение всего цикла я переопределяю IFS по умолчанию только на новые строки и возврат каретки.

Кстати, ваш профессор читает stackoverflow, может быть, в следующий раз вам не разрешат использовать точки с запятой;-)... (вернуться к "man bash"... BTW, если вы делаете "man bash" сделать это в emacs, намного легче читать IMO)

Ответ 2

Это почти работает. В основном это происходит из-за отсутствия файлов, похожих друг на друга в алфавитном порядке между двумя каталогами.

sdiff -s <(ls -1 dir1) <(ls -1 dir2)

Ответ 3

Мне нравится использовать diff для сравнения:

diff <(ls -1 dir1) <(ls -1 dir2) | awk '{if ($1==">") print "in dir 2: "$2; if($1=="<") print "in dir 1: "$2;}'

Вы также можете проанализировать его с помощью:

diff <(ls -1 $dir1) <(ls -1 $dir2) | while read status filename
do
  [ "$status" == "<" ] && echo "in dir 1: $(ls -l $dir1/$filename)"
  [ "$status" == ">" ] && echo "in dir 2: $(ls -l $dir2/$filename)"
done

Ответ 4

Основной рецепт того, что вы хотите сделать, уже выполняется с помощью утилиты diff, доступной в Unix-подобных системах, или используя cygwin или GnuWin в Windows. Вы должны использовать этот факт.

Если у меня есть каталог a и b со следующим содержимым:

[email protected]:~$ ls -R
.:
a  b

./a:
d  e  f  x  y  z

./b:
i  j  k  x  y  z

x, y и z в каждом каталоге точно совпадают.

Я могу добиться того, что вы хотите, используя команду diff следующим образом:

[email protected]:~$ diff a b
Only in a: d
Only in a: e
Only in a: f
Only in b: i
Only in b: j
Only in b: k

Если я добавлю новый файл в каждый каталог (с именем new), который отличается, я получаю следующее:

[email protected]:~$ diff a b
Only in a: d
Only in a: e
Only in a: f
Only in b: i
Only in b: j
Only in b: k
diff a/new b/new
1c1
< ezraa
---
> ezra

То есть, это даже скажет вам, как и где происходят различия в файлах. Конечно, если вы не хотите или не нуждаетесь в этой функции, вы можете не использовать ее.

Вы также получаете следующее:

[email protected]:~$ diff a c
diff: c: No such file or directory

При тяжелом подъеме этой программы, выполняемой с помощью diff, большая часть написанного вами будет обрабатывать вывод этой команды, а затем манипулировать или выводить ее по своему усмотрению.

Один из awk или sed может представлять особый интерес, когда вы это делаете.

Ответ 5

awk '{a[$0]++}END{print "some message"; for(i in a)if(a[i]<2){print i}}' <(ls -1 dir2) <(ls -1 dir1)

Доказательство концепции

$ ls -1 dir1
file1.txt
file2.txt
file3.txt
file4.txt
file5.txt

$ ls -1 dir2
file1.txt
file3.txt
file4.txt

$ awk '{a[$0]++}END{print "Files in dir1 but NOT in dir2"; for(i in a)if(a[i]<2){print i}}' <(ls -1 dir2) <(ls -1 dir1)
Files in dir1 but NOT in dir2
file5.txt
file2.txt

Ответ 6

echo "Files that are in $dir1 but not $dir2"
for i in "$dir1/"*; do
    [[ -e "$dir2/$i" ]] || (cd "$dir1"; ls -l "$i")
done
echo

Это одна половина.

Замените [[ ... ]] на [ ... ] или test ..., если не используете Bash.