เหตุใดจึงไม่ใช้การวนซ้ำ


3

นี่เป็นบน Ubuntu 12.04 ฉันพยายามหาวิธีรับ ffmpeg เพื่อทำการแปลงแบบ FLAC เป็น MP3 แบบเรียกซ้ำ ถ้าฉันcdเป็นไดเรกทอรีและใช้

for f in *.flac; do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"; done

ที่ใช้งานได้ดีอย่างสมบูรณ์แบบ อย่างไรก็ตามเมื่อฉันลองสิ่งนี้มันไม่ทำงาน:

for f in "$(find . -type f -name *.flac)"; do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"; done

มันไม่ได้ทำให้เกิดข้อผิดพลาดที่เป็นประโยชน์ (แต่นี่คือผลลัพธ์ต่อไปไม่จำเป็นต้องบ่น):

evilsoup@enchantment:~/Music/Jean Sibelius$ for f in "$(find . -type f -name *.flac)"; do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"; done
ffmpeg version git-2012-12-18-b7e085a Copyright (c) 2000-2012 the FFmpeg developers
  built on Dec 18 2012 19:23:11 with gcc 4.6 (Ubuntu/Linaro 4.6.3-1ubuntu5)
  configuration: --enable-gpl --enable-libfaac --enable-libfdk-aac --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-librtmp --enable-libtheora --enable-libvorbis --enable-libvpx --enable-x11grab --enable-libx264 --enable-nonfree --enable-version3
  libavutil      52. 12.100 / 52. 12.100
  libavcodec     54. 80.100 / 54. 80.100
  libavformat    54. 49.102 / 54. 49.102
  libavdevice    54.  3.102 / 54.  3.102
  libavfilter     3. 28.100 /  3. 28.100
  libswscale      2.  1.103 /  2.  1.103
  libswresample   0. 17.102 /  0. 17.102
  libpostproc    52.  2.100 / 52.  2.100
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/02. Symphony No.1.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/03. Symphony No.1.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/stripped2.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/05. Symphony No.1.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/stripped3.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/09. Andante festivo.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/08. Symphony No.3.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/01. Finlandia.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/07. Symphony No.3.flac
./Symphonies 1, 2, 3 & 5

ผมเคยทดสอบfindคำสั่งในตัวเองและทำงานได้ตามที่คาดไว้ดังนั้นปัญหาที่เกิดขึ้นจะต้องมีบางสิ่งบางอย่างจะทำอย่างไรกับการมีปฏิสัมพันธ์ระหว่างและfindfor

ฉันรู้ว่าฉันจะทำอะไรกับfind's -execตัวเลือก แต่ฉันไม่สามารถหาวิธีที่จะทำสตริงทดแทนเท่าที่จะทำได้ด้วยการทุบตีใด ๆforห่วงและผมไม่ต้องการมีพวงของfile.flac.mp3ที่จะจัดการกับแม้ renameถ้าพวกเขาสามารถได้รับการแก้ไขด้วยง่าย


1
ฉันสงสัยว่าคุณมีปัญหากับอักขระ SPACE ในชื่อไฟล์ของคุณหรือไม่ ลองเปลี่ยนIFS=$'\n';ก่อนfindลูป
nik

@evilsoup แน่นอนฉันจะลองเปลี่ยนคำพูดเป็นเห็บกลับเช่น:for f in `find . -type f -name *.flac`; do
mnmnc

@mnmnc นี่ไม่เปลี่ยนแปลงอะไรเลย - มันเทียบเท่ากันจริงๆ ในความเป็นจริงรูปแบบเห็บกลับที่คุณเสนอไม่ได้รับการสนับสนุนด้วยเหตุผลของการอ่าน การถอดคำพูดย่อมเป็นประโยชน์นี่ แต่เห็บกลับไม่ได้จริงๆสร้างความแตกต่าง
slhck

1
@evilsoup ถ้าเป็น @slhck บอกว่าสิ่งนี้จะไม่เปลี่ยนแปลงอะไรให้คุณแล้วเปลี่ยนส่วนหลังจากที่doไปecho $f; doneและดูว่าforส่วนที่เป็นแม้จะผ่านข้อโต้แย้งไปยังร่างกายของลูป
mnmnc

@slhck แต่น่าเสียดายที่ฉันมีชื่อไฟล์ที่มีช่องว่างในนั้นและลบเครื่องหมายคำพูดรอบ ๆ$()ทำให้การforแยกเหล่านั้นออกเป็นไฟล์แยกที่ช่องว่าง
evilsoup

คำตอบ:


3

เครื่องหมายอัญประกาศคู่รอบ$(find ...)คำสั่งทำให้เห็นผลลัพธ์ของ find เป็นชื่อไฟล์เดียวซึ่งเห็นได้ชัดว่าไม่ใช่สิ่งที่คุณต้องการ

มีหลายวิธีในการบรรลุสิ่งที่คุณต้องการ:

  • ทำให้ find execute execute ffmpeg โดยตรง (ไม่มี piping, no loops):

    find . -type f -name *.flac -exec bash -c '
       ffmpeg -i "$0" -c:a libmp3lame -q:a 2 "${0/%flac/mp3}"
    ' {} \;
  • ใช้find ... -print0 | xargs -0 ...แทนสำหรับซึ่งทำขึ้นเป็นพิเศษสำหรับวัตถุประสงค์เหล่านี้:

    find . -type f -name *.flac -print0 | xargs -0 -I {} bash -c '
        ffmpeg -i "$0" -c:a libmp3lame -q:a 2 "${0/%flac/mp3}"
    ' {}
  • เปลี่ยนIFSเป็น newline เท่านั้นใช้คำสั่งเดียวกับก่อนหน้านี้ลบเครื่องหมายคำพูดคู่:

    IFS=$'\n'
    for f in $(find . -type f -name *.flac); do
        ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"
    done
    unset IFS

    คำสั่งunset IFSเปลี่ยน IFS กลับไปเป็นค่าเริ่มต้น (ไม่จำเป็นในเชลล์สคริปต์ยกเว้นว่าจะรบกวนคำสั่งที่ตามมา)


โปรดทราบว่าคุณยังต้องเปลี่ยนชื่อไฟล์ (ที่ OP ทำการทดแทน)
slhck

@shlck: ใช่ฉันไม่ได้สังเกตว่า นั่นทำให้สองตัวเลือกหลังซับซ้อนกว่าเล็กน้อย
Dennis

โอ้เรียกทุบตีจาก-execนั้นเป็นทางออกที่เรียบร้อยจริงๆขอบคุณ! ฉันมีความปรารถนาอย่างไม่มีเหตุผลที่จะหลีกเลี่ยงท่อให้มากที่สุดเท่าที่จะทำได้ดังนั้นคำตอบที่สามนั้นสมบูรณ์แบบ และฉันเพิ่งทดสอบและใช้งานได้
evilsoup

1
วิธีสุดท้ายของคุณดีที่สุดแน่นอนที่สุด แต่ก็ใช้ดีที่สุดfind . -type f -name '*.flac' -exec bash -c 'ffmpeg -i "$0" -c:a libmp3lame -q:a 2 "${0/%flac/mp3}"' {} \;เพราะจะปลอดภัย 100% สำหรับสัญลักษณ์ตลก ๆ ในชื่อไฟล์
gniourf_gniourf

@gniourf_gniourf: การส่งชื่อไฟล์เป็นอาร์กิวเมนต์เพื่อทุบตีเป็นความคิดที่ดี ฉันจะรวมมันเข้าไปในคำตอบของฉัน
Dennis

3

โปรดอ่านBashFAQ / 020 - เกร็กวิกิพีเดีย ในการวนซ้ำอย่างเหมาะสมกับผลลัพธ์ของต่อfindไปนี้เป็นวิธีที่ควรจัดการกับตัวละครแปลก ๆ ทุกประเภทในชื่อไฟล์ (ช่องว่าง, การขึ้นบรรทัดใหม่, การวนซ้ำ, ฯลฯ )

while IFS= read -r -d '' file; do
   ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"
done < <(find . -type f -name "*.flac" -print0)

อย่าลืมที่จะพูดเพื่อป้องกันการขยายตัวถ้ามีไฟล์ในไดเรกทอรีปัจจุบันที่ลงท้ายด้วย"*.flac".flac


ไม่ควรที่จะเป็นwhile $IFS ...อย่างไร และไม่ควรที่ทุกอย่างอยู่ระหว่างwhileและ; doอยู่ในวงเล็บเหลี่ยม? หรือฉันเข้าใจผิดอย่างสมบูรณ์ว่าคำสั่งควรทำอะไร?
evilsoup

ด้วย $ whileคุณต้องการเขียนทับมันทั่วโลกเพื่อที่ว่าอาจมีผลกระทบที่ไม่ดีและจะไม่ได้ทำงานภายใน ที่เหลือก็ยากที่จะเข้าใจ แต่ไม่มีมันไม่จำเป็นต้องอยู่ในวงเล็บเหลี่ยม - ในความเป็นจริงที่ไม่ทำงาน syntactically
slhck

อ่าฉันคิดว่ามันเป็นtest... ฉันเข้าใจแล้วว่าreadเอาท์พุทของfindแต่ฉันคิดว่าฉันต้องอ่านเพิ่มอีกนิดหน่อยfileและอาจจะเป็นwhileลูปที่จะเข้าใจสิ่งนี้ แต่แล้วฉันก็ชอบที่จะเรียนรู้เกี่ยวกับสิ่งใหม่ ๆ ดังนั้นขอบคุณสำหรับคำตอบ :)
evilsoup

ใช่ทุบตีมีลักษณะของมันเมื่อมันมาถึงการทำสิ่งที่ควร "เพียงแค่ทำงาน" อย่างไรก็ตามคำตอบที่ดีเกี่ยวกับ FFmpeg - ติดตามคุณไปรอบ ๆ ไม่เคยพูดว่ายินดีต้อนรับผู้ใช้ระดับสูง! ติดตามโพสต์ที่ยอดเยี่ยม!
slhck

ขอบคุณ มันเป็นเครื่องมือที่ฉันต้องใช้เป็นอย่างมากในช่วงหลายปีที่ผ่านมาและไม่ได้รับการจัดทำเป็นเอกสารที่ดี (สิ่งที่ฉันจะพัฒนาเพื่อปรับปรุง) โดยเฉพาะอย่างยิ่งสำหรับผู้เริ่มต้น - บางสิ่งที่ทำให้เข้าใจผิด ดังนั้นจึงเป็นหนึ่งในสิ่งที่ไม่ได้มีผู้ใช้ 'ระดับกลาง' จริงๆคุณสามารถคัดลอกและวางข้อมูลจากคู่มือหรือคุณเป็นผู้เชี่ยวชาญ ฮ่า ๆ ฉันไม่ได้ตั้งใจจะเขียนสิ่งนี้มากเมื่อฉันเริ่มเขียน
evilsoup

0

เช่นเดียวกับคำตอบที่ดีมากที่นี่ฉันพบว่า GNU Parallel สามารถทำได้:

find . -type f -name "*.flac" | parallel ffmpeg -i {} -c:a libmp3lame -q:a 2 {.}.mp3

{.}แถบส่วนขยายไฟล์จากชื่อไฟล์ GNU Parallel ใช้การขึ้นบรรทัดใหม่เป็นตัวคั่นตามค่าเริ่มต้นดังนั้นจึงไม่จำเป็นต้องใช้find ... -print0 | parallel -0 ...เว้นเสียแต่ว่าคุณจะคาดหวังชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่

ตามชื่อของมัน GNU Parallel จะเรียกใช้กระบวนการในแบบขนาน - หนึ่งงานต่อซีพียูคอร์โดยค่าเริ่มต้น ดังนั้นมันอาจเร่งความเร็วของสิ่งต่าง ๆ เล็กน้อย ไม่มากในกรณีนี้เนื่องจาก ffmpeg มีหลายเธรดด้วยตัวเอง ... แต่ก็ยัง

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