为什么字符串的Base64编码中包含"\n"字符?

$ echo -n "apfjxkic-omyuobwd339805ak:60a06cd2ddfad610b9490d359d605407" | base64
YXBmanhraWMtb215dW9id2QzMzk4MDVhazo2MGEwNmNkMmRkZmFkNjEwYjk0OTBkMzU5ZDYwNTQw
Nw==

输出在Nw==之前有一个换行符。在Linux中生成base64的正确方法是什么?

terminal screenshot


6你确定输出包含换行符,并且不仅仅是你的窗口自动换行吗?这个命令在我的Mac上运行得很好。你用的是哪个操作系统? - Ian
@lan 你也可以从http://www.motobit.com/util/base64-decoder-encoder.asp 上查看。 - Tiina
55RFC 2045定义了Base64,要求每76个字符(最多)后必须换行。你为什么认为你的示例不是正确的方式呢? - MSalters
36@MSalters RFC 4648明确指出了这个问题。实现必须不在基编码数据中添加换行符,除非引用此文档的规范明确指示基编码器在特定字符后添加换行符。 => 根据RFC 4648,这个实现是错误的,只要它声称产生“纯粹”的base64编码输出。有趣的是,GNU base64(是否是你提到的?)man页面专门提到RFC 3548,该文档也默认不进行换行处理,而RFC 4648将其废弃。 - Bob
因为我相信"base64编码不包含空格/制表符/\n",这也是java.util.Base64的实现方式。 - Tiina
4@Bob:RFC对API的稳定性要求稍低一些;一个base64工具不能随意改变输出格式而不影响脚本的运行。 - MSalters
3@MSalters 我不能确定是否存在旧版本,但GNU base64是在2004年编写的,并且据我所知,它一直声称遵循RFC 3548。RFC 3548中包含了相同的“不得添加换行符”的条款。因此,即使最初的实现也是“错误”的。至少,它的实现与文档不匹配。无论如何,你问为什么OP的示例是正确的并引用了一个RFC;我的回答是正确定义了独立的base64的RFC。如果你的答案是“出于历史原因”,那就这样吧,但OP在这里并没有错。 - Bob
2看起来有点乱:https://en.wikipedia.org/wiki/Base64#Implementations_and_history 基本上,Base64与自身不兼容。太好了。 - Oskar Skog
有没有不会默默接受输入中的换行符\n的Base64解码实现?(我希望没有,但谁知道呢...) - marcelm
2在Java中,如果一个Base64字符串包含\n,解码器会抛出java.lang.IllegalArgumentException: Illegal base64 character a的异常。 - Tiina
顺便说一下,在你的例子中,base64的目的不太清楚,因为输入已经是ASCII文本了,所以base64只会不必要地增加数据大小。 - Display Name
虽然这个例子可能是ASCII,但不能完全安全地假设OP所关心的内容总是ASCII。 - kayleeFrye_onDeck
在Java 8及更高版本(自2014年起),java.util.Base64.getMime{Encoder,Decoder}() 分别添加了换行符(CRLF)并接受/移除它们。 - dave_thompson_085
4个回答

尝试:

echo -n "apfjxkic-omyuobwd339805ak:60a06cd2ddfad610b9490d359d605407" | base64 -w 0

man base64中得知:

-w, --wrap=COLS
COLS个字符后换行编码(默认为76)。使用0来禁用换行。

< p > 76成为默认值的一个可能原因是,Base64编码旨在提供一种将二进制文件包含在电子邮件和Usenet帖子中的方法,这是为使用80个字符宽度的显示器的人类设计的。将76个字符宽度作为默认值使得这种用法更加方便。

29哎呀,我总是只用tr来处理这个。好知道还有一种“正确的方法”。 - Score_Under
关于为什么默认值不是零的解释对我来说是一个谜。 - Dherik
4@Dherik 我猜测这是对文本处理工具的礼貌。base64将任意二进制数据编码为文本。通常,希望处理文本的工具逐行读取,并且可能无法处理非常长的行。如果-w 0是默认值,那么默认情况下你将只得到一行文本;如果输入很大,那么这将是一行非常长的文本。最好是默认换行。我认为选择76是因为它比80稍小,而80是终端一种事实上的标准 - Kamil Maciorowski
@KamilMaciorowski 谢谢你提供的信息。每次我使用base64命令时,都需要添加-w 0参数(如果忘记了,可能会发生奇怪的事情...),所以这个默认行为对我来说非常奇怪。 - Dherik
3一个可能的原因是,Base64编码是为了在电子邮件和usenet帖子中包含二进制文件,而这些内容是为使用80个字符宽度的显示器的人类用户设计的。将默认宽度设置为76个字符使得这种用法更加便捷。 - Thorbjørn Ravn Andersen
@ThorbjørnRavnAndersen 现在答案已经变成了社区维基,并且您的评论已被整合进去。谢谢您。 - Kamil Maciorowski
请注意,MacOS系统没有-W选项,但是有-b选项。 - Thorbjørn Ravn Andersen
@ThorbjørnRavnAndersen 注意,您可以编辑答案并进行改进,尤其是如果它是社区维基的话。 - Kamil Maciorowski
@KamilMaciorowski 是的。我可以留下评论。根据我的自由裁量权。你可以根据那个来随意编辑你的回答。 - Thorbjørn Ravn Andersen
完美 -n 解决了我的问题。对我来说,罪魁祸首是 echo 命令在管道到 base64 之前附加了一个换行符。 - Phil_1984_

在一些系统上,例如Alpine Linux、Arch Linux的initramfs hook等,没有base64命令的-w选项。在这种情况下,您可以手动处理base64的输出结果:
base64 some_file.txt | tr -d \\n

这是一种粗暴的方法;而不是让程序合作,我使用来无差别地去除stdout上的每个换行符。

3当有锤子可用时,始终使用锤子。 - dna
2以下内容对于“修正”一个base64编码的值非常有用:echo 'dGVzdAo=' | base64 -d | tr -d \\n | base64 - Aubrey Lavigne
不是低劣,只是一种不同而且通常是唯一的方法。就像你所说的,有很多系统没有-w选项,包括MacOSX。 - nnsense

对于使用 openssl base64 的任何人,您可以使用 -A 标志:

 -A                 Process base64 data on one line (requires -a)
 -a                 Perform base64 encoding/decoding (alias -base64)

以下方法对我有效:
echo -n '{string}' | openssl base64 -A

所以请在重定向到base64之前使用echo -n来删除换行符;并且使用base64 -w 0来防止base64本身将换行符添加到输出中。
me@host:~ 
$ echo -n mypassword | base64 -w 0
bXlwYXNzd29yZA==me@host:~ # <<<<<<<<<<<<<<< notice that no line break added after "==" due to '-w 0'; so me@host is on the same line
$ echo -n 'mypassword' | base64 -w 0
bXlwYXNzd29yZA==me@host:~ # <<<<<<<<<<<<<<<<<< notice adding single quotes does not affect output, so you can use values containing spaces freely

使用od -c来显示实际字符是一种很好的验证方法。
me@host:~ 
$ echo -n bXlwYXNzd29yZA== | base64 -d | od -c
0000000   m   y   p   a   s   s   w   o   r   d
0000012

你看到没有添加"\n"。但是如果你使用没有"-n"的"echo",od会显示"\n":
me@host:~ 
$ echo mypassword | base64 -w 0
bXlwYXNzd29yZAo=me@host:~ 
$ echo bXlwYXNzd29yZAo= | base64 -d | od -c
0000000   m   y   p   a   s   s   w   o   r   d  \n
0000013

在最后,创建一个函数将来会帮助你:
base64-encode() {
    if [ -z "$@" ]; then
        echo "Encode string with base64; echoing without line break, and base64 does not print line break neither, to not introducing extra chars while redirecting. Provide the string to encode. "
        return 1
    fi
    echo -n "$@" | base64 -w 0  # here I suppose string if containing space is already quoted
}