Hello. That was discovered at http://unix.stackexchange.com/a/331884
Consider this script that modifies itself (and happens not to end in a newline character): $ printf %s 'printf "\necho %s\n" {1..10} >> $0' > script.sh; bash -x ./script.sh + printf '\necho %s\n' 1 2 3 4 5 6 7 8 9 10 + echo 1 1 + echo 2 2 + echo 3 3 + echo 4 4 + echo 5 5 + echo 6 6 + echo 7 7 + echo 8 8 + echo 9 9 + echo 10 10 That's fine so far. Now, if I run an external command instead of printf (like /usr/bin/printf): $ printf %s '/usr/bin/printf "\necho %s\n" {1..10} >> $0' > script.sh; bash -x ./script.sh + /usr/bin/printf '\necho %s\n' 1 2 3 4 5 6 7 8 9 10 + ho 6 ./script.sh: line 2: ho: command not found + echo 7 7 + echo 8 8 + echo 9 9 + echo 10 10 Running bash under strace, we see: read(255, "/usr/bin/printf \"\\necho %s\\n\" {1"..., 43) = 43 read(255, "", 43) = 0 brk(0x12c5000) = 0x12c5000 write(2, "+ /usr/bin/printf '\\necho %s\\n' "..., 53+ /usr/bin/printf '\necho %s\n' 1 2 3 4 5 6 7 8 9 10 ) = 53 rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0 lseek(255, 43, SEEK_CUR) = 86 Note that it's a SEEK_CUR, not SEEK_SET above, so we seek 43 bytes past the end of the file. In gdb breaking on that lseek(): (gdb) bt #0 lseek64 () at ../sysdeps/unix/syscall-template.S:84 #1 0x00005555555c808f in sync_buffered_stream (bfd=<optimized out>) at input.c:554 #2 0x00005555555aeff8 in make_child (command=command@entry=0x555555925808 "/usr/bin/printf \"\\necho %s\\n\" {1..10} >> $0", async_p=async_p@entry=0) at jobs.c:1910 #3 0x0000555555599fcd in execute_disk_command (words=words@entry=0x555555928d88, redirects=0x555555928b08, command_line=command_line@entry=0x55555592c5c8 "/usr/bin/printf \"\\necho %s\\n\" {1..10} >> $0", pipe_in=pipe_in@entry=-1, pipe_out=pipe_out@entry=-1, async=async@entry=0, fds_to_close=0x555555928aa8, cmdflags=0) at execute_cmd.c:5232 #4 0x000055555559ac52 in execute_simple_command (simple_command=0x555555928a48, pipe_in=pipe_in@entry=-1, pipe_out=pipe_out@entry=-1, async=async@entry=0, fds_to_close=fds_to_close@entry=0x555555928aa8) at execute_cmd.c:4429 #5 0x000055555559bcea in execute_command_internal (command=command@entry=0x555555928a08, asynchronous=asynchronous@entry=0, pipe_in=pipe_in@entry=-1, pipe_out=pipe_out@entry=-1, fds_to_close=fds_to_close@entry=0x555555928aa8) at execute_cmd.c:806 #6 0x000055555559dfa2 in execute_command (command=0x555555928a08) at execute_cmd.c:405 #7 0x0000555555585e30 in reader_loop () at eval.c:180 #8 0x00005555555848fa in main (argc=3, argv=0x7fffffffdb78, env=0x7fffffffdb98) at shell.c:792 (gdb) frame 1 #1 0x00005555555c808f in sync_buffered_stream (bfd=<optimized out>) at input.c:554 554 lseek (bp->b_fd, -chars_left, SEEK_CUR); (gdb) p *bp $1 = {b_fd = 255, b_buffer = 0x555555927f08 "", b_size = 43, b_used = 0, b_flag = 1, b_inputp = 43} Here b_used < b_inputp which as far as I understand is not meant to happen. That sync_buffered_stream is meant to seek back to where we're meant to resume reading the script when we've read more than needed, but here b_inputp > b_used would suggest we've processed code that is passed what we've read. Or more likely b_used has incorrectly been set to 0. I stopped looking at that point. -- Stephane