简体   繁体   English

Bash脚本:为什么此文件中缺少最后一行?

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

I'm writing a bash script to read a set of files line by line and perform some edits. 我正在编写一个bash脚本,以逐行读取一组文件并执行一些编辑。 To begin with, I'm simply trying to move the files to backup locations and write them out as-is, to test the script is working. 首先,我只是尝试将文件移至备份位置并按原样将其写出,以测试脚本是否正常运行。 However, it is failing to copy the last line of each file. 但是,它无法复制每个文件的最后一行。 Here is the snippet: 这是代码段:

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

I obviously want to preserve whitespace when I copy the files, which is why I have set the IFS to null. 复制文件时,我显然想保留空格,这就是为什么将IFS设置为null的原因。 I can see from the output that the last line of each file is being read, but it never appears in the output. 从输出中可以看到正在读取每个文件的最后一行,但是它从未出现在输出中。

I've also tried an alternative variation, which does print the last line, but adds a newline to it: 我还尝试了另一种变体,它确实打印了最后一行,但在其中添加了换行符:

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

What is the best way to do this do this read-write operation, to write the files exactly as they are, with the correct whitespace and no newlines added? 什么是执行此读写操作,以正确的空格完全插入文件且未添加换行符的方式完全写入文件的最佳方法?

The command that is adding the line feed (LF) is not the read command, but the echo command. 添加换行(LF)的命令不是 read命令,而是echo命令。 read does not return the line with the delimiter still attached to it; read 返回与仍连接在其分隔符行; rather, it strips the delimiter off (that is, it strips it off if it was present in the line, IOW, if it just read a complete line). 相反,它将剥离定界符(也就是说,如果它只是在一行完整的行中出现,那么它将剥离IOW)。

So, to solve the problem, you have to use echo -n to avoid adding back the delimiter, but only when you have an incomplete line. 因此,要解决该问题,必须使用echo -n避免加回定界符,但前提是您的行不完整。

Secondly, I've found that when providing read with a NAME (in your case line ), it trims leading and trailing whitespace, which I don't think you want. 其次,我发现,当提供readNAME (你的情况line ),它TRIMS开头和结尾的空白,我不认为你想要的。 But this can be solved by not providing a NAME at all, and using the default return variable REPLY , which will preserve all whitespace. 但这可以通过完全不提供NAME并使用默认的返回变量REPLY ,它将保留所有空白。

So, this should work: 因此,这应该工作:

#!/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! 编辑:哇! Three excellent suggestions from Charles Duffy, here's an updated script: 查尔斯·达菲(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;

Add a new line if line is not a line. 如果line不是一行,请添加新行。 Like this: 像这样:

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

After review i wonder if : 经过审查,我想知道是否:

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

is juts not enough... 突出还不够...

Here my initial proposal : 这是我最初的建议:

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

Idea is to always add a newline at the end of file and to print newlines only BETWEEN lines that are read. 想法是始终在文件末尾添加换行符,并仅在读取的行之间打印换行符。

This should work for quite all text files given they are not containing 0 byte ie \\0 character, in which case 0 char byte will be lost. 给定所有不包含0字节(即\\ 0字符)的文本文件,这应该适用于几乎所有文本文件,在这种情况下,将丢失0个字符字节。

Initial test can be used to decided whether an incomplete text file is acceptable or not. 可以使用初始测试来确定是否可以接受不完整的文本文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM