仅当不存在换行符时才添加换行符

2024-11-01 08:41:00
admin
原创
41
摘要:问题描述:我想仅在文件末尾不存在换行符时才添加换行符。这是为了防止文件末尾出现多个换行符。我希望使用sed。以下是我当前代码遇到的问题:sed -i -e '/^$/d;$G' /inputfile echo file1 name1 name2 echo file2 n...

问题描述:

我想仅在文件末尾不存在换行符时才添加换行符。这是为了防止文件末尾出现多个换行符。

我希望使用sed。以下是我当前代码遇到的问题:

sed -i -e '/^$/d;$G' /inputfile

echo file1
name1
name2

echo file2
name3
name4
(newline)

当我在文件上运行我的代码时;

echo file1
name1
name2
(newline)

echo file2
name3
name4

如果没有换行符,它会添加一个换行符;如果存在换行符,它会删除它……这让我很困惑。


解决方案 1:

sed

GNU:

sed -i '$a\' *.txt

操作系统:

sed -i '' '$a\' *.txt

$解决最后一行。a是附加函数。

OS X 的 sed

sed -i '' -n p *.txt

-n禁用打印并p打印模式空间。p在 OS X 的 sed 中添加缺少的换行符,但在 GNU sed 中不添加,因此这不适用于 GNU sed。

awk

awk 1

1(数字 1)可以替换为任何计算结果为 true 的值。 就地修改文件:

{ rm file;awk 1 >file; }<file

狂欢

[[ $(tail -c1 file) && -f file ]]&&echo ''>>file

尾随换行符将从命令替换的结果中删除,因此$(tail -c1 file)仅当file以换行符结尾或为空时才为空。-f file如果为空则为 false file[[ $x ]]相当于[[ -n $x ]]bash 中的。

解决方案 2:

不必处理整个文件只是sed为了在末尾添加一个换行符,只需检查最后一个字符,如果不是换行符,则添加一个。测试换行符有点意思,因为 shell 通常会从字符串末尾修剪它们,所以我附加了“x”来保护它:

if [ "$(tail -c1 "$inputfile"; echo x)" != $'
x' ]; then
    echo "" >>"$inputfile"
fi

请注意,这会将换行符附加到空文件,这可能不是您想要的。如果您想保留空文件,请添加另一个测试:

if [ -s "$inputfile" ] && [ "$(tail -c1 "$inputfile"; echo x)" != $'
x' ]; then
    echo "" >>"$inputfile"
fi

解决方案 3:

为方便起见,将诺曼的回答转换为拆分的单行。

for i in * ; do  echo $i; \n if diff /dev/null "$i" | tail -1 | \n  grep '^\\ No newline' > /dev/null; then echo >> "$i"; \n fi; done

将 * 替换为您想要的任何文件模式,例如*.c

另一个只是告诉您哪些文件已损坏:

for i in * ; do \n if diff /dev/null "$i" | tail -1 | \n  grep '^\\ No newline' > /dev/null; then  echo $i; \n fi; done

解决方案 4:

因为如果没有换行符它会删除换行符,所以您可以简单地使用:

echo "" >> file;  sed -ie '/^$/d;$G' file; sed -ie '/^$/d;$G' file

添加换行符并删除所有内容,然后添加换行符。虽然方法不太优雅,但确实有效 :)

解决方案 5:

对于文件末尾“缺少”换行符的文件,一个简单的修复方法是使用 sed;下面“就地”修复文件(使用“-i”选项):

find . -type f -exec sed -i -e '$a\' {} ; -print 

解释:

  • 查找所有文件 ( -type f),

  • 跑步sed

  • 就地修改文件(-i),

  • 给定以下 ( -e) 脚本/表达式,它与文件结尾匹配 ( $),

    • 并执行“附加”操作(a),

    • 但实际上并没有指定要附加的任何文本( 之后没有任何内容``),这将在文件末尾添加一个换行符,但只有当它丢失时才会这样做。

  • 打印找到的所有文件(无论是否修复),这可能是不必要的。

主要需要注意的是,sed功能在不同平台之间有所差异,因此-i可能-e支持也可能不支持/相同;例如,较旧的 Unix 或 MacOS 的奇怪之处可能需要略有不同的语法。

要仅对与特定后缀匹配的文件名进行操作,只需添加find path/to/dir -type f ( -name *.C -o -name *.h -o -name *.java ) -exec ...

解决方案 6:

如果您可以使用 Unix 工具,您可以运行diff来找出哪些文件缺少换行符,然后将其附加:

#!/bin/sh
for i
do
  if diff /dev/null "$i" | tail -1 | grep '^\\ No newline' > /dev/null
  then 
    echo >> "$i"
  fi
done

我依靠来生成第一列diff带有 的消息 ,给我输出的最后一行,并告诉我最后一行是否是我要查找的消息。如果一切正常,则生成一个换行符,并将其附加到文件。周围的引号确保即使文件名中有空格,一切仍能正常工作。`taildiffgrepecho>>"$i"`"$i"

解决方案 7:

好的,在评论中抱怨之后,我有一个更好的解决方案。首先,您要知道哪些文件缺少换行符:

find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -v 0a$" ';' -print

不是超级快(每个文件调用几个进程),但对于实际使用来说还可以。

现在,当您拥有它时,您也可以添加换行符,以及另一个-exec

find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -v 0a$" ';' -exec sh -c "echo >> {}" ';'

可能存在的陷阱:

  • 如果文件名不好,例如有空格,则可能需要tail -1 "{}"。或者 find 做得对吗?

  • 您可能想要添加更多过滤功能来查找、喜欢-name *py或类似内容。

  • 使用前请考虑可能出现的 DOS/Unix 换行混乱问题(先修复它)。

编辑:

如果您不喜欢这些命令的输出(回显一些十六进制),请添加-q到 grep:

find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -q -v 0a$" ';' -print
find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -q -v 0a$" ';' -exec sh -c "echo >> {}" ';'

解决方案 8:

tail -c1 file | read -r _ || echo >> file

获取文件的最后一个字符read,将其导入,如果在换行符之前遇到 EOF(因此,如果文件的最后一个字符不是换行符),它将以非零退出代码退出。如果read退出非零,则使用将换行符附加到文件上echo(如果read退出 0,则满足||,因此echo命令不会运行)。

摘自http://backreference.org/2010/05/23/sanitizing-files-with-no-trailing-newline/

解决方案 9:

尝试 ex-way:

ex -s +"bufdo wq" *.c

并递归(启用新的通配符选项):

ex -s +"bufdo wq" **/*.c

这相当于vi -es。更改*.c为您兴趣的延伸。

如果不存在ex/,则在保存时会自动添加换行符。vi

解决方案 10:

仅使用 Bash

您可以将命令替换(删除尾随换行符)与此处字符串(附加换行符)结合使用:

   Command Substitution
       Command substitution allows the output of a command to replace the command  name.   There  are  two
       forms:

          $(command)
       or
          `command`

       Bash  performs  the expansion by executing command in a subshell environment and replacing the com-
       mand substitution with the standard output of the command,  with  any  trailing  newlines  deleted.
       Embedded newlines are not deleted, but they may be removed during word splitting.  The command sub-
       stitution $(cat file) can be replaced by the equivalent but faster $(< file).



   Here Strings
       A variant of here documents, the format is:

          [n]<<<word

       The word undergoes brace expansion, tilde expansion, parameter and variable expansion, command sub-
       stitution,  arithmetic expansion, and quote removal.  Pathname expansion and word splitting are not
       performed.  The result is supplied as a single string, with a newline appended, to the  command  on
       its standard input (or file descriptor n if n is specified).

工作原理如下:

cat <<<"$(<inputfile)"

输出到文件:

cat <<<"$(<inputfile)" >outputfile

如果您需要inputfile并且outputfile使用相同的文件名,您有几个选择 - 使用sponge命令,使用更多命令替换保存到临时变量,或保存到临时文件。


使用 Sed

其他人建议使用

sed '$a\' inputfile

它在最后一行不添加任何内容。这很好,但我认为

sed '$q' inputfile

更清晰一些,因为它在最后一行退出。​​或者你可以这样做

sed -n 'p'

它用于-n抑制输出,但是用 将其打印出来p

在任何这些情况下,sed都会修复行并添加换行符,至少对于 GNU 和 BSD sed 而言是这样。但是,我不确定此功能是否由 POSIX 定义。的版本sed可能会跳过没有换行符的行,因为行定义为

零个或多个非 <newline> 字符加上终止 <newline> 字符的序列。

解决方案 11:

使用 awk :

awk &#039;/^$/{f=1}END{ if (!f) {print &quot;
&quot;}}1&#039; inputfile

匹配空白行^$(就像你所做的一样)并设置一个标志。如果末尾没有设置标志,则放置换行符。

注意:这`
是在 OS X 中。可
`用于其他。

解决方案 12:

我很惊讶没有人提到许多简单的文本处理工具(如 Awk)会添加换行符作为副作用。这是一个简单的循环,只有实际添加了换行符时才会覆盖文件。

for f in *; do
    awk 1 &quot;$f&quot; >tmp
    cmp -s tmp &quot;$f&quot; || mv tmp &quot;$f&quot;
done
rm -f tmp

(临时文件显然有点问题。)

IDEone 演示:http://ideone.com/HpRHcx

解决方案 13:

尝试使用viex

ex -scwq foo.txt

或对于多个文件:

vi -es +&quot;bufdo wq&quot; *.txt
ex -s +&quot;bufdo wq&quot; *.txt

如果缺失,则在文件保存时自动在 EOF 处添加 EOL。

要递归地申请某些文件,请使用新的通配符选项( **),例如**/*.txt(enable by shopt -s globstar)。

解决方案 14:

如果它有用,那么对我来说通常这样的方法有效:

printf &quot;%s
&quot; &quot;$(cat file_that_MIGHT_need_a_NL.txt)&quot;

这不是最优雅的解决方案,但是它允许我使用诸如、等工具,sedgrep不仅仅是cat在那里。

当然,任何有效的变量或字符串也应该可以起作用。

年龄变化率

解决方案 15:

find -type f | while read f; do [[ tail -c1 "$f" ]] &amp;&amp; echo >> &quot;$f&quot;; done

我使用find而不是for f in *因为它是递归的并且问题是关于“大量的源文件”。

我出于性能原因使用它while read来代替find -execxargs,它每次都会节省生成 shell 进程。

我利用了反引号运算符返回命令输出“删除所有尾随换行符”的事实man bash,因此对于正确终止的文件,反引号将为空并且将跳过回显。

find | read对操作将会在包含换行符的文件名上失败,但如果需要的话很容易修复:

find -type f -print0 | while read -d $&#039;&#039; f; do [[ tail -c1 "$f" ]] &amp;&amp; echo >> &quot;$f&quot;; done

解决方案 16:

以下是我的 bash 脚本解决方案。它首先检查文件是否为文本文件。然后,如果是文本文件,它使用 tail 和 od(八进制转储)查看最后一个字符是否为换行符。如果不是,则使用 echo 添加换行符:

item=&quot;$1&quot;

if file &quot;$item&quot; | egrep &#039;text&#039; > /dev/null
then
    if ! tail -c 1 &quot;$item&quot; | od -b -A n | egrep &#039;012&#039; > /dev/null
    then
        echo &quot;(appending final newline to ${item})&quot;
        echo >> &quot;$item&quot;
    fi
fi

解决方案 17:

从 dos2unix 7.5.0 开始,您可以使用 -e 或 --add-eol 选项在最后一行添加换行符(前提是它还不存在):

dos2unix -e file.txt

要检查最后一行是否有换行符,请输入

dos2unix -e -ih file.txt

它将打印换行符的类型 (dos/unix/mac) 或 noeol(如果没有)。

解决方案 18:

由于命令本地化 Tim 和 Norman 的回答应使用 'LANG=C' 前缀进行改进,以便有机会将 '无换行符' 模式与具有任何区域参数的每个系统进行匹配

这确保了此脚本命令行上放置的每个文件都有一个空行:

 #!/bin/sh -f
 for i in $* ; do  echo $i; \n if LANG=C diff /dev/null &quot;$i&quot; | tail -1 | \n  grep &#039;^\\ No newline&#039; > /dev/null; then echo >> &quot;$i&quot;; \n fi; done

此脚本检测缺少的文件:

 #!/bin/sh -f
 for i in $* ; do \n if LANG=C diff /dev/null &quot;$i&quot; | tail -1 | \n  grep &#039;^\\ No newline&#039; > /dev/null; then  echo $i; \n fi; done

解决方案 19:

我通过使用dos2unix(或对应物)标志解决了这个任务--newline。优点是这些工具可以自行检测二进制文件。我喜欢使用的解决方案,tail -c1但事先过滤二进制文件对我来说真的慢。

dos2unix --newline my_file.txt

最后,我编写了一个脚本,搜索我的项目目录,将文件(, )之外的所有文件转换为LF( ) ,并使用标志通过一次调用获得正确的换行符。dos2unix`*.cmdCRLFunix2dos`

解决方案 20:

找到这个工具后,我决定自己写一个

这是我的 Python 脚本,用于完成这项工作

它仅将 (\r\n) 附加到文件,而不在文件末尾包含 (\n)

https://github.com/tranhuanltv/append_newline

用法:append_newline.py .c ./projects ./result_dir

如果你想的话,可以提出 Pull 请求

解决方案 21:

pcregrep --recursive --exclude-dir=.git \n  --files-without-match --multiline &#039;
z&#039; . |
  while read k ; do echo >> &quot;$k&quot;; done

这里涉及几个步骤:

  1. 递归查找文件

  2. 检测哪些文件缺少尾随换行符

  3. 循环遍历每个文件

  4. 追加换行符

步骤 1 按照传统方式使用find(遵循 Unix 传统“每个工具只做一件事,并做好它”),但由于 pcregrep 具有内置支持,因此我使用它很舒服。我小心避免弄乱 .git 文件夹。

第 2 步是通过多行正则表达式匹配具有最终换行符的文件并打印匹配的文件的名称来完成的。

步骤 3 使用 while/read 循环而不是 for/in 完成,因为后者无法处理带有空格的文件名和极长的文件列表。

步骤 4 是一个简单的回声,遵循@norman-ramsey 的方法。

h/t @anthony-bush https://stackoverflow.com/a/20687956/577438了解 pcregrep 建议。

解决方案 22:

存在一种使用标准 shell 命令的优雅解决方案:

tail -c 1 file.txt | read || echo >> file.txt    
  1. tail输出文件的最后一个字节

  2. read将一行读入变量。如果未指定变量,则不执行任何操作,但如果 EOF 出现在换行符之前,则以代码 1 退出。

  3. echo仅当读取失败时运行(即如果最后一个字符不是换行符),并将换行符附加到file.txt

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用