2015-02-02 2 views
1

Я пишу скрипт bash для чтения набора файлов по строкам и выполнения некоторых изменений. Начнем с того, что я просто пытаюсь переместить файлы в места резервного копирования и записать их как есть, чтобы проверить, работает ли скрипт. Однако он не копирует последнюю строку каждого файла. Вот фрагмент кода:Bash scripting: почему последняя строка, отсутствующая в этом файле, добавляется?

while IFS= read -r line 
    do 
      echo "Line is ***$line***" 
      echo "$line" >> $POM 
    done < $POM.backup 

Я, очевидно, хотят, чтобы сохранить пробелы при копировании файлов, поэтому я поставил МФС к нулю. Я вижу на выходе, что последняя строка каждого файла читается, но никогда не появляется на выходе.

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

while IFS= read -r line || [ -n "$line" ] 
    do 
      echo "Line is ***$line***" 
      echo "$line" >> $POM 
    done < $POM.backup 

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

+0

Я вижу, что читается последняя строка, так как она выводится командой echo. Однако он не отображается в новом файле. – Hedley

+0

Тогда '$ POM.backup' может иметь' \ r' перед '\ n' – anubhava

+0

Как это повлияет на запись в новый файл? – Hedley

ответ

1

Команда, которая добавляет подачу линии (LF), составляет , а не команда read, но команда echo. read делает не возвращает линию с разделителем, все еще прикрепленным к ней; скорее, он удаляет разделитель (т. е. он удаляет его, если он присутствует в строке, IOW, если он просто читает полную строку).

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

Во-вторых, я обнаружил, что при предоставлении read с NAME (в вашем случае line), он урезает начальные и конечные пробелы, которые я не думаю, что вы хотите. Но это можно решить, не предоставив вообще NAME и используя возвращаемую по умолчанию переменную REPLY, которая сохранит все пробелы.

Таким образом, это должно работать:

#!/bin/bash 

inFile=in; 
outFile=out; 

rm -f "$outFile"; 

rc=0; 
while [[ $rc -eq 0 ]]; do 
    read -r; 
    rc=$?; 
    if [[ $rc -eq 0 ]]; then ## complete line 
     echo "complete=\"$REPLY\""; 
     echo "$REPLY" >>"$outFile"; 
    elif [[ -n "$REPLY" ]]; then ## incomplete line 
     echo "incomplete=\"$REPLY\""; 
     echo -n "$REPLY" >>"$outFile"; 
    fi; 
done <"$inFile"; 

exit 0; 

Edit: Wow! Три замечательных предложения от Чарльза Даффи, вот обновленный сценарий:

#!/bin/bash 

inFile=in; 
outFile=out; 

while { read -r; rc=$?; [[ $rc -eq 0 || -n "$REPLY" ]]; }; do 
    if [[ $rc -eq 0 ]]; then ## complete line 
     echo "complete=\"$REPLY\""; 
     printf '%s\n' "$REPLY" >&3; 
    else ## incomplete line 
     echo "incomplete=\"$REPLY\""; 
     printf '%s' "$REPLY" >&3; 
    fi; 
done <"$inFile" 3>"$outFile"; 

exit 0; 
+1

Это работает, но читать его немного сложно. Возможно, использование составной команды в условном выражении 'while' может помочь в этом счете? –

+1

Также см. Раздел «ИСПОЛЬЗОВАНИЕ ПРИМЕНЕНИЯ» в http://pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html для заметок прямо из спецификации POSIX по ограничениям переносимости эха. Безопаснее использовать 'printf '% s \ n' '$ REPLY" '(или' printf'% s '"$ REPLY" ', когда не требуется новая строка), если вы хотите, чтобы это работало на системах с равным эхо-сигналом POSIX, XSI расширенное эхо и реализация GNU (которая не соответствует ни стандарту). –

+2

Кроме того, более эффективно открывать выходной файл только один раз, а не открывать его каждый раз, когда вы хотите добавить еще одну строку до конца. Просто поместите '3>" $ outFile "в конец вашего цикла и перенаправьте'> & 3' каждый раз, когда вы хотите добавить строку; это не только более эффективно, но также означает, что вам не нужен 'rm -f'. –

0

Добавить новую строку, если линия не является строкой. Как это:

while IFS= read -r line 
do 
    echo "Line is ***$line***"; 
    printf '%s' "$line" >&3; 
    if [[ ${line: -1} != '\n' ]] 
    then 
     printf '\n' >&3; 
    fi 
done < $POM.backup 3>$POM 
+1

'echo" \ n "' будет вызывать буквенные символы \ и 'n' на нескольких системах. 'printf '\ n'' был бы более безопасным подходом. Аналогично, 'printf '% s \ n'" $ line "' будет обрабатывать содержимое, где 'echo '$ line' 'будет (во многих системах) испортить вещи - как строка, содержащая буквальное содержимое' -n'. –

+0

@CharlesDuffy, спасибо. Исправленный. –

+1

Кроме того, поскольку я прокомментировал другой ответ, повторное открытие выходного файла для каждой строки является существенным ненужным штрафом за производительность, а не просто его открытием и повторным использованием дескриптора файла. –

0

После обзора я интересно, если:

{ 
line= 
while IFS= read -r line 
do 
    echo "$line" 
    line= 
done 
echo -n "$line" 
} <$INFILE >$OUTFILE 

это выпирает не хватает ...

Вот мое первоначальное предложение:

#!/bin/bash 

INFILE=$1 

if [[ -z $INFILE ]] 
then 
    echo "[ERROR] missing input file" >&2 
    exit 2 
fi 

OUTFILE=$INFILE.processed 

# a way to know if last line is complete or not : 
lastline=$(tail -n 1 "$INFILE" | wc -l) 

if [[ $lastline == 0 ]] 
then 
    echo "[WARNING] last line is incomplete -" >&2 
fi 

# we add a newline ANYWAY if it was complete, end of file will be seen as ... empty. 
echo | cat $INFILE - | { 
    first=1 
    while IFS= read -r line 
    do 
     if [[ $first == 1 ]] 
     then 
     echo "First Line is ***$line***" >&2 
     first=0 
     else 
     echo "Next Line is ***$line***" >&2 
     echo 
     fi 
     echo -n "$line" 
    done 
} > $OUTFILE 

if diff $OUTFILE $INFILE 
then 
    echo "[OK]" 
    exit 0 
else 
    echo "[KO] processed file differs from input" 
    exit 1 
fi 

Идея заключается в том, чтобы всегда добавьте новую строку в конец файла и напечатайте новые строки только прочитанные строки BETWEEN.

Это должно работать практически для всех текстовых файлов, если они не содержат 0 байт, то есть символ \ 0, и в этом случае 0 байт-символ будет потерян.

Первоначальный тест может быть использован для определения приемлемого или неполного текстового файла.

Смежные вопросы