assembly 如何在Linux汇编语言中正确地从STDIN中读取文件名?

4xrmg8kj  于 2023-03-18  发布在  Linux
关注(0)|答案(1)|浏览(141)

我正在阅读Jonathan Bratlett的《从头开始编程》,这本书教x86处理器的汇编语言和使用GCC工具集的Linux操作系统。
在第7单元介绍错误处理的内容时,我们要求修改一个现有的程序,为程序添加一个恢复机制,以便在无法打开程序中已经硬编码的标准文件时,允许程序从STDIN读取。
这里的问题是用户从STDIN输入的文件名在默认情况下**在末尾附加了\n。因此程序找不到要读取的文件。
我必须
在程序中手动将\n替换为0才能使其正常工作。**而且感觉这不是处理此情况的正确方法。我该如何正确地解决此问题?
PS:这是我第一次在这里提问,如果我有什么可以改进的地方,请告诉我。谢谢。
下面是代码:

.include "consts/linux.s"
.include "consts/record-def.s"

.section .data
input_filename:
    .ascii "wrongtest.dat\0"
output_filename:
    .ascii "testout.dat\0"

.section .bss
.lcomm record_buffer, RECORD_SIZE

.section .text
.equ ST_INPUT_DESCRIPTOR, -4
.equ ST_OUTPUT_DESCRIPTOR, -8

.globl _start
_start:
    movl %esp, %ebp
    subl $8, %esp

    movl $SYS_OPEN, %eax
    movl $input_filename, %ebx
    movl $0, %ecx
    movl $0666, %edx
    int $LINUX_SYSCALL
    movl %eax, ST_INPUT_DESCRIPTOR(%ebp)    #Input descriptor.

    cmpl $0, %eax
    jg continue_processing
    .section .data
    no_open_error_code:
        .ascii "0001\0"
    no_open_error_message:
        .ascii "Could not open the file\0"

    .section .text
    pushl $no_open_error_message
    pushl $no_open_error_code
    call error_exit
    addl $8, %esp

    movl $SYS_READ, %eax
    movl $STDIN, %ebx
    movl $record_buffer, %ecx
    movl $RECORD_SIZE, %edx
    int $LINUX_SYSCALL

    ###Manually replace last byte of the filename with 0###
    #decl %eax
    #movb $0, record_buffer(,%eax,1)

    movl $SYS_OPEN, %eax
    movl $record_buffer, %ebx
    movl $0, %ecx
    movl $0666, %edx
    int $LINUX_SYSCALL
    movl %eax, ST_INPUT_DESCRIPTOR(%ebp)    #Recovery input descriptor.

    continue_processing:
    movl $SYS_OPEN, %eax
    movl $output_filename, %ebx
    movl $0101, %ecx
    movl $0666, %edx
    int $LINUX_SYSCALL
    movl %eax, ST_OUTPUT_DESCRIPTOR(%ebp)   #Output descriptor.

    start_inc_age_loop:
        pushl ST_INPUT_DESCRIPTOR(%ebp)
        pushl $record_buffer
        call read_record
        addl $8, %esp   

        cmpl $RECORD_SIZE, %eax
        jne end_inc_age_loop
        incl record_buffer + RECORD_AGE

        pushl ST_OUTPUT_DESCRIPTOR(%ebp)
        pushl $record_buffer
        call write_record
        addl $8, %esp
        jmp start_inc_age_loop
    end_inc_age_loop:
        movl %ebp, %esp
        movl $SYS_EXIT, %eax
        movl $0, %ebx
        int $LINUX_SYSCALL
yks3o0rb

yks3o0rb1#

需要手动处理终端/文件输入中的换行符是正常的。read系统调用只给予您访问原始字节流,没有解析。
这就是为什么通常从命令行参数(如cat foo.txt)中获取文件名,而不是从stdin中获取文件列表。
为什么在Unix shell编程中,你一定要避免从文本流中解析文件名:https://unix.stackexchange.com/questions/128985/why-not-parse-ls-and-what-to-do-instead-以及为什么像find -print0xargs -0这样使用\0作为分隔符的东西存在。
\n是一个可以出现在文件名中的法律的字符,因此从stdin中安全/明确地解析文件名的唯一方法是用一个不能出现在文件名中的字节0(C字符串终止符)将它们分隔开(或者使用某种具有明确长度的格式,这样您就可以知道接下来的123个字符都是文件名,而不管它们是什么)。
对于单个文件名,您也可以期望用户在文件名末尾结束输入,例如通过按EOF字符提交TTY输入(默认情况下,运行control-D,运行stty显示终端模式)。
然后,您可以直接将read()的结果作为C字符串使用到一个已经清零的缓冲区中。(只是在终端不处于原始模式的情况下,仍然不能完全处理文件名中的换行符;用户输入一个换行符将导致终端输入被提交,即read()将返回。2然而,用户可以通过使用control-V使下一个字符为“literal”来解决这个问题,让他们按^Venter来输入一个literal换行符,而不用在“cooked”规范模式下使用TTY提交输入。3自己尝试在终端上输入catstrace cat > /dev/null,沿着在非空行上键入control-D(read返回非零)与在空行上(或在前一个control-D之后)键入control-D以使read返回零(即EOF)时发生的情况)

相关问题