2015-11-29 36 views
13

Qual è il modo migliore per verificare se due percorsi sono uguali in Bash? Ad esempio, data la struttura della directoryCome verificare se due percorsi sono uguali in Bash?

~/ 
    Desktop/ 
    Downloads/ (symlink to ~/Downloads) 
    Downloads/ 
    photo.png 

e supponendo che la directory corrente è la directory home, tutti i seguenti sarebbe equivalente:

./     and ~ 
~/Desktop    and /home/you/Desktop 
./Downloads   and ~/Desktop/Downloads 
./Downloads/photo.png and ~/Downloads/photo.png 

Esiste un modo Bash nativo per fare questo?

+0

maggior parte delle risposte (tranne il mio) si romperà con i supporti legare e altre cose che i link simbolici può fare due percorsi si riferiscono alla stessa cosa. –

risposta

20

comandi di test di Bash hanno un operatore di -ef per questo scopo

if [[ ./ -ef ~ ]]; then ... 

if [[ ~/Desktop -ef /home/you/Desktop ]]; then ... 

ecc ...

$ help test | grep -e -ef 
     FILE1 -ef FILE2 True if file1 is a hard link to file2. 
+1

Bello! Sembra proprio questo metodo [confronta i numeri di dispositivo e inode] (http://linux.about.com/library/cmd/blcmdl1_test.htm), quindi accetto questa risposta. –

4

Native modo bash:

pwd -P restituisce la directory fisica indipendentemente dal link simbolici.

cd "$dir1" 
real1=$(pwd -P) 
cd "$dir2" 
real2=$(pwd -P) 
# compare real1 with real2 

Un altro modo è quello di utilizzare cd -P, che seguirà link simbolici, ma si lascia nella directory fisica:

cd -P "$dir1" 
real1=$(pwd) 
cd -P "$dir2" 
real2=$(pwd) 
# compare real1 with real2 
+0

Funziona solo per collegamenti simbolici. Se lo stesso filesystem è montato su più punti (ad esempio 'mount --bind/raid0/var-cache/var/cache'), la soluzione migliore è confrontare gli inode. –

+1

Questo non fa esattamente quello che vuoi, dal momento che il primo cambio di directory influenza l'interpretazione di qualsiasi percorso relativo nel secondo. Per risolvere questo problema, dovresti spostare il 'cd' all'interno della sostituzione del comando (quindi viene eseguito all'interno della sottoshell). – ruakh

+0

@ruakh è un punto eccellente che ho trascurato. la risposta di geirha è anche abbastanza interessante. –

3

Se avete coreutils 8.15 o versione successiva, si dispone di un comando realpath che normalizza completamente un sentiero. Rimuove qualsiasi componente . e .., rende assoluto il percorso e risolve tutti i collegamenti simbolici. Quindi:

if [ "$(realpath "$path1")" = "$(realpath "$path2")" ]; then 
    echo "Same!" 
fi 
2

metodi basati sulla risoluzione dei link simbolici non riescono quando ci sono altri fattori coinvolti. Ad esempio, bind mounts. (Come mount --bind /raid0/var-cache /var/cache

Utilizzando find -samefile è una buona scommessa. Che metterà a confronto filesystem e il numero di inode.

-samefile è un'estensione GNU. Anche su Linux, busybox trovare probabilmente non avrà. GNU userspace e kernel Linux vanno spesso insieme, ma si può avere sia senza l'altra, e questo questione è aggiunto solo Linux e bash.)

# params: two paths. returns true if they both refer to the same file 
samepath() { 
    # test if find prints anything 
    [[ -s "$(find -L "$1" -samefile "$2")" ]] # as the last command inside the {}, its exit status is the function return value 
} 

esempio sul mio sistema:

$ find /var/tmp/EXP/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb -samefile /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb 
/var/tmp/EXP/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb 


$ stat {/var/tmp/EXP,/var}/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb 
    File: ‘/var/tmp/EXP/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb’ 
... 
Device: 97ch/2428d  Inode: 2147747863 Links: 1 
... 

    File: ‘/var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb’ 
Device: 97ch/2428d  Inode: 2147747863 Links: 1 

È possibile utilizzare find -L per i casi in cui si desidera seguire i collegamenti simbolici nel componente percorso finale:

$ ln -s /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb foo 
$ find /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb -samefile foo 
$ find -L /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb -samefile foo 
/var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb 

Ovviamente questo funziona per i percorsi che fanno riferimento a directory o di qualsiasi tipo di file , non solo file regolari. Hanno tutti numeri inode.


utilizzo:

$ samepath /var/cache/apt/ /var/tmp/EXP/cache/apt/ && echo true 
true 
$ ln -sf /var/cache/apt foobar 
$ samepath foobar /var/tmp/EXP/cache/apt/ && echo true 
true 
samepath /var/tmp/EXP/cache/apt/ foobar && echo true 
true 
samepath foobar && echo true # doesn't return true when find has an error, since the find output is empty. 
find: `': No such file or directory 

Così find -L dereferenziazioni collegamenti simbolici per -samefile, così come per l'elenco dei percorsi. Quindi entrambi o entrambi possono essere collegamenti simbolici.

+1

+1. Potresti aggiungere qualche informazione sulla portabilità, però? (Non sembra che '-samefile' sia specificato da POSIX?) – ruakh

+0

@ruakh: oh, giusto, è un'estensione GNU, probabilmente trovata solo in GNU' find'. Ovviamente 'bash' non viene fornito con GNU find, ma penso che il tag' linux' mi abbia fatto escludere una menzione di questo. –