Разделение упорядоченного списка на подсписки

Вместо:

ls /var/log/hello grep -i hello.log. | echo "" | xargs echo "" | sudo tee

Попробуйте:

for f in /var/log/hello/*hello.log.*; do sudo truncate --size 0 "$f"; done

Чтобы отправить это по SSH, поместите его в одинарные кавычки , а не двойные кавычки :

for server in $(cat c10_servers.txt); do
  ssh "$server" 'for f in /var/log/hello/*hello.log.*; do sudo truncate --size 0 "$f"; done'
done

Вы можете сделать это без команды truncate , но цитирование становится уродливее:

for server in $(cat c10_servers.txt); do
  ssh "$server" 'sudo sh -c '\''for f; do : > "$f"; done'\'' sudo-sh /var/log/hello/*hello.log.*'
done

Если вы можете использовать SSH напрямую как root, команда будет проще:

for server in $(cat c10_servers.txt); do
  ssh -l root "$server" 'for f in /var/log/hello/*hello.log.*; do : > "$f"; done'
done
3
23.09.2016, 18:23
3 ответа

Сначала соберите список в массив Bash. Если файлы находятся в текущем каталоге, вы можете использовать

files=(prefix_????.mp3)

Альтернативно вы можете использовать find и sort,

IFS=$'\n' ;
files=($(find . -name 'prefix_*.mp3' printf '%p\n' | sort -d))

Установка IFS указывает Bash разделять только по новым строкам. Если имена ваших файлов и каталогов не содержат пробелов, вы можете опустить это.

В качестве альтернативы, вы можете читать имена файлов из файла, скажем filelist, по одному имени в строке, и никаких пустых строк,

IFS=$'\n'
files=($(<filelist))

Если у вас могут быть пустые строки, используйте

IFS=$'\n'
files=($(sed -e '/$/ d' filelist))

Далее, решите, сколько файлов вы хотите в каждом срезе, имя временного накопительного файла, а также окончательное объединенное имя файла:

s=100
src="combined-in.mp3"
out="combined-out.mp3"

Затем нам нужно просто нарезать список и обработать каждый подсписок:

while (( ${#files[@]} > 0 )); do
    n=${#files[@]}

    # Slice files array into sub and left.
    if (( n <= s )); then
        sub=("${files[@]}")
        left=()
    else
        (( n-= s ))
        sub=("${files[@]:0:s}")
        left=("${files[@]:s:n}")
    fi

    # If there is no source file, but there is
    # a sum file, rename sum to source.
    if [ ! -e "$src" -a -e "$out" ]; then
        mv -f "$out" "$src"
    fi

    # If there is a source file, include it first.
    if [ -e "$src" ]; then
        sub=("$src" "${sub[@]}")
    fi

    # Run command.
    if ! sox "${sub[@]}" "$out" ; then
        rm -f "$out"
        echo "Failed!"
        break
    fi

    rm -f "$src"
    echo "Done up to ${sub[-1]}."
    files=("${left[@]}")

    # rm -f "${sub[@]}"
done

Если sox сообщит о неудаче, цикл прервется раньше. В противном случае будет выведено последнее имя в обработанной партии.

Мы используем if для команды sox, чтобы определить сбой и удалить выходной файл, если действительно произошел сбой. Поскольку мы также откладываем изменение массива files до успешного выполнения команды sox, мы можем спокойно редактировать/исправлять отдельные файлы, а затем просто повторно запустить цикл while, чтобы продолжить с того места, на котором остановились.

Если у вас мало места на диске, вы можете откомментировать предпоследнюю строку, rm -f "${sub[@]}", чтобы удалить все файлы, которые были успешно объединены.


Вышеописанное обрабатывает начальные части снова и снова.

Как я объяснил в комментарии ниже, результаты будут намного лучше, если вы сначала объедините файлы с помощью ffmpeg (без перекодировки с помощью sox), а затем, возможно, выполните перекодировку с помощью sox. (Или, конечно, можно сначала перекодировать каждый из них. )

Сначала вы создаете разделенный трубами список (строку) имен файлов,

files="$(ls -1 prefix_????.mp3 | tr '\n' '|')"

удаляете последнюю лишнюю трубу,

files="${files%|}"

и передаете их в ffmpeg, без перекодировки:

ffmpeg -i "concat:$files" -codec copy output.mp3

Обратите внимание, что вы, возможно, захотите запустить

ulimit -n hard

для увеличения числа открытых файлов до максимально допустимого для текущего процесса (hard limit); вы можете запросить его с помощью ulimit -

ffmpeg -i "concat:$files" -codec copy output.mp3

.n. (Я не помню, открывает ли ffmpeg concat: источники последовательно или все сразу.)

Если вы делаете это более одного раза, я бы записал все это в простой скрипт:

#!/bin/bash
export LANG=C LC_ALL=C
if [ $# -le 2 -o "$1" = "-h" -o "$1" = "--help" ]; then
    exec >&2
    printf '\n'
    printf 'Usage: %s -h | --help ]\n' "$0"
    printf '       %s OUTPUT INPUT1 .. INPUTn\n' "$0"
    printf '\n'
    printf 'Inputs may be audio mp3 or MPEG media files.\n'
    printf '\n'
    exit 1
fi

output="$1"
shift 1
ulimit -n hard

inputs="$(printf '%s|' "${@}")"
inputs="${inputs%|}"

ffmpeg -i "concat:$inputs" -codec copy "$output"
retval=$?

if [ $retval -ne 0 ]; then
    rm -f "$output"
    echo "Failed!"
    exit $retval
fi

# To remove all inputs now, uncomment the following line:
# rm -f "${@}"
echo "Success."
exit 0

Обратите внимание, что поскольку я использую -codec copy вместо -acodec copy, вышеописанное должно работать для всех типов MPEG-файлов, а не только для аудиофайлов mp3.

2
27.01.2020, 21:15

Вы можете увеличить лимит файловых дескрипторов:

ulimit -n 11000

Как обычный пользователь, вы должны иметь возможность поднять этот лимит до жесткого лимита. См. ulimit -Hn для получения информации о текущем жестком ограничении.

Некорневой процесс не может поднять жесткое ограничение (в том-то и дело, администратор устанавливает его, чтобы обычные пользователи не злоупотребляли системными ресурсами). Если у вас есть доступ суперпользователя через sudo , вы можете запустить новую оболочку без суперпользователя с повышенным жестким и мягким пределом с помощью:

sudo HOME="$HOME" zsh -c 'ulimit -HSn 100000; USERNAME=$SUDO_USER; zsh'

Или этой команды sox:

sudo HOME="$HOME" zsh -c 'ulimit -HSn 100000; USERNAME=$SUDO_USER
                          sox prefix_*.mp3 script_name_output.mp3'

Если в Linux, вы также можете вызовите команду prlimit от имени пользователя root, чтобы поднять предел вашей оболочки (и ее дочерних элементов):

bash-4.3$ ulimit -n
1024
bash-4.3$ ulimit -Hn
65536
bash-4.3$ sudo prlimit --nofile=100000:100000 --pid="$$"
bash-4.3$ ulimit -Hn
100000
bash-4.3$ ulimit -n
100000

В противном случае вы могли бы выполнить задание в два этапа: объединить файлы в группы по 347 файлов, а затем объединить промежуточные файлы.

С zsh :

intermediate_concat() sox "$@" intermediate.$((++n)).mp3
autoload zargs
n=0
zargs -n 347 prefix_*.mp3 -- intermediate_concat
sox intermediate.*.mp3(n) script_name_output.mp3
1
27.01.2020, 21:15

(отредактировано для ясности и безопасности)

Это должно работать, если в последовательности файлов нет пробелов. Просто замените LAST = 0 последним 4-значным числом в вашей последовательности. У вас останется script_name_output.mp3 .

# make a backup in case anything goes wrong
mkdir backup && cp *.mp3 backup

# enter last 4-digit number in the file sequence
LAST=0
LASTNN__=$(echo ${LAST:0:2})
LAST__NN=$(echo ${LAST:2:2})

# sox 100 files at a time
for i in $(seq -f "%02g" 0 $((--LASTNN__))); do
  LIST=$(paste -sd' ' <(seq -f "prefix_$i%02g.mp3" 0 99));
  sox $LIST script_name_output_$i.mp3;
done

# sox the last group
LAST_LIST=$(paste -sd' ' \
  <(seq -f "prefix_${LASTNN__}%02g.mp3" 0 $LAST__NN))
sox $LAST_LIST script_name_output_${LASTNN__}.mp3

# concatenate all the sox'ed files
OUTPUT_LIST=$(paste -sd' ' \
  <(seq -f "script_name_output_%02g.mp3" 0 $LASTNN__))
sox $OUTPUT_LIST script_name_output.mp3

# delete the intermediate files
rm $OUTPUT_LIST

# delete input files if everything worked
rm prefix_*.mp3
2
27.01.2020, 21:15

Теги

Похожие вопросы