在bash脚本中逐行读写

时间:2016-06-20 20:05:29

标签: bash perl

在网上搜索后,我能够弄清楚如何逐行阅读文件:

while read p; do
  echo $p
done < file.txt

但我真的想修改文件中的行。 例如:

while read p; do
  if condition
  then
    echo $p | perl -i -pe 's/a/b/'
  fi
done < file.txt

然而,这并没有实际修改文件。

1 个答案:

答案 0 :(得分:1)

更新添加了更好的bash代码版本。感谢Charles Duffy的评论。

你的Perl one-liner采用echo $p |传递给它的一条线,以此方式获得标准输入。它对文件本身没有任何作用,因此-i标志无效。 -p使其打印到标准输出流。因此,整行echo ...不会触及文件。

您可以将输出重定向到新文件,然后移动该文件以覆盖file.txt。这是一个简单的例子,它将每一行附加到一个新文件。有关更好的bash代码,请参阅下面的更新。

while read p; do
    if condition
    then
        echo $p | perl -pe 's/a/b/' >> temp_out.txt
    else
        echo $p >> temp_out.txt
    fi
done < file.txt
mv temp_out.txt file.txt

我们必须添加else,其中还会附加所有未修改的行。请注意,通常我们不能只更换一些行,但必须重写整个文件。

如果这就是脚本所做的一切,你可以使用一个非常简单的单行程序来完成它,请参阅结尾。如果做了更多的工作,你也可以将它全部放在Perl脚本中,但我认为bash脚本可能有其他充分的理由。

更新上述更好的版本。请参阅Builtins in Bash manual

中的readecho
  • 每次添加行都会重新打开文件,而不需要这样做。 只需在循环结束时重定向,就像在终端

  • 中完成的那样
  • read使用反斜杠进行转义,将其从输入中删除。请使用-r

  • 关闭此功能
  • 删除尾随空白区域,作为将该行划分为单词的一部分。通过取消设置控制用于拆分的字符的变量来解决此问题,IFS=

  • echo $p可以做各种无意识的事情。格式化的打印效果更好,printf '%s\n' "$p"或至少echo "$p"

有了这个,

while IFS= read -r p; do
    if condition
    then
        echo "$p" | perl -pe 's/a/b/'
    else
        echo "$p"
    fi
done < file.txt  > temp_out.txt
mv temp_out.txt file.txt

最后,如果Perl one-liner的唯一目的是运行一个简单的替换,那么在shell本身中执行该操作要比拥有一个管道并为每一行运行一个全新的进程要好得多。 / p>

echo "${p//a/b}"

感谢Charles Duffy在评论中提出所有这些要点。

关于Perl one-liners的一些评论。请参阅perlrun上的文档。

命令perl -e '...'执行''之间的任何有效Perl代码。当我们添加-n-p开关时,它还会读取标准输入并在此行的某一行上执行该代码,其中-p也会在处理完每行后打印出来。标准输入可以从文件

提供给它
perl -pe '...' input.txt

在这种情况下,添加-i标志将导致文件就地更改。或者,输入可以通过管道输入,例如

echo "input text" | perl -pe '...'

在这种情况下,处理过的行打印到标准输出。这可以重定向到文件,如上面的答案。

要一次更改给定文件,您只需在命令行

上进行此操作
perl -i -pe 's/a/b/' file.txt

如果还有更多工作要做,那么最好将它放在脚本中,当然。在这种情况下,单行也可以是bash脚本中的命令,替换上面的所有代码(除非处理行首选某些特定于bash的功能)。