Dateien vergleichen, die sich in Verzeichnis 1, aber nicht in Verzeichnis 2 befinden?


9

Ich habe Probleme mit einem Bash-Skript, das ich erstellen möchte

Ich weiß, dass ls Dateien auflisten wird, die sich in einem Verzeichnis befinden, aber ich möchte, dass Verzeichnisse in Verzeichnis1, aber NICHT in Verzeichnis2 aufgelistet werden, und dann Dateien in Verzeichnis2, die sich NICHT in Verzeichnis1 befinden.

In einem schwachen Versuch versuchte ich:

ls -al | diff directory1 directory2

Schnell wurde mir klar, warum es nicht funktionierte. Kann jemand einem totalen Bash-Nerd helfen?

Antworten:


9

Angesichts der Bash könnte dies am einfachsten sein

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

Der <(command)Ausdruck führt den Befehl über eine Pipe aus und ersetzt eine /dev/fdReferenz:

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

Der obige Befehl wird also ls -afür jedes Verzeichnis ausgeführt und gibt seine Ausgaben als Dateiargumente an comm, die bis zu 3 Spalten mit Tabulatoren ausgeben: Einträge nur im ersten, Einträge in beiden, Einträge im zweiten. (Das heißt, wenn es in beiden ist, wird es durch eine Registerkarte eingerückt, wenn es nur in der Sekunde ist, wird es durch 2 Registerkarten eingerückt.) Sie können Spalten auch nach Zahlen unterdrücken: comm -1 foo barZeigt nur die Zeilen in beiden und Zeilen in der zweiten Datei an. Letzteres wird durch eine Registerkarte eingerückt . (Dies wird am häufigsten verwendet, indem alle außer der gewünschten Spalte unterdrückt werden: Es comm -13 foo barwerden nur die gemeinsamen Zeilen angezeigt.)

Vorausgesetzt, Sie möchten diese im ersten Verzeichnis, übersetzt das in

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

Wenn Sie mehr als nur benötigen, ob es vorhanden ist, verwenden Sie diff -r, um Unterschiede für die gemeinsamen Dateien und eine einzeilige Nachricht für Dateien auszugeben, die nur in der einen oder der anderen gefunden werden.


1
lswird mit allen Problemen mit Leerzeichen, Tabulatoren, Zeilenvorschüben, Backspaces und dergleichen in Dateinamen kommen.
Benutzer unbekannt

1
Das einzige, was dies verwirren wird, sind Zeilenumbrüche, und ich würde behaupten, dass Sie in diesem Fall sowieso Probleme haben. :) Wenn du paranoid bist, benutze ls -b.
Geekosaurier

Nicht mit finden, oder?
Benutzer unbekannt

Kommt darauf an, womit du dich aufrufst find. Aber meine Hauptbeschwerde findist, dass es ein sehr schwerer Vorschlaghammer ist, eine normalerweise eher kleine Fliege zu schlagen.
Geekosaurier

Vielen Dank! Das funktioniert, aber kann ich es dazu bringen, den Inhalt in Datei b auszugeben, der NICHT in Datei a ist, ohne 'comm' zu verwenden?
Soju

4

Und hier ein reines Drehbuch. Dies sind die Verzeichnisse a und 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

Hier ist der Befehl:

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

Ausgabe:

./a
./f/i

Tauschen Sie a und b gegen Dateien in a, aber nicht in b. Das ! ist eine Negation. -e testet auf -existenz. In prosa: "Teste, ob die in a in ../b gefundene Datei nicht vorhanden ist".

Hinweis: Sie müssen in eine erste eintauchen, um Namen ohne 'a' zu erhalten. Für den zweiten Vergleich muss man cd ../b.


1

Wenn Sie ein grafisches Werkzeug bevorzugen, verwenden Sie

xxdiff dir1 dir2 

Möglicherweise müssen Sie es zuerst installieren. Ähnliche Programme sind

gtkdiff
tkdiff

Der Mitternachtskommandant hat einen compare directorieseingebauten Befehl, der gut funktioniert, wenn Sie keine Unterverzeichnisse wählen.


1
Nein, ich suche kein grafisches Werkzeug, danke!
Soju

1

Sie können den vernachlässigten joinBefehl verwenden. Hier einige Einstellungen für zwei Beispielverzeichnisse, d1 / und d2 /, von denen jedes einige Dateien mit Namen enthält, die für das Verzeichnis eindeutig sind, und einige Dateien mit Namen, die mit dem anderen Verzeichnis gemeinsam sind. Dies ist nur ein Beispiel, daher habe ich Dateinamen mit einem Buchstaben verwendet, um Dateinamen zu veranschaulichen, die für den einen oder anderen eindeutig sind, sowie gemeinsame Dateinamen.

# 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/

Für mich zeigt es Dateien wie diese:

 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

UPDATE: Sie möchten im wirklichen Leben verschiedene Dinge tun, um Dateien mit Leerzeichen aufzunehmen, da joindas erste Feld "durch Leerzeichen begrenzt" verwendet, um zu entscheiden, welche Zeilen eindeutig und welche Zeilen gemeinsam sind.


Sie sollten Ihr Programm mit einer Datei namens "d2 / f" testen. Faustregel: Verwenden Sie ls fast nie in Skripten.
Benutzer unbekannt

Können Sie etwas klarstellen? Sollte d2 / nur eine Datei haben, d2 / f? Weil ich es versucht habe und es wie erwartet funktioniert.
Bruce Ediger

Ich denke, er ist besorgt über Dateinamen, die Leerzeichen enthalten (oder Tabulatoren, da beide Standardeingabefeldbegrenzer für Joins sind ). Vielleicht würde join -t ''(kein Trennzeichen) diesem Fall helfen.
Chris Johnsen

Ja, es ist nicht , d2/faber d2/f . Tabulatoren und Zeilenumbrüche in Dateinamen sind selten, aber zulässig.
Benutzer unbekannt

0

Sie können dies verwenden findund awklösen.

Mit folgendem Layout:

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

Teil eins:

$ 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

Zweiter Teil:

$ 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

Dies ist vergleichbar mit einer commLösung:

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

Und zu einer joinLösung:

$ join -v1 <(ls a) <(ls b)
3
f2
$ join -v2 <(ls a) <(ls b)
2
f3

0

benutze meine Funktionen:

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}
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.