Comparer les fichiers qui se trouvent dans le répertoire 1 mais pas dans le répertoire 2?

9

J'ai des problèmes avec un script bash que je veux créer

Je sais que ls répertorie les fichiers qui se trouvent dans un répertoire, mais je veux qu'il répertorie les répertoires qui se trouvent dans le répertoire1 mais PAS dans le répertoire2, puis liste les fichiers dans le répertoire2 qui ne sont PAS dans le répertoire1.

Dans une tentative faible, j'ai essayé:

ls -al | diff directory1 directory2

Je compris rapidement pourquoi cela ne fonctionnait pas. Quelqu'un peut-il aider un nerd total?

soju
la source

Réponses:

9

Étant donné bash, cela pourrait être plus facile car

$ comm <(ls -a dir1) <(ls -a dir2)

L' <(command)expression exécute la commande sur un canal et remplace une /dev/fdréférence:

mress:10018 Z$ echo <(ls)
/dev/fd/11

Ainsi, la commande ci-dessus s'exécute ls -asur chaque répertoire et alimente leurs sorties en tant qu'arguments de fichier comm, qui génère jusqu'à 3 colonnes, en retrait par tabulation: entrées uniquement dans le premier, entrées dans les deux, entrées dans le second. (Autrement dit, s'il est dans les deux, il est indenté par un onglet, s'il est uniquement dans le second, il est indenté par 2 onglets.) Vous pouvez également supprimer les colonnes par numéro: comm -1 foo baraffiche uniquement les lignes des deux et les lignes du deuxième fichier, ce dernier étant échancré d' un onglet. (Ceci est le plus souvent utilisé en supprimant tout sauf la colonne souhaitée: comm -13 foo baraffiche uniquement les lignes en commun.)

Étant donné que vous voulez ceux du premier répertoire, cela se traduit par

$ comm -23 <(ls -a dir1) <(ls -a dir2)

Si vous avez besoin de plus que simplement s'il est présent, utilisez diff -r, qui produira des différences pour les fichiers communs et un message d'une ligne pour les fichiers trouvés uniquement dans l'un ou l'autre.

geekosaure
la source
1
lsviendra avec tous les problèmes d'espace blanc, tabulations, sauts de ligne, backspaces et autres dans les noms de fichiers.
utilisateur inconnu
1
Le seul qui va confondre cela est les nouvelles lignes, et je dirais que vous avez quand même des problèmes dans ce cas. :) Si vous êtes paranoïaque, utilisez ls -b.
geekosaur
Pas avec find, n'est-ce pas?
utilisateur inconnu
Cela dépend de ce que vous invoquez find. Mais ma principale plainte findest que c'est un marteau très lourd pour écraser ce qui est généralement une mouche assez petite.
geekosaur
Merci! Cela fonctionne, mais puis-je le faire sortir le contenu du fichier b qui n'est PAS dans le fichier a sans utiliser «comm»?
soju
4

Et voici un pur script. Ce sont les répertoires a et b:

find a b
a
a/a
a/b
a/c
a/f
a/f/h
a/f/i
b
b/b
b/c
b/d
b/f
b/f/g
b/f/h

Voici la commande:

cd a
find ./ -exec test ! -e ../b/{} ";" -print 

production:

./a
./f/i

Échangez a et b pour les fichiers dans a mais pas dans b. Le ! est une négation. -e teste -existence. En prosa: "Tester s'il n'existe pas le fichier trouvé dans un dans ../b".

Remarque: vous devez plonger dans une première, pour obtenir des noms sans «a». Pour la deuxième comparaison, vous devez cd ../b.

Utilisateur inconnu
la source
1

Si vous préférez un outil graphique, utilisez

xxdiff dir1 dir2 

vous devrez peut-être d'abord l'installer. Des programmes similaires sont

gtkdiff
tkdiff

Le commandant de minuit a une compare directoriescommande intégrée, qui fonctionne bien, si vous n'allez pas pour les sous-répertoires.

Utilisateur inconnu
la source
1
Non, je ne recherche pas d'outil graphique, merci!
soju
1

Vous pouvez utiliser la joincommande négligée . Voici une configuration pour deux répertoires d'exemple, d1 / et d2 /, chacun ayant des fichiers avec des noms uniques au répertoire et des fichiers avec des noms en commun avec l'autre répertoire. Ceci n'est qu'un exemple, j'ai donc utilisé des noms de fichiers à une seule lettre pour illustrer les noms de fichiers uniques à l'un ou à l'autre et les noms de fichiers en commun.

# set up for example
mkdir d1 d2
for name in a  b  c  d  e  f  g  h
do
    touch d1/$name
done
for name in e f g h i j k l
do
    touch d2/$name
done

ls -1 d1 > d1.out   # That's "minus one" not "minus ell"
ls -1 d2 > d2.out
join d1.out d2.out       # files common to both d1/ and d2/
join -v 1 d1.out d2.out  # files only in directory d1/
join -v 2 d1.out d2.out  # files only in directory d2/

Pour moi, il montre des fichiers comme celui-ci:

 5:51PM 100 % join d1.out d2.out
e
f
g
h
 5:51PM 101 % join -v 1 d1.out d2.out
a
b
c
d
 5:52PM 102 % join -v 2 d1.out d2.out
i
j
k
l

MISE À JOUR: Vous voudriez faire différentes choses dans la vie réelle pour accueillir des fichiers avec des espaces, comme joinutilise le premier champ "délimité par des espaces" pour décider quelles lignes sont uniqe et quelles lignes sont communes.

Bruce Ediger
la source
Vous devez tester votre programme avec un fichier appelé "d2 / f". Règle générale: n'utilisez presque jamais ls dans les scripts.
utilisateur inconnu
Pouvez-vous clarifier un peu? D2 / ne devrait-il avoir qu'un seul fichier, d2 / f? Parce que j'ai essayé ça, et ça marche comme prévu.
Bruce Ediger
Je pense qu'il est préoccupé par les noms de fichiers qui incluent des espaces (ou des tabulations, car les deux sont des délimiteurs de champ de saisie par défaut pour la jointure ). Peut-être join -t ''(pas de délimiteur) aiderait ce cas.
Chris Johnsen
Oui, ce n'est pas d2/fmais d2/f . Les tabulations et les retours à la ligne dans les noms de fichiers sont rares, mais autorisés.
utilisateur inconnu
0

Vous pouvez utiliser findet awkpour résoudre ce problème.

Avec la disposition suivante:

$ mkdir a b a/1 b/1 b/2 a/3
$ touch a/f1 b/f1 a/f2 b/f3

Partie un:

$ find a b -mindepth 1 -maxdepth 1 -type d | \
    awk -F/ ' { if (!w[$1]) w[$1]=++i; if (w[$1]>1) b[$2]=1; else a[$2]=1; }
          END { for (x in a) if (!b[x]) print x }'
3

Deuxième partie:

$ find b a -mindepth 1 -maxdepth 1 -type f | \
    awk -F/ ' { if (!w[$1]) w[$1]=++i; if (w[$1]>1) b[$2]=1; else a[$2]=1; }
          END { for (x in a) if (!b[x]) print x }'
f3

Cela se compare à une commsolution:

$ comm -23 <(ls a) <(ls b)    
3
f2
$ comm -13 <(ls a) <(ls b)
2
f3

Et à une joinsolution:

$ join -v1 <(ls a) <(ls b)
3
f2
$ join -v2 <(ls a) <(ls b)
2
f3
maxschlepzig
la source
0

utiliser mes fonctions:

setColors ()
{
# http://wiki.bash-hackers.org/scripting/terminalcodes
set -a
which printf >/dev/null 2>&1 && print=printf || print=print # Mandriva doesn't know about printf

hide='eval tput civis'
show='eval tput cnorm'
CLS=$(tput clear)
bel=$(tput bel)

case ${UNAME} in
AIX)
# text / foreground
N=$(${print} '\033[1;30m')
n=$(${print} '\033[0;30m')
R=$(${print} '\033[1;31m')
r=$(${print} '\033[0;31m')
G=$(${print} '\033[1;32m')
g=$(${print} '\033[0;32m')
Y=$(${print} '\033[1;33m')
y=$(${print} '\033[0;33m')
B=$(${print} '\033[1;34m')
b=$(${print} '\033[0;34m')
M=$(${print} '\033[1;35m')
m=$(${print} '\033[0;35m')
C=$(${print} '\033[1;36m')
c=$(${print} '\033[0;36m')
W=$(${print} '\033[1;37m')
w=$(${print} '\033[0;37m')
END=$(${print} '\033[0m')

# background
RN=$(${print} '\033[6;40m')
Rn=$(${print} '\033[40m')
RR=$(${print} '\033[6;41m')
Rr=$(${print} '\033[41m')
RG=$(${print} '\033[6;42m')
Rg=$(${print} '\033[42m')
RY=$(${print} '\033[6;43m')
Ry=$(${print} '\033[43m')
RB=$(${print} '\033[6;44m')
Rb=$(${print} '\033[44m')
RM=$(${print} '\033[6;45m')
Rm=$(${print} '\033[45m')
RC=$(${print} '\033[6;46m')
Rc=$(${print} '\033[46m')
RW=$(${print} '\033[6;47m')
Rw=$(${print} '\033[47m')

HIGH=$(tput bold)
SMUL=$(tput smul)
RMUL=$(tput rmul)
BLINK=$(tput blink)
REVERSE=$(tput smso)
REVERSO=$(tput rmso)
;;
*)
# text / foreground
n=$(tput setaf 0)
r=$(tput setaf 1)
g=$(tput setaf 2)
y=$(tput setaf 3)
b=$(tput setaf 4)
m=$(tput setaf 5)
c=$(tput setaf 6)
w=$(tput setaf 7)
N=$(tput setaf 8)
R=$(tput setaf 9)
G=$(tput setaf 10)
Y=$(tput setaf 11)
B=$(tput setaf 12)
M=$(tput setaf 13)
C=$(tput setaf 14)
W=$(tput setaf 15)
END=$(tput sgr0)

HIGH=$(tput bold)
SMUL=$(tput smul)
RMUL=$(tput rmul)
BLINK=$(tput blink)
REVERSE=$(tput smso)
REVERSO=$(tput rmso)

# background
Rn=$(tput setab 0)
Rr=$(tput setab 1)
Rg=$(tput setab 2)
Ry=$(tput setab 3)
Rb=$(tput setab 4)
Rm=$(tput setab 5)
Rc=$(tput setab 6)
Rw=$(tput setab 7)
RN=$(tput setab 8)
RR=$(tput setab 9)
RG=$(tput setab 10)
RY=$(tput setab 11)
RB=$(tput setab 12)
RM=$(tput setab 13)
RC=$(tput setab 14)
RW=$(tput setab 15)
;;
esac

BLUEf="${B}"
BLUE="${b}"
REDf="${R}"
RED="${r}"
GREENf="${G}"
GREEN="${g}"
YELLOWf="${Y}"
YELLOW="${y}"
MANGENTAf="${M}"
MANGENTA="${m}"
WHITEf="${W}"
WHITE="${w}"
CYANf="${C}"
CYAN="${c}"

OK="${RG}${n}OK${END}"
KO="${RR}${n}KO${END}"
NA="${N}NA${END}"

COLORIZE='eval sed -e "s/{END}/${END}/g" -e "s/{HIGH}/${HIGH}/g" -e "s/{SMUL}/${SMUL}/g" -e "s/{RMUL}/${RMUL}/g" -e "s/{BLINK}/${BLINK}/g" -e "s/{REVERSE}/${REVERSE}/g" -e "s/{REVERSO}/${REVERSO}/g"'
LOWS=' -e "s/{n}/${n}/g" -e "s/{r}/${r}/g" -e "s/{g}/${g}/g" -e "s/{y}/${y}/g" -e "s/{b}/${b}/g" -e "s/{m}/${m}/g" -e "s/{c}/${c}/g" -e "s/{w}/${w}/g"'
HIGHS=' -e "s/{N}/${N}/g" -e "s/{R}/${R}/g" -e "s/{G}/${G}/g" -e "s/{Y}/${Y}/g" -e "s/{B}/${B}/g" -e "s/{M}/${M}/g" -e "s/{C}/${C}/g" -e "s/{W}/${W}/g"'
REVLOWS=' -e "s/{Rn}/${Rn}/g" -e "s/{Rr}/${Rr}/g" -e "s/{Rg}/${Rg}/g" -e "s/{Ry}/${Ry}/g" -e "s/{Rb}/${Rb}/g" -e "s/{Rm}/${Rm}/g" -e "s/{Rc}/${Rc}/g" -e "s/{Rw}/${Rw}/g"'
REVHIGHS=' -e "s/{RN}/${RN}/g" -e "s/{RR}/${RR}/g" -e "s/{RG}/${RG}/g" -e "s/{RY}/${RY}/g" -e "s/{RB}/${RB}/g" -e "s/{RM}/${RM}/g" -e "s/{RC}/${RC}/g" -e "s/{RW}/${RW}/g"'
# COLORIZE Usage:
# command |${COLORIZE} ${LOWS} ${HIGHS} ${REVLOWS} ${REVHIGHS}
}

# diffDir shows diff content between two dirs
diffDir()
{
(($# < 2)) && echo "${W}diffDir ${C}<leftDir> <rightDir> ${c}[[[${C}miss|diff|same|all*${c}] [${C}uniq${c}]] [${C}resolv${c}]]${END}" && return 99
local showWhat=all
local UNIQ=false
local RESOLV=false
local uniqNames="cat"
local resolvPaths="cat"
local rightDirContent=/tmp/diffDir.$$.tmp

local leftDir=$1
local rightDir=$2
case $3 in
mis*) showWhat=miss ;;
dif*|siz*) showWhat=diff ;;
sam*) showWhat=same ;;
*)  showWhat=all ;;
esac
UNIQ=${4:+true}
RESOLV=${5:+true}

[ "$4" == "uniq" ] && uniqNames="awk '/~/ {n=split(\$2,libname,\".\");print libname[1]}'|sort|uniq"
[ "$5" == "resolv" ] && resolvPaths='while read _lib;do /bin/ls ${leftDir}/${_lib}.*;done'

ls -lqF ${rightDir}| awk 'NR>1 {if ($(NF-1) == "->") {printf "%s %s->%s\n",$5,$(NF-2),$NF} else {print $5,$NF}}' | sort -k 2 >${rightDirContent}
ls -lqF ${leftDir}| awk 'NR>1 {if ($(NF-1) == "->") {printf "%s %s->%s\n",$5,$(NF-2),$NF} else {print $5,$NF}}' | sort -k 2 | join -a1 -a2 -1 2 -2 2 -o 1.2,1.1,2.1,2.2 -e 0 - ${rightDirContent} |\
awk -v leftDir=${leftDir} -v rightDir=${rightDir} -v showWhat=${showWhat} '
function commas(d) {
  # http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_65.html
  d = d ""
  gsub(",","",d)
  point = index(d,".") - 1
  if (point < 0) point = length(d)
  while (point > 3) {
    point -= 3
    d = substr(d,1,point)","substr(d,point + 1)
  }
  return d
}
BEGIN {i=1;leftWidth=20;rightWidth=20;totalSizeLeft=0;totalSizeRight=0;sep="----------------------------------------------------------------"}
{
leftColor[i]="{w}";sign[i]="="
if ($2==$3) {if (showWhat!="all" && showWhat!="same") {next} else {leftColor[i]="{N}"}} else {leftColor[i]="{y}";sign[i]="~"}
if ($1 ~ "->") {leftColor[i]="{c}"}
leftName[i]=$1;leftSize[i]=$2;rightSize[i]=$3;rightName[i]=$4
middleColor[i]=leftColor[i]
if (leftName[i]=="0") {leftSize[i]="";leftName[i]="";middleColor[i]="{w}";sign[i]="#"} else {totalLeft++;totalSizeLeft+=leftSize[i]}
if (rightName[i]=="0") {rightSize[i]="";rightName[i]="";leftColor[i]=middleColor[i]="{w}";sign[i]="#"} else {totalRight++;totalSizeRight+=rightSize[i]}
if (showWhat=="same" && sign[i]!="=") {next}
if (showWhat=="miss" && sign[i]!="#") {next}
if (showWhat=="diff" && sign[i]!="~") {next}
if (length($1) > leftWidth) {leftWidth=length($1)}
if (length($4) > rightWidth) {rightWidth=length($4)}
if (leftName[i] ~ "->") {middleColor[i]="{c}"}
i++
}
END {
if (i==1) {print "identical"} else {
printf "%s %."leftWidth"s %.14s : %.14s %."rightWidth"s\n","{c}",sep,sep,sep,sep
printf "%s %"leftWidth"s %14s : %14s %-"rightWidth"s\n","{c}",leftDir,"","",rightDir
for (n=1; n<i; n++) {
  printf "%s %"leftWidth"s %14s %s%s %-14s %-"rightWidth"s\n",leftColor[n],leftName[n],commas(leftSize[n]),middleColor[n],sign[n],commas(rightSize[n]),rightName[n]
}
printf "%s %."leftWidth"s %.14s : %.14s %."rightWidth"s\n","{W}",sep,sep,sep,sep
printf "%s %"leftWidth"s %14s : %-14s %-"rightWidth"s{END}\n","{W}","total : "totalLeft,commas(totalSizeLeft),commas(totalSizeRight),totalRight
}
}' |\
${COLORIZE} ${LOWS} ${HIGHS} |\
eval ${uniqNames} |\
eval ${resolvPaths}

rm -f ${rightDirContent}
}
charognard
la source