ฉันจะ tar ไดเร็กทอรีของไฟล์และโฟลเดอร์โดยไม่รวมไดเร็กทอรีเองได้อย่างไร?


382

ฉันมักจะทำ:

tar -czvf my_directory.tar.gz my_directory

ถ้าฉันต้องการรวมทุกอย่าง (รวมถึงไฟล์ระบบที่ซ่อนอยู่) ใน my_directory แต่ไม่ใช่ไดเรกทอรีเอง ฉันไม่ต้องการ:

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

ฉันต้องการ:

my_file
my_file
my_file

นั่นคือพฤติกรรมเริ่มต้นของการทำtar -czf? ในกรณีของฉันมันเป็นเพียงการจัดเก็บไฟล์และไม่ใช่ไดเรกทอรี เมื่อฉันแค่tarไดเรกทอรีมันรวม แต่มีtar -czfเพียงการเพิ่มไฟล์
Durga Swaroop

คำตอบ:


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

ควรทำงานในบรรทัดเดียว มันทำงานได้ดีสำหรับไฟล์ที่ซ่อนเช่นกัน "*" ไม่ขยายไฟล์ที่ซ่อนอยู่โดยการขยายชื่อพา ธ อย่างน้อยเป็นทุบตี ด้านล่างคือการทดลองของฉัน:

$ 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

2
สิ่งนี้จะทำงานกับไฟล์ที่มีช่องว่างหรืออักขระพิเศษอื่น ๆ เยี่ยมมาก!
PanCrit

29
ไม่สมบูรณ์ - ไฟล์ tar มี '.' และ. /file1 แทนเพียงแค่ file1 ฉันชอบวิธีแก้ปัญหาโดย mateusza ด้านล่างเพื่อใช้ --strip-components เมื่อไม่แสดงตัว
Ivan

23
@ ฉันได้ถ้าคุณแทนที่.ด้วย*ดังนั้นคำสั่งจะเป็นcd my_directory/ && tar -zcvf ../my_dir.tgz * && cd ..แล้วมันจะทำงานตามที่คุณคาดหวัง
ไม่ระบุชื่อ

3
@jmathew คุณยังสามารถใช้ subshell เพื่อไดเรกทอรีการทำงานของเชลล์ปัจจุบันของคุณจะไม่เปลี่ยนแปลง:$ (cd my_directory/ && tar -zcvf ../my_dir.tgz .)
Alec

ฉันรู้ว่ามันเป็นคำตอบเก่า แต่การcdเข้าไปในไดเรกทอรีและออกจะค่อนข้างง่อย สามารถในการใช้งานน้อยpushdและpopdถ้าtarไม่ได้มีธงใด ๆ -Cเช่น
Andris

667

ใช้-Cสวิตช์ของ tar:

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

-C my_directoryบอก tar เพื่อเปลี่ยนไดเรกทอรีปัจจุบันไปmy_directoryแล้ว.หมายความว่า "เพิ่มไดเรกทอรีปัจจุบันทั้งหมด" (รวมถึงไฟล์ที่ซ่อนอยู่และไดเรกทอรีย่อย)

ตรวจสอบให้แน่ใจว่าคุณทำ-C my_directoryก่อนที่จะทำ.หรืออย่างอื่นคุณจะได้รับไฟล์ในไดเรกทอรีปัจจุบัน


5
+1 ขอบคุณ! มันคือคำด่า '.' ฉันหายไป ทำให้รุนแรงขึ้น
JCotton

14
"ซึ่งแตกต่างจากตัวเลือกส่วนใหญ่จะมีการประมวลผล -C ณ จุดที่เกิดขึ้นภายในรายการไฟล์ที่จะประมวลผลพิจารณาคำสั่งต่อไปนี้: tar --create --file=foo.tar -C /etc passwd hosts -C /lib libc.a" apl.jhu.edu/Misc/Unix-info/tar/tar_65.html ฉันลองtar -czvf my_directory.tar.gz * -C my_directoryและ ที่ไม่ทำงาน -Cสถานที่เป็นสิ่งสำคัญ! Damn tar ...
m-ric

57
ไม่สมบูรณ์ - ไฟล์ tar มี '.' และ. /file1 แทนที่จะเป็นเพียง file1 ฉันชอบวิธีแก้ปัญหาโดย mateusza ด้านล่างเพื่อใช้ --strip-components เมื่อไม่แสดงตัว
Ivan

3
@Superole: เชลล์ใช้อักขระแทนก่อนเรียกใช้ tar โปรดทราบด้วยว่าการใช้สัญลักษณ์แทนเช่น*จะไม่รวมไฟล์ที่ซ่อนอยู่ (ซึ่งเป็นข้อกำหนดดั้งเดิม)
dubek

28
มันสร้าง เป็นไดเรกทอรีรากใน. tar.gz
ผู้ไม่ประสงค์ออกนาม

59

คุณยังสามารถสร้างไฟล์เก็บถาวรตามปกติและแตกไฟล์ด้วย:

tar --strip-components 1 -xvf my_directory.tar.gz

3
วิธีแก้ปัญหานี้ดีเป็นพิเศษในสถานการณ์ที่คุณทำงานกับ tarballs ที่สร้างขึ้นก่อนที่ความต้องการทั้งหมดของคุณจะเป็นที่รู้จักกัน ...
Digger

2
ใจว่า--strip-componentsเป็นส่วนขยายของ GNU
zneak

2
คำตอบนี้สามารถปรับปรุงได้โดยการให้ตัวอย่าง "ตามปกติ" ในบริบท
Josh Habdas

33

ลองดูที่--transform/ --xformมันเปิดโอกาสให้คุณนวดชื่อไฟล์เมื่อไฟล์ถูกเพิ่มลงในไฟล์เก็บถาวร:

% 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

การแสดงออกของการแปลงนั้นคล้ายกับของsedและเราสามารถใช้ตัวคั่นอื่นที่ไม่ใช่/( ,ในตัวอย่างด้านบน)
https://www.gnu.org/software/tar/manual/html_section/tar_52.html


3
ฉันจะทำสิ่งนี้ สิ่งอื่นใดก็แค่แฮ็ค!
jwg

2
นี่เป็นทางออกที่ดีกว่ามาก
leesei

นี่คือทางออกที่ดีที่สุด
justhalf

1
ทางออกที่ดี file list too longแต่มันอาจจะก่อให้เกิด โซลูชันของฉันป้องกันสิ่งนั้นและมีความยืดหยุ่นมากขึ้นเช่นกัน
aross

นี่เป็นทางออกที่ดี คุณยังสามารถผ่าน--xformหลาย ๆ ครั้งสำหรับหลายเส้นทาง
Elig

26

TL; DR

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

ด้วยเงื่อนไขบางประการ (เก็บถาวรเฉพาะไฟล์ dirs และ symlink):

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

คำอธิบาย

น่าเสียดายที่ด้านล่างมีไดเรกทอรีหลัก./ในที่เก็บถาวร:

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

คุณสามารถย้ายไฟล์ทั้งหมดออกจากไดเรกทอรีนั้นโดยใช้--transformตัวเลือกการกำหนดค่า แต่ไม่ได้กำจัด.ไดเรกทอรีเอง มันยากที่จะเชื่อคำสั่งมากขึ้นเรื่อย ๆ

คุณสามารถใช้$(find ...)เพื่อเพิ่มรายชื่อไฟล์ลงในคำสั่ง (เช่นในคำตอบแมกนัส ) แต่นั่นอาจทำให้เกิดข้อผิดพลาด "รายการไฟล์ยาวเกินไป" วิธีที่ดีที่สุดคือการรวมเข้ากับ-Tตัวเลือกของ tar เช่นนี้

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

โดยพื้นฐานแล้วสิ่งที่มันทำคือรายการไฟล์ทั้งหมด ( -type f), ลิงค์ ( -type l) และไดเรกทอรีย่อย ( -type d) ภายใต้ไดเรกทอรีของคุณสร้างชื่อไฟล์ทั้งหมดที่เกี่ยวข้องโดยใช้-printf "%P\n"แล้วส่งผ่านไปยังคำสั่ง tar (ใช้ชื่อไฟล์จาก STDIN โดยใช้-T -) -Cตัวเลือกที่เป็นสิ่งจำเป็นเพื่อให้ tar รู้ว่าไฟล์ที่มีชื่อญาติตั้งอยู่ การ--no-recursionตั้งค่าสถานะคือเพื่อที่ tar จะไม่ถูกเรียกคืนไปยังโฟลเดอร์ที่ถูกบอกให้เก็บถาวร (ทำให้ไฟล์ซ้ำกัน)

หากคุณต้องการทำสิ่งพิเศษกับชื่อไฟล์ (การกรอง, การเชื่อมโยงตามปกติเป็นต้น), findคำสั่งมีประสิทธิภาพมากและคุณสามารถทดสอบได้โดยเพียงแค่ลบtarส่วนของคำสั่งด้านบน:

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

ตัวอย่างเช่นหากคุณต้องการกรองไฟล์ PDF ให้เพิ่ม ! -name '*.pdf'

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

ไม่ใช่ GNU

คำสั่งใช้printf(มีใน GNU find) ซึ่งบอกfindให้พิมพ์ผลลัพธ์ด้วยพา ธ สัมพัทธ์ อย่างไรก็ตามหากคุณไม่มี GNU findสิ่งนี้จะทำให้เส้นทางสัมพันธ์ (ลบผู้ปกครองด้วย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 -

2
คำตอบที่ดี บรรจงมากและที่สำคัญที่สุดแก้ปัญหาได้อย่างสมบูรณ์แบบ
Alex

16

คำตอบนี้ควรทำงานในสถานการณ์ส่วนใหญ่ แจ้งให้ทราบล่วงหน้า แต่วิธีการที่ชื่อไฟล์จะถูกเก็บไว้ในไฟล์ tar เป็นตัวอย่างเช่นแทนที่จะเป็นเพียง./file1 file1ผมพบว่าปัญหาที่เกิดขึ้นเมื่อใช้วิธีนี้ในการจัดการกับ tarball ที่ใช้เป็นไฟล์แพคเกจในBuildRoot

ทางออกหนึ่งคือการใช้ Bash globs เพื่อแสดงรายการไฟล์ทั้งหมดยกเว้น..สิ่งนี้:

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

นี่เป็นเคล็ดลับที่ฉันเรียนรู้จากคำตอบนี้

ตอนนี้ tar จะส่งคืนข้อผิดพลาดหากไม่มีไฟล์ที่ตรงกัน ..?*หรือ.[^.]*แต่จะยังคงใช้งานได้ หากข้อผิดพลาดเป็นปัญหา (คุณกำลังตรวจสอบความสำเร็จในสคริปต์) สิ่งนี้จะได้ผล:

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

แม้ว่าตอนนี้เรากำลังยุ่งกับตัวเลือกของเชลล์เราอาจตัดสินใจว่ามันเป็นสิ่งที่มีให้ *จับคู่ไฟล์ที่ซ่อนอยู่:

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

สิ่งนี้อาจไม่ทำงานที่เชลล์ของคุณมองดู*ในไดเรกทอรีปัจจุบันดังนั้นให้ใช้:

shopt -s dotglob
cd my_dir
tar -zcvf ../my_dir.tar.gz *
cd ..
shopt -u dotglob

1
ฉันพบข้อผิดพลาดแปลก ๆ เมื่อฉันทำสิ่งtar: start.sh: Cannot stat: No such file or directoryนี้เกิดขึ้นกับไฟล์ทั้งหมดในไดเรกทอรีปัจจุบันของฉัน! ฉันจะหลีกเลี่ยงสิ่งนี้ได้อย่างไร
BrainStone

@BrainStone ฉันได้รับผลลัพธ์เดียวกันทั้งหมด
mbmast

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

2
Hal ถามอย่างชัดเจนเกี่ยวกับไฟล์ที่ซ่อนอยู่ คุณยังต้องการ ?? *
PanCrit

2
-1: สิ่งนี้จะไม่เพิ่มไฟล์ที่ซ่อนอยู่ใน tar ดูคำตอบของ tbman
dubek

3

หากเป็นระบบ Unix / Linux และคุณใส่ใจกับไฟล์ที่ซ่อนอยู่ (ซึ่งพลาดโดย *) คุณต้องทำ:

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

ฉันไม่รู้ว่าไฟล์ที่ซ่อนอยู่มีลักษณะอย่างไรใน Windows


2

ฉันจะเสนอฟังก์ชั่น Bash ต่อไปนี้ (อาร์กิวเมนต์แรกคือเส้นทางไปยัง dir, อาร์กิวเมนต์ที่สองคือพื้นฐานของการเก็บถาวรที่เกิดขึ้น):

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"
}

คุณสามารถเรียกใช้ด้วยวิธีนี้:

$ tar_dir_contents /path/to/some/dir my_archive

และมันจะสร้างไฟล์เก็บถาวรmy_archive.tar.gzภายในไดเรกทอรีปัจจุบัน มันทำงานร่วมกับองค์ประกอบที่ซ่อนอยู่ (. *) และองค์ประกอบที่มีช่องว่างในชื่อไฟล์


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

อันนี้ใช้ได้สำหรับฉันและมันรวมไฟล์ที่ซ่อนไว้ทั้งหมดโดยไม่ต้องวางไฟล์ทั้งหมดในไดเรกทอรีรากชื่อ "." เหมือนคำตอบของ tomoe :


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

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

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

ตัวอย่าง:

tar.create folder

ยินดี.


1

นี่คือสิ่งที่ได้ผลสำหรับฉัน

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

คุณสามารถใช้

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

ในคำสั่งแรกพบส่งกลับรายการของไฟล์และไดเรกทอรีย่อยของmy_dir อย่างไรก็ตามไดเรกทอรีmy_dirนั้นรวมอยู่ในรายการว่า '.' -printfพารามิเตอร์เอาเส้นทางแบบเต็มรวมทั้ง '' และทั้งหมด อย่างไรก็ตาม ในสตริงรูปแบบ'% P'ของprintfจะมีเศษเหลืออยู่ในรายการไฟล์และไดเรกทอรีย่อยของmy_dirและสามารถมองเห็นได้ด้วยช่องว่างชั้นนำในผลลัพธ์ของคำสั่งfind

นั่นจะไม่เป็นปัญหาสำหรับ TAR แต่ถ้าคุณต้องการแก้ไขให้เพิ่ม-mindepth 1เช่นเดียวกับในคำสั่งที่สอง


0

ใช้ pax

Pax เป็นแพ็คเกจที่เลิกใช้แล้ว แต่ทำงานได้อย่างสมบูรณ์แบบและเรียบง่าย

pax -w > mydir.tar mydir

ใช้งานได้จริงและทำงาน +1
Breno Salgado

คำสั่งนี้สร้าง mydir.tar ด้วยเนื้อหา: mydir / file1 mydir / file2 สิ่งที่ควรหลีกเลี่ยง
อเล็กซ์

0

วิธีที่ง่ายที่สุดที่ฉันพบ:

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


1
ที่จะไม่รวมไฟล์ที่ซ่อนอยู่
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=-
}

จัดการชื่อไฟล์อย่างปลอดภัยด้วยช่องว่างหรือตัวอักษรผิดปกติอื่น ๆ คุณสามารถเลือกที่จะเพิ่ม-name '*.sql'ตัวกรองที่คล้ายกันลงในคำสั่ง find เพื่อ จำกัด ไฟล์ที่รวมอยู่


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

หากโฟลเดอร์คือ mytemp หากคุณใช้ข้างต้นไฟล์นั้นจะซิปและลบไฟล์ทั้งหมดในโฟลเดอร์ แต่ทิ้งไว้ตามลำพัง

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

คุณสามารถกำหนดรูปแบบที่แยกได้และระบุไม่ให้มองเข้าไปในโฟลเดอร์ย่อยด้วย


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.