¿Comparar archivos que están en el directorio 1 pero no en el directorio 2?

9

Tengo problemas con un script de bash que quiero hacer

Sé que enumerará los archivos que están en un directorio, pero quiero que enumere los directorios que están en el directorio1 pero NO en el directorio2, y luego los archivos en el directorio2 que NO están en el directorio1.

En un intento débil, intenté:

ls -al | diff directory1 directory2

Rápidamente me di cuenta de por qué no funcionó. ¿Alguien puede ayudar a un bash nerd total?

soju
fuente

Respuestas:

9

Dado bash, esto podría ser más fácil ya que

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

La <(command)expresión ejecuta el comando sobre una tubería y sustituye una /dev/fdreferencia:

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

Por lo tanto, el comando anterior se ejecuta ls -aen cada directorio y alimenta sus resultados como argumentos de archivo comm, que genera hasta 3 columnas, con sangría de tabulación: entradas solo en el primero, entradas en ambos, entradas en el segundo. (Es decir, si está en ambos, entonces está sangrado por una pestaña, si solo está en el segundo está sangrado por 2 pestañas). También puede suprimir columnas por número: comm -1 foo barmuestra solo las líneas en ambos y las líneas en el segundo archivo, este último sangrado por una pestaña. (Esto se usa más comúnmente al suprimir todo menos la columna que desea: comm -13 foo barmuestra solo las líneas en común).

Dado que desea aquellos en el primer directorio, eso se traduce en

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

Si necesita algo más que solo si está presente, use diff -r, que generará diferencias para los archivos en común y un mensaje de una línea para los archivos que se encuentran solo en uno u otro.

geekosaur
fuente
1
lsvendrá con todos los problemas con espacios en blanco, pestañas, avances de línea, espacios de fondo y demás en los nombres de archivo.
Usuario desconocido
1
El único que confundirá esto son las nuevas líneas, y diría que de todos modos tienes problemas en ese caso. :) Si usted es paranoico, el uso ls -b.
geekosaur
No con encontrar, ¿verdad?
usuario desconocido
Depende de con qué estés invocando find. Pero mi queja principal findes que es un mazo muy pesado para aplastar lo que generalmente es una mosca bastante pequeña.
geekosaur
¡Gracias! Eso funciona, pero ¿puedo hacer que muestre los contenidos que están en el archivo b que NO están en el archivo a sin usar 'comm'?
soju
4

Y aquí un guión puro. Estos son los directorios ayb:

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

Aquí está el comando:

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

salida:

./a
./f/i

Cambie a y b por archivos en a pero no en b. Los ! Es una negación. -e prueba de -existencia. En prosa: "Probar si no existe el archivo encontrado en a en ../b".

Nota: Tienes que sumergirte en un primero, para obtener nombres sin 'a'. Para la segunda comparación que tiene que cd ../b.

usuario desconocido
fuente
1

Si prefiere una herramienta gráfica, use

xxdiff dir1 dir2 

Es posible que deba instalarlo primero. Programas similares son

gtkdiff
tkdiff

El comandante de medianoche tiene un compare directoriescomando incorporado, que funciona bien, si no va por subdirecciones.

usuario desconocido
fuente
1
No, no estoy buscando una herramienta gráfica, ¡gracias!
soju
1

Podrías usar el joincomando descuidado . Aquí algunas configuraciones para dos directorios de ejemplo, d1 / y d2 /, cada uno de los cuales tiene algunos archivos con nombres únicos para el directorio, y algunos archivos con nombres en común con el otro directorio. Este es solo un ejemplo, por lo que utilicé nombres de archivo de una letra para ilustrar nombres de archivo únicos para uno u otro, y nombres de archivo en común.

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

Para mí, muestra archivos como este:

 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

ACTUALIZACIÓN: Desearía hacer cosas diferentes en la vida real para acomodar archivos con espacios en blanco, ya que joinutiliza el primer campo "delimitado por espacios en blanco" para decidir qué líneas son únicas y qué líneas son comunes.

Bruce Ediger
fuente
Debe probar su programa con un archivo llamado "d2 / f". Regla de oro: casi nunca usa ls en los scripts.
usuario desconocido
¿Puedes aclarar un poco? ¿Debería d2 / solo tener un archivo, d2 / f? Porque probé esto, y funciona como se esperaba.
Bruce Ediger
Creo que le preocupan los nombres de archivo que incluyen espacios (o pestañas, ya que ambos son delimitadores de campo de entrada predeterminados para unirse ). Quizás join -t ''(sin delimitador) ayudaría en ese caso.
Chris Johnsen
Sí, no lo es d2/fpero d2/f . Las pestañas y las nuevas líneas en los nombres de archivo son raras, pero están permitidas.
Usuario desconocido
0

Puedes usar findy awkpara resolver esto.

Con el siguiente diseño:

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

Parte uno:

$ 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

La segunda parte:

$ 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

Esto se compara con una commsolución:

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

Y a una joinsolución:

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

usa mis funciones:

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}
}
carroñero
fuente