bash - toxico - hulla usos
Interesante uso del alquitrán... ¿pero qué está pasando? (12)
Vi el siguiente uso interesante de alquitrán en los guiones Bash de un compañero de trabajo:
`tar cf - * | (cd <dest> ; tar xf - )`
Aparentemente funciona muy parecido a rsync -av, pero más rápido. Surge la pregunta, ¿cómo?
-metro
EDITAR : ¿Alguien puede explicar por qué esta solución es preferible a la siguiente?
cp -rfp * dest
¿Es el primero más rápido?
Algunas versiones antiguas de cp no tenían opciones -f / -p (y similares) para preservar los permisos, por lo que este truco de alquitrán hizo el trabajo.
Escribe el archivo en salida estándar, luego lo canaliza a un subproceso - envuelto entre paréntesis - que cambia a un directorio diferente y lee / extrae de la entrada estándar. Eso es lo que significa el personaje de tablero después del argumento f
. Básicamente se trata de copiar todos los archivos visibles y subdirectorios del directorio actual a otro directorio.
Este es un uso único de tuberías. Básicamente, la primera tar normalmente escribe directamente en un archivo, pero en su lugar va a escribir en stdout (the -), que luego se redirige a la otra tar que toma stdin en lugar de un archivo. Básicamente, esto es lo mismo que tarring a un archivo y desmarque después, excepto sin el archivo intermedio.
La solución de alquitrán conservará los enlaces simbólicos, mientras que cp solo hará copias y destruirá los enlaces.
tar ha sido una utilidad estándar de Unix mucho más larga que rsync. Es más probable que lo encuentre en una situación en la que una jerarquía de directorios deba copiarse a otra ubicación (incluso a otra computadora). rsync es probablemente más fácil de usar en estos días, pero es más lento porque compara tanto el origen como los destinos y los sincroniza. alquitrán solo copia en una dirección.
Sobre la diferencia entre cp y tar para copiar las jerarquías de directorios, se puede realizar un experimento simple para mostrar la diferencia:
alastair box:~/hack/cptest [1134]% mkdir src
alastair box:~/hack/cptest [1135]% cd src
alastair box:~/hack/cptest/src [1136]% touch foo
alastair box:~/hack/cptest/src [1137]% ln -s foo foo-s
alastair box:~/hack/cptest/src [1138]% ln foo foo-h
alastair box:~/hack/cptest/src [1139]% ls -a
total 0
-rw-r--r-- 2 alastair alastair 0 Nov 25 14:59 foo
-rw-r--r-- 2 alastair alastair 0 Nov 25 14:59 foo-h
lrwxrwxrwx 1 alastair alastair 3 Nov 25 14:59 foo-s -> foo
alastair box:~/hack/cptest/src [1142]% mkdir ../cpdest
alastair box:~/hack/cptest/src [1143]% cp -rfp * ../cpdest
alastair box:~/hack/cptest/src [1144]% mkdir ../tardest
alastair box:~/hack/cptest/src [1145]% tar cf - * | (cd ../tardest ; tar xf - )
alastair box:~/hack/cptest/src [1146]% cd ..
alastair box:~/hack/cptest [1147]% ls -l cpdest
total 0
-rw-r--r-- 1 alastair alastair 0 Nov 25 14:59 foo
-rw-r--r-- 1 alastair alastair 0 Nov 25 14:59 foo-h
lrwxrwxrwx 1 alastair alastair 3 Nov 25 15:00 foo-s -> foo
alastair box:~/hack/cptest [1148]% ls -l tardest
total 0
-rw-r--r-- 2 alastair alastair 0 Nov 25 14:59 foo
-rw-r--r-- 2 alastair alastair 0 Nov 25 14:59 foo-h
lrwxrwxrwx 1 alastair alastair 3 Nov 25 15:00 foo-s -> foo
La diferencia está en los archivos con enlaces duros. Observe cómo los archivos vinculados se copian individualmente con cp
y junto con tar
. Para hacer la diferencia más obvia, eche un vistazo a los inodos para cada uno:
alastair box:~/hack/cptest [1149]% ls -i cpdest
24690722 foo 24690723 foo-h 24690724 foo-s
alastair box:~/hack/cptest [1150]% ls -i tardest
24690801 foo 24690801 foo-h 24690802 foo-s
Probablemente haya otras razones para preferir el alquitrán, pero este es uno grande, al menos si tiene archivos extensamente vinculados.
tar cf - *
Esto usa tar para enviar * a stdout
|
Esto hace que la redirección obvia de stdout a ...
(cd <dest> ; tar xf - )
Esto, que cambia PWD a la ubicación adecuada y luego extrae de stdin
No sé por qué esto sería más rápido que rsync, ya que no hay compresión involucrada.
tar cf - * | (cd <dest> ; tar xf - )
va a atacar todos los archivos / directorios no ocultos del directorio actual a stdout, y luego conectarlos a un nuevo stdin de subcapas. Ese shell primero cambia el directorio de trabajo actual a <dest>
y luego lo anota en ese directorio.
Si tiene GNU cp
(que todos los sistemas basados en Linux), el cp --archive
funcionará, incluso en archivos con enlaces duros, y tar no es necesario.
Como sucede, un compañero de trabajo escribió un comando casi idéntico en uno de nuestros scripts. Después de pasar algún tiempo desconcertado, le pregunté por qué lo había usado en lugar de cp
. Su respuesta, según recuerdo, fue que cp
es lento al hacer una copia de un sistema de archivos a otro.
Si esto es cierto o no, requeriría más pruebas de las que me gustaría gastar en la pregunta, pero tiene cierto sentido. El primer proceso de tar
se lee desde el dispositivo de origen lo más rápido posible y solo espera que el dispositivo lo lea. Mientras tanto, el segundo proceso tar
lee de su tubería de entrada y escribe lo más rápido posible. Puede que tenga que esperar la entrada, pero si las escrituras en el dispositivo de destino son más lentas que las lecturas en el dispositivo de origen, solo esperará en el dispositivo de destino. Un único comando cp
tendrá que esperar tanto en el dispositivo de origen como en el de destino.
Por otro lado, los sistemas operativos modernos hacen un buen trabajo al precaching de operaciones de IO. Es muy posible que cp
pase la mayor parte del tiempo esperando las escrituras y obteniendo lecturas de la memoria en lugar del dispositivo en sí. Parece que uno necesitaría datos realmente sólidos para elegir utilizando dos comandos tar
lugar del comando cp
más sencillo.
Para un directorio con 25,000 archivos vacíos:
$ time { tar -cf - * | (cd ../bar; tar -xf - ); } real 0m4.209s user 0m0.724s sys 0m3.380s $ time { cp * ../baz/; } real 0m18.727s user 0m0.644s sys 0m7.127s
Para un directorio con 4 archivos de 1073741824 bytes (1GB) cada uno
$ time { tar -cf - * | (cd ../bar; tar -xf - ); } real 3m44.007s user 0m3.390s sys 0m25.644s $ time { cp * ../baz/; } real 3m11.197s user 0m0.023s sys 0m9.576s
Creo que este fenómeno depende mucho del sistema de archivos. Si estoy en lo cierto, verá una diferencia drástica entre un sistema de archivos que se especializa en numerosos archivos pequeños, como reiserfs 3.6, y un sistema de archivos que es mejor para manejar archivos de gran tamaño.
(Ejecuté las pruebas anteriores en HFS +).
Creo que el alquitrán hará una operación de "fusión" de estilo de Windows con directorios profundamente anidados, mientras que el cp sobrescribirá los subdirectorios.
Por ejemplo, si tienes el diseño:
dir/subdir/file1
y lo copias a un destino que contiene:
dir/subdir/file2
Luego, con copia, se quedará con:
dir/subdir/file1
Pero con el comando tar, su destino contendrá:
dir/subdir/file1
dir/subdir/file2
El libro de PowerTools tiene la copia como:
tar cf - * | (cd <dest> && tar xvBf - )
El ''&&'' es un condicional que verifica el código de retorno del comando anterior. Es decir, si el "cd" falla, el "tar xf -" no se ejecutará. Siempre arrojo un -v (verboso) y un -B (reblock input).
Uso alquitrán todo el tiempo. Es especialmente útil para copiar en un sistema remoto, como por ejemplo:
tar cvf -. | ssh alguien @ algunamaquina ''(cd en algún lugar && tar xBf -)''