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