Скрипт Bash не продолжает читать следующую строку файла - PullRequest
3 голосов
/ 17 февраля 2009

У меня есть сценарий оболочки, который сохраняет выходные данные команды, которая выполняется в файл CSV. Он читает команду, которую должен выполнить из сценария оболочки, который имеет следующий формат:

ffmpeg -i /home/test/videos/avi/418kb.avi /home/test/videos/done/418kb.flv
ffmpeg -i /home/test/videos/avi/1253kb.avi /home/test/videos/done/1253kb.flv
ffmpeg -i /home/test/videos/avi/2093kb.avi /home/test/videos/done/2093kb.flv

Вы можете видеть, что каждая строка является командой ffmpeg. Однако скрипт выполняет только первую строку. Минуту назад он выполнял почти все команды. По какой-то причине ему не хватало половины. Я отредактировал текстовый файл, содержащий команды, и теперь он будет выполнять только первую строку. Вот мой скрипт bash:

#!/bin/bash
# Shell script utility to read a file line line.
# Once line is read it will run processLine() function

#Function processLine
processLine(){
line="$@"

START=$(date +%s.%N)

eval $line > /dev/null 2>&1 

END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)

echo "$line, $START, $END, $DIFF" >> file.csv 2>&1
echo "It took $DIFF seconds"
echo $line
}

# Store file name
FILE=""

# get file name as command line argument
# Else read it from standard input device
if [ "$1" == "" ]; then
   FILE="/dev/stdin"
else
   FILE="$1"
   # make sure file exist and readable
   if [ ! -f $FILE ]; then
    echo "$FILE : does not exists"
    exit 1
   elif [ ! -r $FILE ]; then
    echo "$FILE: can not read"
    exit 2
   fi
fi
# read $FILE using the file descriptors

# Set loop separator to end of line
BAKIFS=$IFS
IFS=$(echo -en "\n\b")
exec 3<&0
exec 0<$FILE
while read line
do
    # use $line variable to process line in processLine() function
    processLine $line
done
exec 0<&3

# restore $IFS which was used to determine what the field separators are
BAKIFS=$ORIGIFS
exit 0

Спасибо за любую помощь.

ОБНОВЛЕНИЕ 2

Это команды ffmpeg, а не сценарий оболочки, который не работает. Но я должен был использовать только "\ b", как указал Пол. Я также использую более короткий сценарий Йоханнеса.

Ответы [ 5 ]

4 голосов
/ 17 февраля 2009

Я думаю, что следует сделать то же самое и, кажется, правильно:

#!/bin/bash

CSVFILE=/tmp/file.csv

cat "$@" | while read line; do
    echo "Executing '$line'"
    START=$(date +%s)
    eval $line &> /dev/null
    END=$(date +%s)
    let DIFF=$END-$START

    echo "$line, $START, $END, $DIFF" >> "$CSVFILE"
    echo "It took ${DIFF}s"
done

нет

3 голосов
/ 01 декабря 2011

ffmpeg читает STDIN и исчерпывает его. Решение состоит в том, чтобы вызвать ffmpeg с:

 ffmpeg </dev/null ...

См. Подробное объяснение здесь: http://mywiki.wooledge.org/BashFAQ/089

2 голосов
/ 26 декабря 2009

У меня просто была такая же проблема.

Я полагаю, что ffmpeg ответственен за это поведение.

Мое решение для этой проблемы:

1) Вызовите ffmpeg с «&» в конце командной строки ffmpeg

2) Поскольку теперь скрипт не будет ждать завершения процесса ffmpeg, мы должны предотвратить запуск нашего сценария нескольких процессов ffmpeg. Мы достигаем этой цели, задерживая прохождение цикла, пока есть хотя бы один запущенный процесс ffmpeg.

#!/bin/bash

cat FileList.txt |
while read VideoFile; do
    <place your ffmpeg command line here> &
    FFMPEGStillRunning="true"
    while [ "$FFMPEGStillRunning" = "true" ]; do
        Process=$(ps -C ffmpeg | grep -o -e "ffmpeg" )
        if [ -n "$Process" ]; then
            FFMPEGStillRunning="true"
        else
            FFMPEGStillRunning="false"
        fi 
        sleep 2s
    done
done
1 голос
/ 17 февраля 2009

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

Точно так же я не вижу причины для того, чтобы пить с IFS вообще. Конечно, нет необходимости восстанавливать значение IFS перед выходом - это настоящая оболочка, которую вы используете, а не файл DOS BAT.

Когда вы делаете:

read var1 var2 var3

оболочка назначает первое поле $var1, второе $var2, а остальную часть строки $var3. В случае, когда есть только одна переменная - например, ваш скрипт - вся строка переходит в переменную, как вы этого хотите.

Внутри функции строки процесса вы, вероятно, не хотите выбрасывать вывод ошибок из выполненной команды. Вы, вероятно, хотите подумать о проверке состояния выхода команды. echo с перенаправлением ошибок ... необычно и излишне. Если вы достаточно уверены, что команды не могут завершиться с ошибкой, тогда игнорируйте ошибку. Является ли команда «болтливым»; если это так, отбросьте чат во что бы то ни стало. Если нет, возможно, вам также не нужно выбрасывать стандартный вывод.

Сценарий в целом, вероятно, должен диагностировать, когда ему дается несколько файлов для обработки, поскольку он игнорирует посторонние.

Вы можете упростить обработку файлов, используя просто:

cat "$@" |
while read line
do
    processline "$line"
done

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

Интересно использовать date и bc - я раньше такого не видел.

В общем, я бы посмотрел на что-то вроде:

#!/bin/bash
# Time execution of commands read from a file, line by line.
# Log commands and times to CSV logfile "file.csv"

processLine(){
    START=$(date +%s.%N)
    eval "$@" > /dev/null
    STATUS=$?
    END=$(date +%s.%N)
    DIFF=$(echo "$END - $START" | bc)
    echo "$line, $START, $END, $DIFF, $STATUS" >> file.csv
    echo "${DIFF}s: $STATUS: $line"
}

cat "$@" |
while read line
do
    processLine "$line"
done
1 голос
/ 17 февраля 2009

Я бы добавил echos до и после eval, чтобы увидеть, что он собирается вычислять (если он обрабатывает весь файл как одну большую длинную строку) и после (в случае, если одна из команд ffmpeg работает вечно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...