Bash:将“cat”重定向到文件,不带换行符

7
我有两个文件,我正在将它们连接成一个单独的文件,但我也在两个文件之间添加用户输入。问题是,在第一个文件后插入了一个换行符,其余部分按预期工作。
touch newFile
cat file1 > newFile
echo -n $userInput >> newFile
cat file2 >> newFile

如何在将file1添加到newFile时防止或移除换行符? 如果我使用cat file1,似乎会有一个由cat添加的换行符,但是我看到的关于cat的所有内容都说它不会这样做。如果我使用vim file1,文件末尾没有空白行,这表明换行符是文件的一部分,因此cat实际上可能正在添加一个换行符,或者重定向符号>正在这样做,或者echo在其输出开头添加了一个换行符,而这些情况都不希望发生在这种情况下。我看到的一个解决方案是使用

cat file1 | tr -d '\n'

但是这会丢弃文件中的所有换行符,也不理想。因此,重申我的问题:

我如何将file1复制到新文件中并添加用户输入,而不在它们之间添加换行符?

cat不是必需品,但我不熟悉printf,所以如果那是解决方案,请详细说明其用途)。


printf '%s' "$userInput"echo -n "$userInput"更可靠;如果您查看POSIX规范中的echo,您会注意到-n使行为实现定义,并且标准的APPLICATION USAGE部分建议使用printf代替任何带有反斜杠转义序列的echo -n或任何echo调用。 - Charles Duffy
不确定为什么这个问题被投票否决了,很明显我当时有一些误解,这些误解即使进行了合理的额外研究也不会变得清晰,而且我对自己的问题进行了足够的研究,发现目前已有的答案对我的具体问题来说是不够的。通过提问这个问题,我学到了很多新东西,因此它达到了它的目的。@CharlesDuffy感谢你的帮助和详细的解释!我不是完全的BASH/UNIX新手,但还有很多东西需要学习。 - JWAspin
3个回答

5

使用以下输入:

userInput="Test Test Test"

echo "Line 1
Line 2
Line 3" >file1

echo "Line 4
Line 5
Line 6" >file2

我会做:

我会做:

printf "%s%s%s" "$(cat file1)" "$userInput" "$(cat file2)" >newfile

>newfile的创建相当于使用touch命令,并在第一步添加内容。这样更容易理解意图。

我明白:

$ cat newfile
Line 1
Line 2
Line 3Test Test TestLine 4
Line 5
Line 6

我猜你想在格式字符串中使用第三个%s,而不是$s?不过printf '%s' "$(cat file1)" "$userInput" "$(cat file2)"也会产生相同的效果。 - Charles Duffy
@CharlesDuffy:是的,谢谢,但奇怪的是它仍然可以工作! - dawg
1
当变量为空时,它会像%s%s%s一样工作-- 在占位符用完时重复格式化字符串。当s不为空时,事情就会变得有趣。 :) - Charles Duffy
太棒了,这绝对是一个更简洁的解决方案,谢谢! - JWAspin

3

和其他Unix工具一样,Vim将\n视为行终止符而不是行分隔符。

这意味着在最后一段文本之后的换行符将被视为最后一行的一部分,并且不会显示额外的空行。

如果没有尾随的换行符,则加载文件时Vim将在状态栏中显示[noeol]

foo
~
~
~
~
~
"file" [noeol] 1L, 3C            1,1           All
        ^---- Here

所以,换行符绝对是您的文件的一部分,而不是由bash添加的任何方式。
如果您想剥离所有尾随回车符,可以通过命令展开的副作用来实现此目的:
printf '%s' "$(<file1)" >> newfile

这解释了很多,谢谢!我没有意识到VIM会像那样“隐藏”换行符,所以我的问题基于对换行符来源的误解。 - JWAspin
我在这个答案和我选择的那个答案之间犹豫,但是另一个答案有更优雅的解决方案,但如果没有这个答案,我仍然会对整个VIM的事情感到困惑。SO应该添加一个功能,可以选择多个答案组合作为解决方案 :) - JWAspin

0
touch newFile
echo -n "$(cat file1)" > newFile
echo -n $userInput >> newFile
cat file2 >> newFile

搞定了。


三个反引号是GitHub对Markdown的补充--它们不是基本语法的一部分,也不被StackOverflow解析器所支持。使用由“{}”按钮创建的四个空格缩进进行多行代码格式化;这也启用了语法高亮,而反引号包围的内容则没有。 - Charles Duffy
这里讲的是针对内容而非形式进行操作,你实际上是在删除文件中原有的一行;这是因为 $(...) 会从 ... 的输出中移除最后一个换行符 -- 所以你的输出比输入文件少了几个换行符;你并不是在传递内容时没有 添加 换行符,而是在这样做时 主动删除 了一个换行符。 - Charles Duffy
(此外,您肯定想要"$userInput"而不是$userInput,除非您希望将*替换为在运行代码的目录中的文件名列表;请参见[BashPitfalls#14](http://mywiki.wooledge.org/BashPitfalls#echo_.24foo))。 - Charles Duffy
@CharlesDuffy,再次感谢您的所有解释。我在Slack上花了很多时间,以至于我已经习惯性地使用三个反引号。现在更明白为什么"$(...)"实际上是这样工作的了。在VIM“隐藏”换行符和$(...)删除该换行符之间,我对所见行为感到非常困惑。感谢您解释它的工作原理!(现在,是时候真正学习printf了,显然这是我应该知道的东西) - JWAspin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接