Search code examples
bashshellfile-descriptorio-redirection

Not getting expected error while trying to redirect an input file descriptor from output file descriptor


I am new to shell-scripting and while learning I came across io-redirection & file descriptors. I was reading this document and under the topic Duplicating file descriptors I came across this

[n]<&word

If the digits in word do not specify a file descriptor open for input, a redirection error occurs.

I wanted to test it in my pc so I ran the following simple script

#! /bin/bash
exec 8>'./file.txt'
exec 7<&8
echo 'This should not be printed' >&7
ls -l /proc/$$/fd
exec 7<&-
exec 8>&-

and I expected it to give out an error while duplicating fd7 from fd8 as fd7 was being opened for input from an output file descriptor. Instead of giving an error fd7 was opened with write permissions to the same file fd8 pointed and even printed the echo command onto the file!!

Here is the output on the terminal

total 0
lrwx------ 1 dhruv dhruv 64 Dec 16 12:58 0 -> /dev/pts/0
lrwx------ 1 dhruv dhruv 64 Dec 16 12:58 1 -> /dev/pts/0
lrwx------ 1 dhruv dhruv 64 Dec 16 12:58 2 -> /dev/pts/0
lr-x------ 1 dhruv dhruv 64 Dec 16 12:58 255 -> '/home/fileDescriptors.sh'
l-wx------ 1 dhruv dhruv 64 Dec 16 12:58 7 -> '/home/file.txt'
l-wx------ 1 dhruv dhruv 64 Dec 16 12:58 8 -> '/home/file.txt'

Here is the file.txt

This should not be printed

Can anyone help me on what I am missing, maybe there are changes required in the terminal or any other thing.


Solution

  • It appears the descriptor is not checked as the document says, it's just blindly copied. Here's a variation on the script that uses lsof to get more details about the FDs:

    #! /bin/bash
    exec 8>'./file.txt'
    echo "After opening for write on unit 8:"
    lsof file.txt
    
    exec 7<&8
    echo "After copying to unit 7:"
    lsof file.txt
    
    echo 'This should not be printed' >&7
    read -u7    # this should fail
    
    exec 7<&-
    exec 8>&-
    

    And the results (under bash v5.0.3):

    After opening for write on unit 8:
    COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    t1      27615   pi    8w   REG  179,2        0 3169 file.txt
    After copying to unit 7:
    COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    t1      27615   pi    7w   REG  179,2        0 3169 file.txt
    t1      27615   pi    8w   REG  179,2        0 3169 file.txt
    ./t1: line 11: read: read error: 7: Bad file descriptor
    

    Note that the FD column lists w (open for write) for both FDs, rather than r (open for read) or u (both). Also, file.txt is successfully written, as you saw.

    As stated by @CharlesDuffy in comments, the reason for the blind copying is 3>&4 and 3<&4 are both implemented the exact same way, with an fdup2() call, not an open() call. Thus, the copy retains the exact same flags the descriptor it's copied from had, including whether it's opened for read or write.