आप किसी फ़ाइल की प्रत्येक पंक्ति के लिए कमांड कैसे चलाते हैं?


161

उदाहरण के लिए, अभी मैं निम्नलिखित फ़ाइलों का उपयोग करने के लिए उपयोग कर रहा हूं, जिनके यूनिक्स पथ मैंने एक फ़ाइल में लिखे हैं:

cat file.txt | while read in; do chmod 755 "$in"; done

वहाँ एक और अधिक सुंदर, सुरक्षित तरीका है?

जवाबों:


127

एक फ़ाइल लाइन लाइन द्वारा पढ़ें और कमांड निष्पादित करें: 4 उत्तर

इसका कारण यह है कि केवल 1 उत्तर नहीं है ...

  1. shell कमांड लाइन का विस्तार
  2. xargs समर्पित उपकरण
  3. while read कुछ टिप्पणियों के साथ
  4. while read -uइंटरैक्टिव प्रोसेसिंग (नमूना) के fdलिए समर्पित का उपयोग करना

ओपी अनुरोध के बारे में: फ़ाइल में सूचीबद्ध सभी लक्ष्यों पर चलनाchmod , xargsसंकेतित उपकरण है। लेकिन कुछ अन्य अनुप्रयोगों के लिए, फ़ाइलों की छोटी मात्रा, आदि ...

  1. कमांड लाइन तर्क के रूप में पूरी फाइल पढ़ें।

    यदि आपकी फ़ाइल बहुत बड़ी नहीं है और सभी फाइलें अच्छी तरह से नामित हैं (बिना रिक्त स्थान या अन्य विशेष वर्ण जैसे उद्धरण), तो आप shellकमांड लाइन विस्तार का उपयोग कर सकते हैं । सीधे शब्दों में:

    chmod 755 $(<file.txt)

    छोटी मात्रा में फ़ाइलों (लाइनों) के लिए, यह कमांड हल्का है।

  2. xargs सही उपकरण है

    बड़ी मात्रा में फ़ाइलों के लिए, या आपकी इनपुट फ़ाइल की लगभग किसी भी संख्या में ...

    कई लोगों के लिए binutils उपकरण, जैसे chown, chmod, rm, cp -t...

    xargs chmod 755 <file.txt

    यदि आपके पास विशेष आकर्षण और / या बहुत सारी लाइनें हैं file.txt

    xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)

    यदि आपके आदेश को प्रविष्टि द्वारा ठीक 1 बार चलाने की आवश्यकता है:

    xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)

    इस नमूने के लिए इसकी आवश्यकता नहीं है, chmodक्योंकि तर्क के रूप में कई फ़ाइलों को स्वीकार करते हैं, लेकिन यह प्रश्न के शीर्षक से मेल खाता है।

    कुछ विशेष मामलों के लिए, आप द्वारा उत्पन्न कमांड में फ़ाइल तर्क के स्थान को भी परिभाषित कर सकते हैं xargs:

    xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)

    seq 1 5इनपुट के रूप में परीक्षण करें

    इसे इस्तेमाल करे:

    xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
    Blah 1 blabla 1..
    Blah 2 blabla 2..
    Blah 3 blabla 3..
    Blah 4 blabla 4..
    Blah 5 blabla 5..

    जहां प्रति पंक्ति एक बार कमांड किया जाता है ।

  3. while read और वेरिएंट।

    जैसा कि ओपी सुझाव cat file.txt | while read in; do chmod 755 "$in"; doneकाम करेगा, लेकिन 2 मुद्दे हैं:

    • cat |एक बेकार कांटा है , और

    • | while ... ;doneएक उप-क्षेत्र बन जाएगा जहां पर्यावरण के बाद गायब हो जाएगा ;done

    तो यह बेहतर लिखा जा सकता है:

    while read in; do chmod 755 "$in"; done < file.txt

    परंतु,

    • आपको झंडे $IFSऔर चेतावनी दी जा सकती है read:

      help read
      read: read [-r] ... [-d delim] ... [name ...]
          ...
          Reads a single line from the standard input... The line is split
          into fields as with word splitting, and the first word is assigned
          to the first NAME, the second word to the second NAME, and so on...
          Only the characters found in $IFS are recognized as word delimiters.
          ...
          Options:
            ...
            -d delim   continue until the first character of DELIM is read, 
                       rather than newline
            ...
            -r do not allow backslashes to escape any characters
          ...
          Exit Status:
          The return code is zero, unless end-of-file is encountered...

      कुछ मामलों में, आपको उपयोग करने की आवश्यकता हो सकती है

      while IFS= read -r in;do chmod 755 "$in";done <file.txt

      अजनबियों फ़ाइल नाम के साथ समस्याओं से बचने के लिए। और हो सकता है कि यदि आप समस्याओं के साथ संलग्न करें UTF-8:

      while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
    • जब आप STDINपढ़ने के लिए उपयोग करते हैं file.txt, तो आपकी स्क्रिप्ट इंटरैक्टिव नहीं हो सकती है (आप STDINअब उपयोग नहीं कर सकते हैं)।

  4. while read -u, समर्पित का उपयोग कर fd

    सिंटेक्स: while read ...;done <file.txtरीडायरेक्ट करेगा STDINकरने के लिए file.txt। इसका मतलब है, आप प्रक्रिया से निपटने में सक्षम नहीं होंगे, जब तक कि वे खत्म नहीं हो जाते।

    यदि आप इंटरेक्टिव टूल बनाने की योजना बनाते हैं , तो आपको STDINकुछ वैकल्पिक फ़ाइल डिस्क्रिप्टर के उपयोग और उपयोग से बचना होगा ।

    स्थिरांक फ़ाइल वर्णनकर्ता हैं: 0के लिए STDIN , 1के लिए STDOUT और 2के लिए STDERR । आप उन्हें देख सकते हैं:

    ls -l /dev/fd/

    या

    ls -l /proc/self/fd/

    वहाँ से, आप, अप्रयुक्त संख्या चुन के बीच करने के लिए है 0और 63(अधिक, वास्तव में, के आधार पर sysctlसुपर उपयोगकर्ता उपकरण) के रूप में फ़ाइल वर्णनकर्ता :

    इस डेमो के लिए, मैं fd का उपयोग करूंगा 7:

    exec 7<file.txt      # Without spaces between `7` and `<`!
    ls -l /dev/fd/

    तब आप read -u 7इस तरह से उपयोग कर सकते हैं :

    while read -u 7 filename;do
        ans=;while [ -z "$ans" ];do
            read -p "Process file '$filename' (y/n)? " -sn1 foo
            [ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
        done
        if [ "$ans" = "y" ] ;then
            echo Yes
            echo "Processing '$filename'."
        else
            echo No
        fi
    done 7<file.txt

    done

    बंद करने के लिए fd/7:

    exec 7<&-            # This will close file descriptor 7.
    ls -l /dev/fd/

    नोटा: मैंने स्ट्राइक किया संस्करण क्योंकि यह वाक्यविन्यास उपयोगी हो सकता है, जब समानताएं प्रक्रिया के साथ कई I / O करते हैं:

    mkfifo sshfifo
    exec 7> >(ssh -t user@host sh >sshfifo)
    exec 6<sshfifo

3
जैसा कि xargsइस तरह की ज़रूरत का जवाब देने के लिए प्रारंभिक निर्माण किया गया था, कुछ सुविधाएँ, जैसे वर्तमान वातावरणchmod में यथासंभव कम से कम इस मामले में निवेश करने के लिए कमांड का निर्माण करना, कांटे कम करना प्रभावशीलता सुनिश्चित करता है। while ;do..done <$file1 फ़ाइल के लिए 1 कांटा चल रहा है। xargsएक विश्वसनीय तरीके से हजार फाइलों के लिए 1 कांटा चला सकता है।
एफ। होरी

1
तीसरी कमांड मेकफाइल में काम क्यों नहीं करती है? मैं अप्रत्याशित टोकन के पास "सिंटैक्स त्रुटि" प्राप्त कर रहा हूं, लेकिन कमांड लाइन से सीधे कार्य निष्पादित करता है।
वुडरो बारलो

2
यह मेकफाइल विशिष्ट सिंटैक्स से जुड़ा हुआ लगता है। आप कमांड लाइन को उलटने की कोशिश कर सकते हैं:cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
F. Hauri

@ F.Hauri किसी कारण से, tr \\n \\0 <file.txt |xargs -0 [command]आपके द्वारा बताई गई विधि की तुलना में लगभग 50% तेज है।
phil294

अक्टूबर 2019, नया संपादन, इंटरैक्टिव फ़ाइल प्रोसेसर नमूना जोड़ें ।
एफ। होरी

150

हाँ।

while read in; do chmod 755 "$in"; done < file.txt

इस तरह आप एक catप्रक्रिया से बच सकते हैं ।

catइस तरह के उद्देश्य के लिए लगभग हमेशा बुरा होता है। आप बिल्ली के बेकार उपयोग के बारे में अधिक पढ़ सकते हैं


से बचें एक cat एक अच्छा विचार है, लेकिन इस मामले में, संकेत आदेश हैxargs
एफ HAURI

यह लिंक प्रासंगिक नहीं लगता है, शायद वेब पेज की सामग्री बदल गई है? उत्तर का शेष भाग बहुत बढ़िया है:
स्टारबाईम्रेनबोलाब्स

@starbeamrainbowlabs हां। ऐसा लगता है कि पृष्ठ को स्थानांतरित कर दिया गया है। मैंने फिर से लिंक किया है और अब ठीक होना चाहिए। धन्यवाद :)
PP

1
धन्यवाद! यह मददगार था, खासकर जब आपको कॉल करने के अलावा कुछ और करने की आवश्यकता होती है chmod(यानी फ़ाइल में प्रत्येक पंक्ति के लिए वास्तव में एक कमांड चलाते हैं)।
प्रति Lundberg

बैकस्लैश के साथ सावधान रहें! से unix.stackexchange.com/a/7561/28160 - " read -rमानक इनपुट से एक पंक्ति पढ़ता है ( readबिना -rव्याख्या बैकस्लैश, आपको लगता है कि नहीं करना चाहती)।"
वह ब्राजील के लड़के

16

यदि आपके पास एक अच्छा चयनकर्ता है (उदाहरण के लिए एक dir में सभी .txt फ़ाइलें) आप कर सकते हैं:

for i in *.txt; do chmod 755 "$i"; done

पाश के लिए मारना

या तुम्हारा एक प्रकार:

while read line; do chmod 755 "$line"; done <file.txt

जो काम नहीं करता है वह यह है कि यदि लाइन में स्थान हैं, तो इनपुट को रिक्त स्थान से विभाजित किया जाता है, रेखा से नहीं।
माइकल फॉक्स

@ मिचेल फॉक्स: विभाजक को बदलकर रिक्त स्थान के साथ लाइनों का समर्थन किया जा सकता है। इसे नई सूचियों में बदलने के लिए, स्क्रिप्ट / कमांड से पहले 'IFS' पर्यावरण चर सेट करें। Ex: निर्यात IFS = '$ \ n'
कोड्सनिफर

मेरी अंतिम टिप्पणी में टाइपो। होना चाहिए: निर्यात IFS = $ '\ n'
कोडननिफर

14

यदि आप जानते हैं कि आपके पास इनपुट में कोई व्हाट्सएप नहीं है:

xargs chmod 755 < file.txt

यदि रास्तों में व्हाट्सएप हो सकता है, और यदि आपके पास GNU xargs है:

tr '\n' '\0' < file.txt | xargs -0 chmod 755

मैं xargs के बारे में जानता हूं, लेकिन (दुख की बात है) यह बिल्ट-इन फीचर्स की तुलना में विश्वसनीय समाधान से कम लगता है, जबकि पढ़ा और पढ़ा जाता है। इसके अलावा, मेरे पास GNU xargs नहीं है, लेकिन मैं OS X का उपयोग कर रहा हूं और xargs में भी -0 विकल्प है। जवाब के लिए धन्यवाद।
बाज़

1
@ हॉक नंबर: xargsमजबूत है। यह उपकरण बहुत पुराना है और उसके कोड का जोरदार पुनरीक्षण किया गया है । उनका लक्ष्य शेल सीमाओं (64kchar / लाइन या ऐसा कुछ) के संबंध में लाइनें बनाने के लिए प्रारंभिक था। अब यह उपकरण बहुत बड़ी फ़ाइलों के साथ काम कर सकता है और बहुत से कांटे को अंतिम कमांड तक कम कर सकता है। मेरा उत्तर देखें और / या man xargs
एफ। हौरी

@ हॉक किस तरह से विश्वसनीय समाधान से कम है? यदि यह लिनक्स, मैक / बीएसडी और विंडोज (हां, MSYSGIT के बंडल GNU xargs) में काम करता है, तो यह उतना ही विश्वसनीय है जितना इसे प्राप्त होता है।
कैमिलो मार्टिन

1
उन लोगों के लिए जो अभी भी खोज परिणामों से इसे ढूंढ रहे हैं ... आप Homebrew ( brew install findutils) का उपयोग करके macOS पर GNU xargs स्थापित कर सकते हैं , और फिर gxargsइसके बजाय GNU xargs आह्वान कर सकते हैं , जैसेgxargs chmod 755 < file.txt
Jase

13

यदि आप प्रत्येक पंक्ति के समानांतर अपनी कमांड चलाना चाहते हैं तो आप GNU समानांतर का उपयोग कर सकते हैं

parallel -a <your file> <program>

आपकी फ़ाइल की प्रत्येक पंक्ति को तर्क के रूप में प्रोग्राम करने के लिए पास किया जाएगा। डिफ़ॉल्ट parallelरूप से आपके CPU गिनती के रूप में कई धागे चलाता है। लेकिन आप इसे निर्दिष्ट कर सकते हैं-j


3

मैं देखता हूं कि आपने बैश को टैग किया है, लेकिन पर्ल भी ऐसा करने का एक अच्छा तरीका होगा:

perl -p -e '`chmod 755 $_`' file.txt

आप यह सुनिश्चित करने के लिए कि आप सही फ़ाइलें प्राप्त कर रहे हैं, उदाहरण के लिए केवल .txt फ़ाइलों को संसाधित करने के लिए एक regex भी लागू कर सकते हैं:

perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt

"पूर्वावलोकन" करने के लिए, क्या हो रहा है, बस बैकटिक्स को दोहरे उद्धरण चिह्नों के साथ बदलें print:

perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt

2
क्यों backticks का उपयोग करें? पर्ल का एक chmodकार्य है
ग्लेन जैकमैन

1
आप चाहते हैं perl -lpe 'chmod 0755, $_' file.txt- -l"ऑटो-
चॉम्प

1

आप AWK का भी उपयोग कर सकते हैं जो आपको फ़ाइल को संभालने के लिए अधिक लचीलापन दे सकता है

awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt

यदि आपकी फ़ाइल में फ़ील्ड विभाजक है जैसे:

फ़ील्ड 1, field2, फ़ील्ड 3

केवल पहला क्षेत्र प्राप्त करने के लिए

awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt

आप GNU दस्तावेज़ीकरण https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple पर अधिक विवरण देख सकते हैं


0

तर्क कई अन्य उद्देश्यों पर लागू होता है। और कैसे /। / फाइल सिस्टम से प्रत्येक उपयोगकर्ता के .sh_history को पढ़ने के लिए? क्या होगा अगर उनमें से हजार हैं?

#!/bin/ksh
last |head -10|awk '{print $1}'|
 while IFS= read -r line
 do
su - "$line" -c 'tail .sh_history'
 done

यहाँ स्क्रिप्ट https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh है


0

मुझे पता है कि यह देर हो चुकी है लेकिन अभी भी है

यदि किसी भी संयोग से आप \r\nइसके बजाय विंडोज़ सेव की हुई टेक्स्ट फ़ाइल में चलते हैं \n, तो आप आउटपुट से भ्रमित हो सकते हैं यदि आपके कमांड को पढ़ने के बाद इस तर्क के बाद sth है। तो \rउदाहरण के लिए निकालें :

cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.