LINUX.ORG.RU

Как заставить баш адекватно реагировать на пробелы в именах файлов

 ,


0

2

Суть такова. Есть каталог:

ls Music/TheWall/Pink\ Floyd\ -\ 1979\ -\ The\ Wall\ \(24bit-96kHz\)/Disc\ 1/
'01 In the Flesh.flac'                      '05 Another Brick in the Wall, Pt. 2.flac'  '09 Young Lust.flac'                        '13 Goodbye Cruel World.flac'
'02 The Thin Ice.flac'                      '06 Mother.flac'                            '10 One of My Turns.flac'
'03 Another Brick in the Wall, Pt. 1.flac'  '07 Goodbye Blue Sky.flac'                  "11 Don't Leave Me Now.flac"
'04 The Happiest Days of Our Lives.flac'    '08 Empty Spaces.flac'                      '12 Another Brick in the Wall, Pt. 3.flac'

Из него формируется список файлов без расширения.

ls Music/TheWall/Pink\ Floyd\ -\ 1979\ -\ The\ Wall\ \(24bit-96kHz\)/Disc\ 1/ | sed s/.flac/''/g | sed -e s/' '/'\ '/g > TheWall.list

cat TheWall.list 
01 In the Flesh
02 The Thin Ice
03 Another Brick in the Wall, Pt. 1
04 The Happiest Days of Our Lives
05 Another Brick in the Wall, Pt. 2
06 Mother
07 Goodbye Blue Sky
08 Empty Spaces
09 Young Lust
10 One of My Turns
11 Don't Leave Me Now
12 Another Brick in the Wall, Pt. 3
13 Goodbye Cruel World

Команда:

for i in $(cat TheWall.list); do ffmpeg -i Music/TheWall/Pink\ Floyd\ -\ 1979\ -\ The\ Wall\ \(24bit-96kHz\)/Disc\ 1/$i.flac -ab 320k -map_metadata 0 -id3v2_version 3 mp3/The_Wall/$i.mp3; done

Спотыкается об эти пробелы. Если обернуть пути одинарными кавычками — он перестает воспринимать $i переменную. Как быть?

★☆

Если обернуть пути одинарными кавычками — он перестает воспринимать $i переменную

Мотороллер Баш не мой, строку интерполировать?

fernandos ★★★
()

Если обернуть пути одинарными кавычками — он перестает воспринимать $i переменную. Как быть?

Оборачивать в двойные

MrClon ★★★★★
()

for i in $(cat TheWall.list);

  while read i; do
   ff....  ".../$i"
  done < TheWall.list
futurama ★★★★★
()
Последнее исправление: futurama (всего исправлений: 1)
!#/bin/sh

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

for i in $(cat TheWall.list); do 
    ffmpeg -i Music/TheWall/Pink\ Floyd\ -\ 1979\ -\ The\ Wall\ \(24bit-96kHz\)/Disc\ 1/$i.flac -ab 320k -map_metadata 0 -id3v2_version 3 mp3/The_Wall/$i.mp3; 

ffmpeg -i "$FILEPATH" -strict experimental "$DIR/${BASE}.mp4"

done

IFS=$SAVEIFS

Ну короч у меня есть похожий скрипт для Наутилуса, может приспособишь его для себя:

$ cat Convert\ to\ MP4 
#!/bin/sh
# Convert any type of video file to mp4.

# changing delimeter of bash globbing to \n
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

for FILEPATH in $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS; do
    # get file extension
    EXT=$(echo "$FILEPATH" | sed 's/^.*\.\([^.]*\)$/\1/')

    # get filename without extension
    BASE=$(basename --suffix=".$EXT" "$FILEPATH")

    DIR=$(dirname "$FILEPATH")

    ffmpeg -i "$FILEPATH" -strict experimental "$DIR/${BASE}.mp4"

    if [ $? -eq 0 ]; then
        notify-send "'$BASE.$EXT' convert to mp4" "<b>OK</b>"
    else
        notify-send "'$BASE.$EXT' convert to mp4" "<b>FAIL</b>"
    fi
done

# go back to normal bash delimiter (space)
IFS=$SAVEIFS

Im_not_a_robot ★★★★★
()
Последнее исправление: Im_not_a_robot (всего исправлений: 1)
Ответ на: комментарий от hateWin

А, в твоём случае проблема не с ковычками и переменными, проблема в том что TheWall.list разбивается не по переводам строки, а по пробелам.

Выполни

for i in $(cat TheWall.list); do echo "i is $i"; done

Наглядно увидишь чему равно i на каждой итерации цикла

MrClon ★★★★★
()

sed там зачем? Есть же basename.

ashot ★★★★
()
Ответ на: комментарий от ashot

И как тогда пройтись циклом по строкам с пробелами, если баш считает пробле разделителем?

Im_not_a_robot ★★★★★
()
Ответ на: комментарий от ashot

Вроде бы нет, не помню…

Если задача — только файлы пережать, то с питоном тс уже давно бы все сделал.)

frunobulax ★★★
()

Прекрати кушать Bash-говно и возьми нормальный инструмент для этой задачи, про который тут уже написали.

EXL ★★★★★
()
Ответ на: комментарий от hateWin

Там выше вариант предлагали. Да и ниже тоже. Но вообще ты что-то странное делаешь, зачем сначала собирать обрезанные имена файлов в TheWall.list, а потом читать их оттуда, восстанавливать исходные имена и подсовывать их ffmpeg-у? Проще сделать что-то вроде

for filename in 'Music/TheWall/Pink Floyd - 1979 - The Wall (24bit-96kHz)/Disc 1/'*; do
 do_something_with "$filename"
done
MrClon ★★★★★
()
Ответ на: комментарий от AKonia

Ну не знаю, мне кажется будет хорошим тоном возвращать такие вещи к дефолту, даже если они изменяются только в области действия подпроцесса. Я знаком с баш-скриптами только по SO, мало ли что там будет при каких-нибудь странных условиях.

Im_not_a_robot ★★★★★
()

Кстати говоря, ТС, у deadbeef есть весьма удобный транскодер множества форматов, в т.ч. single file + cue (результирующие mp3 будут в отдельных файлах). Да и зачем в 2022 нужен mp3, если есть aac? Я вот проводил небольшие тесты для себя по степени сжатия (это Мб):

Original lossless ape:  389.7

AAC 320:                131.9
AAC 256:                101.6
AAC 192:                 79.6

MP3 320:                144.8

Im_not_a_robot ★★★★★
()
cd Music/TheWall/"Pink Floyd - 1979 - The Wall (24bit-96kHz)"/"Disc 1"

for f in *.flac; do   ffmpeg -i "$f" -b:a 320k "${f[@]/%flac/mp3}"; done
marcony
()
Последнее исправление: marcony (всего исправлений: 1)
Ответ на: комментарий от Im_not_a_robot

будет хорошим тоном возвращать такие вещи к дефолту

Не будет, т.к. вероятность того, что это окажет влияние в дальшейшем мала, более того не восстанавливая есть риск, что вы замените переменную глобально, что-то сломав, но и напротив восстановив можно неправильно вернуть значение, например опечатавшись или закрыв временное имя из другого контекста.

AKonia ★★
()
Ответ на: комментарий от EXL

Прекрати кушать Bash-говно и возьми нормальный инструмент для этой задачи, про который тут уже написали.

Bash - и есть нормальный инструмент для таких задач.

Самое оптимальное решение подсказали в третьем посте. @hateWin, помечай тему как решённую.

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от Kroz

С чего вдруг оно оптимальное? Оно запутанное и переусложненное. Я вот с трудом понимаю как оно работает, уверен ТС тоже будет ломать голову, если понадобится что-то изменить. Классическим решением для прохода циклом по строкам с пробелами является изменение переменной IFS.

Im_not_a_robot ★★★★★
()
Ответ на: комментарий от Kroz

«Самое оптимальное решение» и выглядит как говно

просто для сравнения:

import os
import subprocess

path = "/mnt/Music/TheWall/Pink Floyd - 1979 - The Wall (24bit-96kHz)/Disc 1"

for file in os.listdir(path):
    new_fname = os.path.splitext(file)[0]+".mp3"
    cmd = f"ffmpeg -i {path}/{file} -ab 320k -map_metadata 0 -id3v2_version 3 {path}/{new_fname}"
    subprocess.check_output(cmd, shell=True)
Ford_Focus ★★★★★
()
Последнее исправление: Ford_Focus (всего исправлений: 1)
Ответ на: комментарий от Im_not_a_robot

С чего вдруг оно оптимальное?

С того, что ты можешь легко в скриптах переиспользовать те же команды, что при обычной работе в командной строке; и для этого не нужны спец. конструкции.

Python нужен тогда, когда возможностей CLI тулов не хватает, или когда на их изучение нужно столько же время, сколько на изучение соответствующих Python модулей. Как правило это сложная работа данными (парсинг JSON/XML/CSV, выборки сложнее grep, трансформации сложнее sed s/../..), взаимодействия по сети (сложнее curl/wget) и т. п.

Я вот с трудом понимаю как оно работает

Любой инструмент нужно изучать. В случае Bash достаточно один раз прочитать Advanced Bash-Scripting Guide (лучше Архив руководства в html-формате (~380Кб)).

Классическим решением для прохода циклом по строкам с пробелами является изменение переменной IFS.

Классическим решение является то, что написано в в третьем посте. Что там сложного или непонятного?

Kroz ★★★★★
()
Ответ на: комментарий от Kroz

В случае Bash достаточно один раз прочитать

Да-да-да, один раз достаточно. Именно так и работает человеческий мозг. Один раз прочитал - и прочитанное уже отпечатано в памяти на десятилетия.

thesis ★★★★★
()

Интересно, сколько времени у тебя бы ушло на запуск ффмпега руками 13 раз. Чисто сравнить с эффективной юниксвейной баш-автоматизацией в стиле «лучше день потерять, зато потом за пять минут долететь».

thesis ★★★★★
()
Ответ на: комментарий от Ford_Focus

просто для сравнения:

Давай сравним с этим:

for FLAC_FILE in '/mnt/Music/TheWall/Pink Floyd - 1979 - The Wall (24bit-96kHz)/Disc 1/'*.flac ; do
   MP3_FILE="${FLAC_FILE%.flac}.mp3" # Удалить последнее вхождение ".flac", добавить ".mp3"
   ffmpeg -i "$FLAC_FILE" -ab 320k -map_metadata 0 -id3v2_version 3 "$MP3_FILE"
done
Kroz ★★★★★
()
Ответ на: комментарий от thesis

Да-да-да, один раз достаточно. Именно так и работает человеческий мозг. Один раз прочитал - и прочитанное уже отпечатано в памяти на десятилетия.

Дело не в том, что один раз. Дело в том, что есть один материал, который даёт исчерпывающее (ну ок, на 90%) понимание возможностей Bash. Есть такое для Python?

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 2)

Как быть?

Использовать адекватные задаче инструменты: curdir=$(pwd); find Music/TheWall/Pink\ Floyd\ -\ 1979\ -\ The\ Wall\ \(24bit-96kHz\)/Disc\ 1/ -name '*.flac' -type f -execdir ffmpeg -i '{}' -ab 320k -map_metadata 0 -id3v2_version 3 "${curdir}/mp3/The_Wall/$(basename {} .flac).mp3" ';'

dexpl ★★★★★
()
Последнее исправление: dexpl (всего исправлений: 1)
readarray -t LIST < <(cat TheWall.list)
src="./Music/TheWall/Pink Floyd - 1979 - The Wall (24bit-96kHz)/Disc 1"
dst="./mp3/The_Wall"

for i in "${LIST[@]}"; do
 ffmpeg -i "${src}/${i}.flac" -ab 320k -map_metadata 0 -id3v2_version 3 "${dst}/${i}.mp3"
done
DiMoN ★★★
()
Ответ на: комментарий от Kroz

Есть такое для Python?

Ну не один материал, а два. Странное какое-то соревнование - по количеству урлов. Да и вообще пофиг питон, у меня бомбануло от этого вот «достаточно(!) один раз(!) прочитать». До сих пор регулярно влетаю на грабли с пробелами и реву паровозом на шелл, линукс и все на свете. На тебя вот тоже.

thesis ★★★★★
()
Последнее исправление: thesis (всего исправлений: 1)
Ответ на: комментарий от thesis

Ну не один материал, а два.

Давай два :)

Не только ж похоливорить сюда ходим.

Если правда полезное чтиво, спасибо скажу.

Kroz ★★★★★
()
Ответ на: комментарий от Kroz

В случае Bash достаточно один раз прочитать Advanced Bash-Scripting Guide

Чтобы понять, насколько он наркоманский и всячески избегать использования bash в качестве языка программирования.

aquadon ★★★★★
()
Ответ на: комментарий от Kroz

Неожиданно. А я уж подготовился читать, что я лох и доки эти тоже для лохов)

thesis ★★★★★
()
Ответ на: комментарий от hateWin
for i in (for i in (ls | grep .flac); basename $i; end); ffmpeg -i $i -vn -c:a aac -b:a 320k -map_metadata 0 ~/Music/aac/TheWall/(echo $i | sed s/.flac/.aac/g); end
hateWin ★☆
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.