#!/bin/perl # # shell_seek --help # shell_seek fd [offset] [whence] # shell_seek fd whence [offset] # # Take the open file descriptor 'fd' inherited from the calling shell # and seek() to the offset given, relative to the location 'whence'. # Other file handle functions are also available. # # Whence can be a word or a number (word is preferred)... # 0 start seek() to offset relative to the start of file (default) # 1 current seek() offset relative to current position # 2 end seek() offset relative to end of file (append) # # Both offset and whence defaults to 0. If whence is a word (which is # recommended usage), the offset may be omitted, or place before the offset. # But if a number if used for whence, whence must be the second argument. This # is for compatibility to a C program of the same that was originally presented # online. # # There are also some special words # tell Print current file offset for the FD (using tell()) # truncate Set the files length to the given offset (using truncate()) # shutdown {rd,wr,both} # shutdown() one side of of a /dev/tcp file descriptor. # Typically the write side is shutdown by a network cleint # to incidate end of communiction, while read remains active # for any final status from the server side. # ### # # Examples... # # exec 3<>some_file # Open a file for both read and write # # shell_seek 3 # Rewind FD 3 to start of file (offset 0) # shell_seek 3 start # ditto but clearer (offset 0) # shell_seek 3 start 0 # ditto but more exact # shell_seek 3 5 # Seek FD 3 to the 5th byte into the file # shell_seek 3 5 start # ditto # shell_seek 3 start 5 # ditto # shell_seek 3 end # Jump FD 3 to the end of file # shell_seek 3 -20 end # Jump FD 3 to 20 bytes before the end of file # shell_seek 3 10 current # Move 10 bytes forward # shell_seek 3 current -5 # Move back 5 bytes # # exec 3>&- # Close the file (both read and write) # # Using the special values... # # shell_seek 3 tell # Report our location in the file # shell_seek 3 2048 truncate # Make file 2 kilobytes long (sparse file) # shell_seek 3 truncate # Empty the file (zero length) # shell_seek 3 shutdown wr # Shutdown the write side of a network link # # (EG: indicate EOF from a network client) # # Other shell commands.... # echo "string" >&3 # Add the "string\n" to the file at current location # read -d 3 -r line # Read from current location to EOL # # The FD will be moved to start of the next line # cat /dev/fd/3 # Print the file from start to end, leave FD as is. # # NOTE: MacOSX 'cat' will print from current position. # cat <&3 # Print the file from current location to end. # # The FD is moved to the end of the file # ##### # From a discussion "Bash read-write file descriptors seek to start of file" # https://stackoverflow.com/questions/3838322 # # Anthony Thyssen, 6 Oct 2022 # use strict; use FindBin; my $PROGNAME = $FindBin::Script; sub Usage { print STDERR "$PROGNAME: ", @_, "\n" if @_; @ARGV = ( "$FindBin::Bin/$PROGNAME" ); # locate script file while( <> ) { next if $. == 1; last if /^###/; next if /^##/; last unless /^#/; s/^#$//; s/^# //; print STDERR; } exit 10; } sub Help { @ARGV = ( "$FindBin::Bin/$PROGNAME" ); # locate script file while( <> ) { next if $. == 1; last if /^#####/; next if /^##/; last unless /^#/; s/^#$//; s/^# //; print STDERR; } exit 10; } # There are no real options, so any open is like --help Help() if $ARGV[0] =~ /^-/; my ($fd, $offset, $whence) = @ARGV; Usage("FD must be a positive number") unless $fd =~ /^\d+$/; # --------------- # Special handling (swap order if offset is a word) if ( $offset =~ /^[a-zA-Z]+$/ && $whence ne 'shutdown' ) { ($whence, $offset) = ($offset, $whence) } $offset = 0 unless length($offset); $whence = 0 unless length($whence); if ( $whence eq 'shutdown' ) { $offset = 0 if $offset eq 'rd'; $offset = 1 if $offset eq 'wr'; $offset = 2 if $offset eq 'rdwr'; $offset = 2 if $offset eq 'both'; } Usage("Offset must be a number, may be negative") unless $offset =~ /^-?\d+$/; if ( $whence =~ /^[a-zA-Z]+$/ ) { my $n = 0; my %words = map { $_ => $n++ } qw( start current end tell truncate shutdown ); $whence= defined $words{$whence} ? $words{lc($whence)} : 99; } Usage("Offset must be positive for a whence of 'start' or '0'") if $offset < 0 && $whence == 0; open(FD,">&$fd") || Usage("FD is Invalid: $!"); if ( $whence <= 2 ) { seek(FD,$offset,$whence); } elsif ( $whence == 3 ) { print tell(FD),"\n"; } elsif ( $whence == 4 ) { truncate(FD,$offset); } elsif ( $whence == 5 ) { shutdown(FD,$offset); # Experimental } else { Usage("Invalid Whence Arguemnt"); }