आप अतिरिक्त फ़ाइल डिस्क्रिप्टर का उपयोग कब करेंगे?


74

मुझे पता है कि आप एक फाइल डिस्क्रिप्टर बना सकते हैं और इसका आउटपुट रीडायरेक्ट कर सकते हैं। जैसे

exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.

लेकिन आप फ़ाइल डिस्क्रिप्टर के बिना भी यही काम कर सकते हैं:

FILE=/tmp/foo
echo a > "$FILE"

जब आप एक अतिरिक्त फ़ाइल डिस्क्रिप्टर का उपयोग करना होगा, तो मैं एक अच्छा उदाहरण ढूंढ रहा हूं।

जवाबों:


50

अधिकांश आदेशों में एक एकल इनपुट चैनल (मानक इनपुट, फ़ाइल डिस्क्रिप्टर 0) और एक एकल आउटपुट चैनल (मानक आउटपुट, फ़ाइल डिस्क्रिप्टर 1) है या फिर कई फ़ाइलों पर काम करते हैं जो वे खुद से खोलते हैं (इसलिए आप उन्हें एक फ़ाइल नाम पास करते हैं)। (यह मानक त्रुटि (fd 2) के अलावा है, जो आमतौर पर उपयोगकर्ता के लिए सभी तरह से फ़िल्टर करता है।) हालांकि यह कभी-कभी एक कमांड के लिए सुविधाजनक होता है जो कई स्रोतों से या कई लक्ष्यों के लिए फ़िल्टर के रूप में कार्य करता है। उदाहरण के लिए, यहां एक सरल स्क्रिप्ट है जो सम-संख्या वाले लोगों से एक फ़ाइल में विषम संख्या वाली रेखाओं को अलग करती है

while IFS= read -r line; do
  printf '%s\n' "$line"
  if IFS= read -r line; then printf '%s\n' "$line" >&3; fi
done >odd.txt 3>even.txt

अब मान लीजिए कि आप विषम संख्या वाली रेखाओं और सम-संख्या वाली रेखाओं के लिए एक अलग फ़िल्टर लागू करना चाहते हैं (लेकिन उन्हें वापस एक साथ नहीं रखा जाता है, तो यह एक अलग समस्या होगी, जो सामान्य रूप से शेल से संभव नहीं है)। शेल में, आप केवल कमांड के मानक आउटपुट को दूसरे कमांड पर पाइप कर सकते हैं; किसी अन्य फ़ाइल डिस्क्रिप्टर को पाइप करने के लिए, आपको इसे पहले 1 fd पर पुनर्निर्देशित करना होगा।

{ while  done | odd-filter >filtered-odd.txt; } 3>&1 | even-filter >filtered-even.txt

एक और, सरल उपयोग मामला एक कमांड के त्रुटि आउटपुट को फ़िल्टर कर रहा है

exec M>&Nस्क्रिप्ट के शेष हिस्से के लिए एक फ़ाइल डिस्क्रिप्टर को फिर से रीडायरेक्ट करता है (या जब तक कि ऐसा कोई अन्य कमांड फ़ाइल डिस्क्रिप्टर को फिर से नहीं बदलता है)। के बीच कार्यक्षमता में कुछ ओवरलैप है exec M>&Nऔर somecommand M>&N। यह execफ़ॉर्म अधिक शक्तिशाली है कि इसमें निहित नहीं है:

exec 8<&0 9>&1
exec >output12
command1
exec <input23
command2
exec >&9
command3
exec <&8

अन्य उदाहरण जो ब्याज के हो सकते हैं:

और भी अधिक उदाहरणों के लिए:

PS यह एक आश्चर्यजनक सवाल है जो साइट पर सबसे अधिक पोस्ट किए गए लेखक से आ रहा है जो fd 3 के माध्यम से पुनर्निर्देशन का उपयोग करता है !


मैं यह कहूंगा कि "अधिकांश कमांड में सिंगल या डबल आउटपुट चैनल है - stdout (fd 1) और बहुत बार stderr (fd 2)"।
rozcietrzewiacz

इसके अलावा, क्या आप बता सकते हैं कि आप इसका उपयोग क्यों करते हैं while IFS= read -r line;? जिस तरह से मैं इसे देखता हूं, यहां IFS का कोई प्रभाव नहीं है क्योंकि आप केवल एक चर ( लाइन ) के लिए मूल्य प्रदान करते हैं । इस प्रश्न को देखें।
rozcietrzewiacz

@rozcietrzewiacz मैंने stderr का उल्लेख किया है, और मेरे उत्तर के पहले भाग को देखें कि क्यों IFSआप एक एकल चर में पढ़ रहे हैं (भले ही यह प्रमुख व्हाट्सएप को बनाए रखने के लिए है) इससे फर्क पड़ता है।
गिल्स

क्या आप ऐसा नहीं कर सकते sed -ne 'w odd.txt' -e 'n;w even.txt'?
वाइल्डकार्ड

1
@Wildcard आप अन्य उपकरणों के साथ भी ऐसा ही कर सकते हैं, निश्चित रूप से। लेकिन इस उत्तर का लक्ष्य शेल में पुनर्निर्देशन को चित्रित करना था।
गाइल्स

13

यहां बैश स्क्रिप्ट चैटटीनेस कंट्रोल के रूप में अतिरिक्त एफडी का उपयोग करने का एक उदाहरण दिया गया है:

#!/bin/bash

log() {
    echo $* >&3
}
info() {
    echo $* >&4
}
err() {
    echo $* >&2
}
debug() {
    echo $* >&5
}

VERBOSE=1

while [[ $# -gt 0 ]]; do
    ARG=$1
    shift
    case $ARG in
        "-vv")
            VERBOSE=3
        ;;
        "-v")
            VERBOSE=2
        ;;
        "-q")
            VERBOSE=0
        ;;
        # More flags
        *)
        echo -n
        # Linear args
        ;;
    esac
done

for i in 1 2 3; do
    fd=$(expr 2 + $i)
    if [[ $VERBOSE -ge $i ]]; then
        eval "exec $fd>&1"
    else
        eval "exec $fd> /dev/null"
    fi
done

err "This will _always_ show up."
log "This is normally displayed, but can be prevented with -q"
info "This will only show up if -v is passed"
debug "This will show up for -vv"

8

नामित पाइप (पंद्रह) के संदर्भ में एक अतिरिक्त फ़ाइल डिस्क्रिप्टर का उपयोग गैर-अवरुद्ध पाइपिंग व्यवहार को सक्षम कर सकता है।

(
rm -f fifo
mkfifo fifo
exec 3<fifo   # open fifo for reading
trap "exit" 1 2 3 15
exec cat fifo | nl
) &
bpid=$!

(
exec 3>fifo  # open fifo for writing
trap "exit" 1 2 3 15
while true;
do
    echo "blah" > fifo
done
)
#kill -TERM $bpid

देखें: स्क्रिप्ट में समय से पहले बंद नामांकित पाइप?


1
आपने मेरे पुराने प्रश्नों में से एक को खोद दिया :) चाड सही है, आप एक दौड़ की स्थिति में दौड़ेंगे।
n0pe

6

एक अतिरिक्त फ़ाइल डिस्क्रिप्टर तब अच्छा होता है जब आप स्टैडआउट को एक वैरिएबल में पकड़ना चाहते हैं, फिर भी स्क्रीन पर लिखना चाहते हैं, उदाहरण के लिए बैश स्क्रिप्ट यूजर इंटरफेस में

arg1 string to echo 
arg2 flag 0,1 print or not print to 3rd fd stdout descriptor   
function ecko3 {  
if [ "$2" -eq 1 ]; then 
    exec 3>$(tty) 
    echo -en "$1" | tee >(cat - >&3)
    exec 3>&- 
else 
    echo -en "$1"  
fi 
}

2
मुझे पता है कि यह कोई नया जवाब नहीं है, लेकिन मुझे यह देखने के लिए काफी कुछ देखना पड़ा कि यह क्या करता है और यह अगर किसी ने इस फ़ंक्शन के उदाहरण का इस्तेमाल किया हो तो यह मददगार होगा। यह एक इको है और इसके पूरे आउटपुट को कैप्चर करता है। एक कमांड - df, इस मामले में। dl.dropboxusercontent.com/u/54584985/mytest_redirect
जो

3

अतिरिक्त फ़ाइल विवरणक का उपयोग करते समय यहां एक और परिदृश्य उपयुक्त है (बाश में):

शेल स्क्रिप्ट पासवर्ड कमांड लाइन मापदंडों की सुरक्षा

env -i bash --norc   # clean up environment
set +o history
read -s -p "Enter your password: " passwd
exec 3<<<"$passwd"
mycommand <&3  # cat /dev/stdin in mycommand

1

उदाहरण: लिपियों को फ़ाइल लॉक के साथ क्रमिक रूप से चलाने के लिए झुंड का उपयोग करना

एक उदाहरण पटकथा प्रणाली को व्यापक रूप से चलाने के लिए स्क्रिप्ट को बाध्य करने के लिए फ़ाइल लॉकिंग का उपयोग करना है। यह उपयोगी है यदि आप एक ही तरह की दो स्क्रिप्ट एक ही फाइल पर काम नहीं करना चाहते हैं। अन्यथा, दो स्क्रिप्ट एक दूसरे और संभवतः भ्रष्ट डेटा के साथ हस्तक्षेप करेंगे।

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#open file descriptor 3 for writing
exec 3> /tmp/file.lock

#create an exclusive lock on the file using file descriptor 3
#exit if lock could not be obtained
flock -n 3

#execute serial code

#remove the file while the lock is still obtained
rm -f /tmp/file.lock

#close the open file handle which releases the file lock and disk space
exec 3>&-

लॉक और अनलॉक को परिभाषित करके कार्यात्मक रूप से झुंड का उपयोग करें

आप इस लॉकिंग / अनलॉकिंग तर्क को पुन: प्रयोज्य कार्यों में लपेट सकते हैं। निम्न trapशेल अंतर्निहित स्वचालित रूप से फ़ाइल लॉक को रिलीज़ करेगा जब स्क्रिप्ट बाहर निकलती है (या तो त्रुटि या सफलता)। trapआपकी फ़ाइल तालों को साफ़ करने में मदद करता है। पथ /tmp/file.lockएक कठिन कोडित पथ होना चाहिए ताकि एकाधिक स्क्रिप्ट उस पर लॉक करने का प्रयास कर सकें।

# obtain a file lock and automatically unlock it when the script exits
function lock() {
  exec 3> /tmp/file.lock
  flock -n 3 && trap unlock EXIT
}

# release the file lock so another program can obtain the lock
function unlock() {
  # only delete if the file descriptor 3 is open
  if { >&3 ; } &> /dev/null; then
    rm -f /tmp/file.lock
  fi
  #close the file handle which releases the file lock
  exec 3>&-
}

unlockतर्क ऊपर से पहले ताला जारी की है फ़ाइल को नष्ट करने के लिए है। इस तरह यह लॉक फाइल को साफ करता है। क्योंकि फ़ाइल को हटा दिया गया था, इस प्रोग्राम का एक और उदाहरण फ़ाइल लॉक प्राप्त करने में सक्षम है।

स्क्रिप्ट में लॉक और अनलॉक फ़ंक्शन का उपयोग

आप इसे अपनी स्क्रिप्ट में निम्न उदाहरण की तरह उपयोग कर सकते हैं।

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#try to lock (else exit because of non-zero exit code)
lock

#system-wide serial locked code

unlock

#non-serial code

यदि आप चाहते हैं कि आपका कोड तब तक प्रतीक्षा करे जब तक कि यह लॉक न हो जाए तो आप स्क्रिप्ट को समायोजित कर सकते हैं जैसे:

set -e

#wait for lock to be successfully obtained
while ! lock 2> /dev/null; do
  sleep .1
done

#system-wide serial locked code

unlock

#non-serial code

0

एक ठोस उदाहरण के रूप में, मैंने अभी एक पटकथा लिखी है, जिसमें एक उपकमांड से समय की जानकारी की आवश्यकता है। एक अतिरिक्त फ़ाइल डिस्क्रिप्टर का उपयोग करने से मुझे timeसबकोन्ड के स्टडआउट या हैडर को बाधित किए बिना कमांड के स्टडर को पकड़ने की अनुमति मिली ।

(time ls -9 2>&3) 3>&2 2> time.txt

यह क्या है बिंदु ls3 के लिए stderr है, स्क्रिप्ट के stderr के लिए 3 अंक, और timeएक फ़ाइल के लिए stderr बिंदु है । जब स्क्रिप्ट चलाई जाती है, तो उसका स्टडआउट और स्टॉडर उप-कमंड के समान होता है, जिसे हमेशा की तरह रीडायरेक्ट किया जा सकता है। केवल timeआउटपुट फ़ाइल में पुनर्निर्देशित है।

$ echo '(time ls my-example-script.sh missing-file 2>&3) 3>&2 2> time.txt' > my-example-script.sh
$ chmod +x my-example-script.sh 
$ ./my-example-script.sh 
ls: missing-file: No such file or directory
my-example-script.sh
$ ./my-example-script.sh > /dev/null
ls: missing-file: No such file or directory
$ ./my-example-script.sh 2> /dev/null
my-example-script.sh
$ cat time.txt

real    0m0.002s
user    0m0.001s
sys 0m0.001s
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.