¿Cómo puedo tar un directorio de archivos y carpetas sin incluir el directorio en sí?

382

Yo típicamente hago:

tar -czvf my_directory.tar.gz my_directory

¿Qué sucede si solo quiero incluir todo (incluidos los archivos ocultos del sistema) en my_directory, pero no el directorio en sí? No quiero

my_directory
   --- my_file
   --- my_file
   --- my_file

Quiero:

my_file
my_file
my_file
l'L'l
fuente
¿Es ese el comportamiento predeterminado de hacer tar -czf? En mi caso, solo está almacenando los archivos y no el directorio. Cuando acabo de tarincluir el directorio, lo incluye pero tar -czfsolo agrega los archivos.
Durga Swaroop

Respuestas:

233
cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd - 

debería hacer el trabajo en una línea. Funciona bien para archivos ocultos también. "*" no expande los archivos ocultos por expansión de nombre de ruta al menos en bash. A continuación se muestra mi experimento:

$ mkdir my_directory
$ touch my_directory/file1
$ touch my_directory/file2
$ touch my_directory/.hiddenfile1
$ touch my_directory/.hiddenfile2
$ cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd ..
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2
$ tar ztf my_dir.tgz
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2
tomoe
fuente
2
Esto también funcionará en archivos con espacios u otros caracteres especiales. ¡Buen trabajo!
PanCrit
29
No perfecto: el archivo tar contiene '.' y también ./file1 en lugar de solo file1. Me gusta la solución de mateusza a continuación para usar --strip-components cuando desatranco.
Ivan
23
@Ivan si lo reemplaza .con *el comando, cd my_directory/ && tar -zcvf ../my_dir.tgz * && cd ..entonces funcionará como esperaba.
Anónimo el
3
@jmathew También puede usar una subshell para que el directorio de trabajo de su shell actual no cambie:$ (cd my_directory/ && tar -zcvf ../my_dir.tgz .)
Alec
Sé que es una respuesta antigua, pero cdentrar en directorios y salir es bastante aburrido. Al menos podría usar pushdy popdsi tarno tuviera ninguna bandera como -C.
Andris
668

Usa el -Cinterruptor de alquitrán:

tar -czvf my_directory.tar.gz -C my_directory .

El -C my_directoryle dice a tar que cambie el directorio actual a my_directory, y luego .significa "agregar todo el directorio actual" (incluidos los archivos y subdirectorios ocultos).

Asegúrese de hacerlo -C my_directoryantes de hacerlo .o de lo contrario obtendrá los archivos en el directorio actual.

dubek
fuente
55
+1 gracias! Fue el maldito '.' Estaba perdido. tan irritante
JCotton
14
"A diferencia de la mayoría de las opciones, -C se procesa en el punto en que ocurre dentro de la lista de archivos a procesar. Considere el siguiente comando: tar --create --file=foo.tar -C /etc passwd hosts -C /lib libc.a" apl.jhu.edu/Misc/Unix-info/tar/tar_65.html Siempre intento tar -czvf my_directory.tar.gz * -C my_directoryy eso no funciona. -Cla ubicación es importante! Maldita alquitrán ...
m-ric
57
No perfecto: el archivo tar contiene '.' y también ./file1 en lugar de solo file1. Me gusta la solución de mateusza a continuación para usar --strip-components cuando desatranco.
Ivan
3
@Superole: el shell sustituye a los comodines antes de ejecutar tar. También tenga en cuenta que el uso de un comodín como *no incluirá archivos ocultos (que era el requisito original).
dubek
28
Crea . como directorio raíz en .tar.gz.
Anónimo
59

También puede crear un archivo como de costumbre y extraerlo con:

tar --strip-components 1 -xvf my_directory.tar.gz
mateusza
fuente
3
Esta solución es especialmente buena en situaciones en las que está trabajando con tarballs creados antes de que se conocieran todos sus requisitos ...
Digger
2
Tenga en cuenta que --strip-componentses una extensión GNU.
zneak
2
Esta respuesta puede mejorarse proporcionando el ejemplo "como de costumbre" en contexto.
Josh Habdas
33

Echa un vistazo a --transform/ --xform, te da la oportunidad de masajear el nombre del archivo a medida que el archivo se agrega al archivo:

% mkdir my_directory
% touch my_directory/file1
% touch my_directory/file2
% touch my_directory/.hiddenfile1
% touch my_directory/.hiddenfile2
% tar -v -c -f my_dir.tgz --xform='s,my_directory/,,' $(find my_directory -type f)
my_directory/file2
my_directory/.hiddenfile1
my_directory/.hiddenfile2
my_directory/file1
% tar -t -f my_dir.tgz 
file2
.hiddenfile1
.hiddenfile2
file1

La expresión de transformación es similar a la de sed, y podemos usar separadores distintos de /( ,en el ejemplo anterior).
https://www.gnu.org/software/tar/manual/html_section/tar_52.html

Magnus
fuente
3
Yo haría esto ¡Cualquier otra cosa es solo un truco!
jwg
2
Esta es una solución mucho mejor.
leesei
Esta es la mejor solución.
justhalf
1
Buena solución, pero podría causar file list too long. Mi solución lo impide y también es más flexible.
Aross
Esta es una gran solución. También puede pasar --xformvarias veces por múltiples caminos.
eleg
26

TL; DR

find /my/dir/ -printf "%P\n" | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

Con algunas condiciones (archivar solo archivos, directorios y enlaces simbólicos):

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

Explicación

Lamentablemente, a continuación se incluye un directorio principal ./en el archivo:

tar -czf mydir.tgz -C /my/dir .

Puede mover todos los archivos fuera de ese directorio utilizando la --transformopción de configuración, pero eso no elimina el .directorio en sí. Se vuelve cada vez más difícil domar el comando.

Puede usar $(find ...)para agregar una lista de archivos al comando (como en la respuesta de magnus ), pero eso puede causar un error de "lista de archivos demasiado larga". La mejor manera es combinarlo con la -Topción tar , de esta manera:

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

Básicamente, lo que hace es enumerar todos los archivos ( -type f), enlaces ( -type l) y subdirectorios ( -type d) en su directorio, hacer que todos los nombres de archivo sean relativos -printf "%P\n"y luego pasar eso al comando tar (toma los nombres de archivo de STDIN usando -T -). La -Copción es necesaria para que tar sepa dónde se encuentran los archivos con nombres relativos. El --no-recursionindicador es para que el alquitrán no vuelva a aparecer en las carpetas y se le ordena archivar (lo que provoca archivos duplicados).

Si necesita hacer algo especial con los nombres de archivo (filtrado, seguimiento de enlaces simbólicos, etc.), el findcomando es bastante poderoso, y puede probarlo simplemente eliminando la tarparte del comando anterior:

$ find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d
> textfile.txt
> documentation.pdf
> subfolder2
> subfolder
> subfolder/.gitignore

Por ejemplo, si desea filtrar archivos PDF, agregue ! -name '*.pdf'

$ find /my/dir/ -printf "%P\n" -type f ! -name '*.pdf' -o -type l -o -type d
> textfile.txt
> subfolder2
> subfolder
> subfolder/.gitignore

Búsqueda no GNU

El comando usa printf(disponible en GNU find) que le indica findque imprima sus resultados con rutas relativas. Sin embargo, si no tiene GNU find, esto funciona para que las rutas sean relativas (elimina a los padres con sed):

find /my/dir/ -type f -o -type l -o -type d | sed s,^/my/dir/,, | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -
aross
fuente
2
Gran respuesta. Muy elaborado, y lo más importante, resuelve el problema perfectamente.
Alex
16

Esta respuesta debería funcionar en la mayoría de las situaciones. Sin embargo, observe cómo los nombres de archivo se almacenan en el archivo tar como, por ejemplo, en ./file1lugar de solo file1. Descubrí que esto causaba problemas al usar este método para manipular tarballs utilizados como archivos de paquete en BuildRoot .

Una solución es usar algunos globos de Bash para enumerar todos los archivos, excepto los ..siguientes:

tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *

Este es un truco que aprendí de esta respuesta .

Ahora tar devolverá un error si no hay archivos que coincidan ..?*o.[^.]* , pero seguirá funcionando. Si el error es un problema (está comprobando el éxito en un script), esto funciona:

shopt -s nullglob
tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *
shopt -u nullglob

Aunque ahora estamos jugando con las opciones de shell, podríamos decidir que es mejor tener *archivos ocultos coincidentes:

shopt -s dotglob
tar -C my_dir -zcvf my_dir.tar.gz *
shopt -u dotglob

Esto podría no funcionar donde los globos de shell *en el directorio actual, así que, alternativamente, use:

shopt -s dotglob
cd my_dir
tar -zcvf ../my_dir.tar.gz *
cd ..
shopt -u dotglob
Rob Fisher
fuente
1
Recibo errores extraños cuando hago esto. ¡ tar: start.sh: Cannot stat: No such file or directoryEsto sucede con todos los archivos de mi directorio actual! ¿Cómo evito esto?
BrainStone
@BrainStone obtengo exactamente los mismos resultados.
mbmast
15
cd my_directory
tar zcvf ../my_directory.tar.gz *
mateusza
fuente
2
Hal preguntó explícitamente sobre los archivos ocultos. También necesitas .??*.
PanCrit
2
-1: Esto no agrega los archivos ocultos al tar. Ver la respuesta de tbman.
dubek
3

Si se trata de un sistema Unix / Linux y le interesan los archivos ocultos (que serán extrañados por *), debe hacer lo siguiente:

cd my_directory
tar zcvf ../my_directory.tar.gz * .??*

No sé cómo se ven los archivos ocultos en Windows.

PanCrit
fuente
2

Propondría la siguiente función Bash (el primer argumento es la ruta al directorio, el segundo argumento es el nombre base del archivo resultante):

function tar_dir_contents ()
{
    local DIRPATH="$1"
    local TARARCH="$2.tar.gz"
    local ORGIFS="$IFS"
    IFS=$'\n'
    tar -C "$DIRPATH" -czf "$TARARCH" $( ls -a "$DIRPATH" | grep -v '\(^\.$\)\|\(^\.\.$\)' )
    IFS="$ORGIFS"
}

Puedes ejecutarlo de esta manera:

$ tar_dir_contents /path/to/some/dir my_archive

y generará el archivo my_archive.tar.gzdentro del directorio actual. Funciona con elementos ocultos (. *) Y con elementos con espacios en su nombre de archivo.

gpz500
fuente
2
cd my_directory && tar -czvf ../my_directory.tar.gz $(ls -A) && cd ..

Este funcionó para mí e incluye todos los archivos ocultos sin poner todos los archivos en un directorio raíz llamado "." como en la respuesta de tomoe :

medicina
fuente
1
tar.create() {
        local folder="${1}"

        local tar="$(basename "${folder}")".tar.gz

        cd "${folder}" && tar -zcvf "../${tar}" .; cd - &> /dev/null
}

Ejemplo:

tar.create folder

De nada.

mmm
fuente
1

Esto es lo que me funciona.

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -maxdepth 1 -printf '%P ')

También podrías usar

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -mindepth 1 -maxdepth 1 -printf '%P ')

En el primer comando, find devuelve una lista de archivos y subdirectorios de my_dir . Sin embargo, el directorio my_dir está incluido en esa lista como '.' El -printf parámetro elimina la ruta completa, incluido ese '.' y también todos Sin embargo, el en la cadena de formato '% P' de printf deja un remanente en la lista de archivos y subdirectorios de my_dir y puede verse por un espacio inicial en el resultado del comando find .

Eso no será un problema para TAR, pero si desea solucionarlo, agregue -mindepth 1 como en el segundo comando.

serendrewpity
fuente
0

Usa pax.

Pax es un paquete obsoleto pero hace el trabajo perfectamente y de manera simple.

pax -w > mydir.tar mydir
rjv
fuente
Más práctico y hace el trabajo +1
Breno Salgado
Este comando crea mydir.tar con contenido: mydir / file1 mydir / file2, exactamente lo que se debe evitar.
Alex
0

La forma más simple que encontré:

cd my_dir && tar -czvf ../my_dir.tar.gz *

alexgo
fuente
1
Eso no incluirá archivos ocultos.
asynts
0
# tar all files within and deeper in a given directory
# with no prefixes ( neither <directory>/ nor ./ )
# parameters: <source directory> <target archive file>
function tar_all_in_dir {
    { cd "$1" && find -type f -print0; } \
    | cut --zero-terminated --characters=3- \
    | tar --create --file="$2" --directory="$1" --null --files-from=-
}

Maneja de forma segura los nombres de archivos con espacios u otros caracteres inusuales. Opcionalmente, puede agregar un -name '*.sql'filtro similar al comando de búsqueda para limitar los archivos incluidos.

marcingo
fuente
-1
 tar -cvzf  tarlearn.tar.gz --remove-files mytemp/*

Si la carpeta es mytemp, si aplica lo anterior, comprimirá y eliminará todos los archivos de la carpeta, pero lo dejará solo

 tar -cvzf  tarlearn.tar.gz --remove-files --exclude='*12_2008*' --no-recursion mytemp/*

Puede dar patrones de exclusión y también especificar no buscar en subcarpetas.

usuario1456599
fuente
-3
tar -C my_dir -zcvf my_dir.tar.gz `ls my_dir`
mateusza
fuente