Stream command output directly to memory.
Executes the given command via popen() and streams output directly to an allocatable string, eliminating disk I/O.
@param[in] command The shell command to execute @param[out] output The command output (allocated) @param[out] exit_status Exit status of the command @return .true. if successful, .false. on error
character(len=:), allocatable :: output
integer :: status
logical :: success
success = stream_command_output('curl -s https://example.com', output, status)
if (success .and. status == 0) then
print *, 'Output: ', output
end if
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
character(len=*), | intent(in) | :: | command | |||
character(len=:), | intent(out), | allocatable | :: | output | ||
integer, | intent(out) | :: | exit_status |
function stream_command_output(command, output, exit_status) result(success) character(len=*), intent(in) :: command character(len=:), allocatable, intent(out) :: output integer, intent(out) :: exit_status logical :: success type(c_ptr) :: pipe character(len=:), allocatable :: c_command character(len=CHUNK_SIZE), target :: buffer integer(c_size_t) :: bytes_read character(len=:), allocatable :: temp_output integer(c_int) :: close_status integer :: total_bytes character(len=256) :: msg success = .false. exit_status = -1 total_bytes = 0 ! Check if streaming is available if (.not. is_streaming_available()) then call s3_log_debug('Streaming not available on this platform') return end if call s3_log_trace('Streaming command: ' // trim(command)) ! Convert Fortran string to C string (null-terminated) c_command = trim(command) // C_NULL_CHAR ! Open pipe for reading call s3_log_debug('Opening pipe with popen()') pipe = c_popen(c_command, 'r' // C_NULL_CHAR) if (.not. c_associated(pipe)) then call s3_log_error('popen() failed - pipe not associated') return end if call s3_log_debug('Pipe opened successfully, starting to read') ! Initialize output output = '' ! Read data in chunks do bytes_read = c_fread(c_loc(buffer), 1_c_size_t, & int(CHUNK_SIZE, c_size_t), pipe) ! Check if we've reached end of stream or error if (bytes_read <= 0) exit total_bytes = total_bytes + int(bytes_read) write(msg, '(A,I0,A,I0,A)') 'Read ', int(bytes_read), ' bytes (total: ', total_bytes, ')' call s3_log_trace(trim(msg)) ! Append chunk to output if (allocated(temp_output)) deallocate(temp_output) if (len(output) > 0) then allocate(character(len=len(output) + int(bytes_read)) :: temp_output) temp_output = output // buffer(1:bytes_read) else allocate(character(len=int(bytes_read)) :: temp_output) temp_output = buffer(1:bytes_read) end if ! Move temp to output call move_alloc(temp_output, output) end do write(msg, '(A,I0,A)') 'Finished reading ', total_bytes, ' total bytes' call s3_log_debug(trim(msg)) ! Close pipe and get exit status close_status = c_pclose(pipe) write(msg, '(A,I0)') 'pclose() returned: ', close_status call s3_log_debug(trim(msg)) ! pclose returns exit status in platform-dependent way ! On most systems, need to check using WEXITSTATUS-like logic ! For simplicity, treat 0 as success, non-zero as failure if (close_status == 0) then exit_status = 0 success = .true. call s3_log_debug('Stream command completed successfully') else ! Command failed, but we may still have partial output exit_status = close_status success = .false. write(msg, '(A,I0,A,I0,A)') 'Stream command failed with exit status ', & close_status, ' (bytes read: ', total_bytes, ')' call s3_log_error(trim(msg)) end if end function stream_command_output