Вместо:
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
Сначала соберите список в массив 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.
Вы можете увеличить лимит файловых дескрипторов:
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
(отредактировано для ясности и безопасности)
Это должно работать, если в последовательности файлов нет пробелов. Просто замените 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