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 字符字节将丢失。
初始测试可用于决定是否接受不完整的文本文件。
我正在编写一个 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 字符字节将丢失。
初始测试可用于决定是否接受不完整的文本文件。