为什么在 linux x86_64 (nasm) 上 write 系统调用在末尾打印 `%`?

Pan*_*nda 2 linux zsh x86-64 nasm system-calls

以下 hello-world 程序%在打印字符串的末尾显示一个符号。这是为什么?我该如何删除它?

这是我的程序:

section .data
    msg db  "hello, world!"

section .text
    global _start
_start:
    mov rax, 1      ; syscall 1 (write)
    mov rdi, 1      ; arg 1 = 1 (stdout)
    mov rsi, msg    ; arg 2 = msg ("hello, world!")
    mov rdx, 13     ; arg 3 = 13 (char count)
    syscall         ; call write
    mov rax, 60     ; syscall 60 (exit)
    mov rdi, 0      ; arg 1 = 0 (OK)
    syscall         ; call exit
Run Code Online (Sandbox Code Playgroud)

这是我运行可执行文件时的输出:hello, world!%

提前致谢。

编辑:这似乎是由 zsh 引起的(在 bash 中无法重现)。问题是为什么会发生这种情况以及如何解决它。

Pet*_*des 5

发生这种情况是因为程序的输出不以换行符结尾。


您的程序没有打印%. 您可以通过将其输出传输到或类似的方式来验证这一点hexdump -C

您还可以用来strace跟踪系统调用的内容,但这不会显示输出因此不排除内核神奇地添加%. (但是你可以确定内核不会这样做。唯一可能的修改是提前write返回,而不写入完整的缓冲区。这只有在缓冲区大小很大的情况下才可能发生,尤其是在写入带有缓冲区的管道时大于管道缓冲区(可能是 64kiB)。


这是针对部分行的 ZSH 功能: 为什么 ZSH 以突出显示的百分比符号结束行?。你没有说这是一个突出显示的%符号。准确/完整的描述很重要。

(此答案的早期版本假设您%在 ZSH 中用作提示符。 %有时用作提示符,但更常见的是在 tcsh 等 C-shell 中,而不是 bash 和 zsh 等 Bourne 派生的 shell 中。)

你会得到完全相同的东西echo -n "hello, world!"。例如在 bash 中:

peter@volta:/tmp$ echo -n 'hello world!'
hello world!peter@volta:/tmp$ 
Run Code Online (Sandbox Code Playgroud)

在ZSH中:

volta:/tmp$ echo -n 'hello world!' 
hello world!%
volta:/tmp$      # and the % on the previous line is in inverse-video
Run Code Online (Sandbox Code Playgroud)

Bash 只是打印$PS1(或运行$PROMPT_COMMANDBash 仅在前台命令退出后它无法直接检查程序的输出是否以换行符结束。我猜想 ZSH 使用 VT100 转义码来查询光标位置,以检测程序将光标不在行首的情况。

cat当您的文件不以换行符结尾或任何其他情况时,您也会遇到这种情况。


要修复此问题,请在消息中包含换行符(ASCII 换行符,0xa)

section .rodata
    msg: db  "hello, world!", 0xa
    msglen equ $ - msg               ; use mov edx, msglen
    msgend:                          ; or  mov edx, msgend - msg
Run Code Online (Sandbox Code Playgroud)

请参阅$ 在 NASM 中到底是如何工作的?有关让汇编器为您计算大小的更多详细信息。

不要省略:NASM 中的数据标签;它的风格很好,避免了与指令助记符和汇编指令的名称冲突。

NASM(但不是 YASM)接受反引号内的 C 风格转义符,因此您可以编写而不是换行符的数字 ASCII 代码

  msg: db  `hello, world!\n`
Run Code Online (Sandbox Code Playgroud)