Bash 脚本:为什么此文件附加的最后一行缺失?

Bash scripting: why is the last line missing from this file append?

我正在编写一个 bash 脚本来逐行读取一组文件并执行一些编辑。首先,我只是尝试将文件移动到备份位置并按原样写出,以测试脚本是否正常工作。但是,它无法复制每个文件的最后一行。这是片段:

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

我显然想在复制文件时保留空格,这就是我将 IFS 设置为空的原因。我可以从输出中看到正在读取每个文件的最后一行,但它从未出现在输出中。

我也试过另一种变体,它打印最后一行,但添加了一个换行符:

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

执行此读写操作的最佳方法是什么,以完全按照原样写入文件,使用正确的空格且不添加换行符?

添加换行符 (LF) 的命令 不是 read 命令,而是 echo 命令。 read 而不是 return 仍然附加分隔符的行;相反,它会去掉分隔符(也就是说,如果分隔符出现在行中,IOW,如果它只是读取一个完整的行,它就会去掉)。

所以,要解决这个问题,你必须使用echo -n来避免加回分隔符,但是只有当你有一个不完整的行时。

其次,我发现在为 read 提供 NAME 时(在您的情况下为 line),它会修剪前导和尾随空格,我认为这不会你要。但这可以通过根本不提供 NAME 并使用默认 return 变量 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;

编辑:哇!来自 Charles Duffy 的三个极好的建议,这里是一个更新的脚本:

#!/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;

如果行不是一行,则添加新行。像这样:

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

经过审核我想知道是否:

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

是不是不够...

这里是我的初步建议:

#!/bin/bash

INFILE=

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

想法是始终在文件末尾添加换行符并仅在读取的行之间打印换行符。

这应该适用于所有文本文件,因为它们不包含 0 字节,即 \0 字符,在这种情况下,0 字符字节将丢失。

初始测试可用于决定是否接受不完整的文本文件。