0

I saw an interesting 'read' loop in an init.d script for CentOS that can be boiled down to essentially this structure:

cat "somefile" | while read var1 var2; do
    # do something with vars 1 and 2
done 3<&1

I experimentally took off the "3<&1" redirect and nothing changed in the execution or behavior... What does the final redirect "3<&1" achieve, and why is it done specifically at the end of the loop?

Below you'll find full init script, it's for Gazzang's zNcrypt service that handles key management for encrypted filesystems. The part I'm interested in occur towards the end of the 'start' and 'stop' cases.

#! /bin/sh
#
# zncrypt       This script mount and umount all zncrypt directories
#
# chkconfig: - 64 36
# description:  zNcrypt start script.

. /etc/rc.d/init.d/functions

if [ -r /usr/lib/zncrypt/zncrypt.functions ]; then
        . /usr/lib/zncrypt/zncrypt.functions
else
        echo "/usr/lib/zncrypt/zncrypt.functions: File does not exist."
        exit 0
fi

ZNCRYPT_LOG_DIR="/var/log/zncrypt"
ZNCRYPT_LOG_ACCESS_FILE=$ZNCRYPT_LOG_DIR"/access.log"

# create zncrypt log directory
mkdir -p "$ZNCRYPT_LOG_DIR"

# create access log file for the kernel module
touch "$ZNCRYPT_LOG_ACCESS_FILE"

case "$1" in
start)
        echo "Starting zNcrypt directories"
        egrep -v "^[[:space:]]*(#|$)" "$ZTABFILE" | while read mnt src type opts; do
                if ! df "$mnt" | grep "$mnt$" >/dev/null; then
                        action $" * Mounting $src ... " do_mount "$src" "$mnt" "$type" "$opts" < /dev/tty
                fi
        done 3<&1
;;
stop)
        echo "Stopping zNcrypt directories"
        egrep -v "^[[:space:]]*(#|$)" "$ZTABFILE" | while read mnt src type opts; do
                if df "$mnt" | grep "$mnt$" >/dev/null; then
                        action $" * Umounting $src ... " do_umount "$mnt"
                fi
        done 3<&1

        if /sbin/lsmod | grep ^zncryptfs &>/dev/null; then
                action $" * Unloading module ... " /sbin/rmmod zncryptfs 2>/dev/null && rm /dev/zncrypt 2>/dev/null
        fi
;;
status)
        show_status
;;
restart)
        /bin/bash $0 stop
        sleep 1
        /bin/bash $0 start
;;
reload|force-reload)
;;
force-start)
;;
*)
        echo "Usage: `basename $0` {start|stop|status|restart}"
        exit 1
        ;;
esac
Eternal Rubyist
  • 3,445
  • 4
  • 34
  • 51

1 Answers1

1

3<&1 tells the bash shell to redirect anything coming from stdout (file descriptor 1) to file descriptor 3. File descriptor 3 would correspond to some file or device opened in the context of the cat/while construct. See this article on standard file descriptors. Also see this related post.

Community
  • 1
  • 1
lurker
  • 56,987
  • 9
  • 69
  • 103
  • If the loop doesn't call any other commands, would file descriptor 3 refer to file read by the "read" command? It doesn't look like the contents of the loop are calling any further commands/functions that open file descriptors... Alternatively is there a way to display what file is currently assigned to file descriptor 3? – Eternal Rubyist Aug 23 '13 at 13:57
  • @nomizzz, perhaps it would help if you could show the contents of the loop in your original problem statement. You might be able to tell which process has 3 open using `lsof -d3` but by the time the loop is done, it may be closed. – lurker Aug 23 '13 at 14:43
  • Thanks for the suggestion, but lsof -d3 shows a few too many different instances of FD3 open for me to narrow them down... I've updated my question with the contents of the full script. – Eternal Rubyist Aug 23 '13 at 15:50
  • @nomizzz yes, indeed, I thought `lsof` might produce a lot of output. But if you scan through the list, perhaps there will be an identifiable process related to something in your loop. – lurker Aug 23 '13 at 15:51
  • Only 2 of the processes listed by lsof are being run by a bash, a pipe that's currently (active maybe corresponding to the 'read' command as hypothesized) and one to the /dev/pts/19 which would be the terminal slave itself... Perhaps I'm missing something because I would assume writing stdout to a pipe or terminal slave (pts) wouldnt sere any purpose... – Eternal Rubyist Aug 23 '13 at 18:08