Ответ 1
В вашем первом примере я предполагаю, что вы читаете stdin. Чтобы сделать то же самое со вторым блоком кода, вам просто нужно удалить перенаправление и echo $REPLY:
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
Предположим, что у меня есть следующий Bash script:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
Я заметил, что для файлов без новой строки в конце это эффективно пропустит последнюю строку.
Я искал для решения и нашел это:
Когда чтение доходит до конца файла конца строки, он читается в данных и присваивать их переменным, но он выходит с ненулевым статусом. Если ваш цикл построен "while читать, делать вещи, делать
Итак, вместо тестирования выхода для чтения статус, проверить флаг и команда чтения устанавливает этот флаг из внутри тела петли. Сюда независимо от того, считывает статус выхода, весь цикл цикла цикла, потому что чтение был только одним из списка команд в цикле, как и любой другой, а не решающим фактором, если цикл будет получить запуск вообще.
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
Как я могу переписать это решение, чтобы заставить его вести себя точно так же, как цикл while
, который у меня был раньше, т.е. без жесткого кодирования местоположения входного файла?
В вашем первом примере я предполагаю, что вы читаете stdin. Чтобы сделать то же самое со вторым блоком кода, вам просто нужно удалить перенаправление и echo $REPLY:
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
Я использую следующую конструкцию:
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
echo "$LINE"
done
Он работает практически со всеми, кроме нулевых символов на входе:
Использование grep
с циклом while:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
Использование grep .
вместо grep ""
будет пропускать пустые строки.
Примечание:
Использование IFS=
не содержит никаких отступов строки.
Файл без новой строки в конце не является стандартным текстовым файлом unix.
Вместо чтения попробуйте использовать GNU Coreutils, например, tee, cat и т.д.
из stdin
readvalue=$(tee)
echo $readvalue
из файла
readvalue=$(cat filename)
echo $readvalue
Основная проблема здесь заключается в том, что read
будет возвращать уровень ошибок 1, когда он встречает EOF, даже если он все равно правильно передает переменную.
Таким образом, вы можете использовать уровень ошибок read
сразу в вашем цикле, иначе, последние данные не будут проанализированы. Но вы можете сделать это:
eof=
while [ -z "$eof" ]; do
read SCRIPT_SOURCE_LINE || eof=true ## detect eof, but have a last round
echo "$SCRIPT_SOURCE_LINE"
done
Если вам нужен очень прочный способ анализа ваших строк, вы должны использовать:
IFS='' read -r LINE
Помните, что:
echo
для сопоставления поведения cat
, вам необходимо принудительно установить echo -n
при обнаружении EOF (вы можете использовать условие [ "$eof" == true ]
)@Netcoder ответ хорош, эта оптимизация устраняет ложные пустые строки, также позволяет последней строке не иметь новую строку, если это так, как оригинал.
DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done
Я использовал вариант этого, чтобы создать 2 функции, которые позволяли бы писать тексты, содержащие "[', чтобы сохранить grep счастливым. (вы можете добавить другие переводы)
function grepfix(){
local x="[email protected]";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\[/\\\[}
done
else
echo "${x//\[/\\\[}"
fi
}
function grepunfix(){
local x="[email protected]";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\\\[/\[}
done
else
echo "${x//\\\[/\[}"
fi
}
(передача - как $1 позволяет pipe иначе переводит аргументы)
Это шаблон, который я использовал:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
Это работает, потому что даже цикл "t21" заканчивается, когда "тест" из read
завершается с ненулевым кодом, read
по-прежнему заполняет встроенную переменную $REPLY
(или любые переменные, которые вы выбираете для назначения с помощью read
).