तर्कों के रूप में मिली फाइलों को कैसे पास करें?


9

कटौती तुच्छ लेकिन अयोग्य जवाब बंद करने के लिए सबसे पहले: मैं न तो उपयोग कर सकते हैं find+ xargsचाल और न ही उसके संस्करण (जैसे findके साथ -exec), क्योंकि मैं प्रति कॉल कुछ इस तरह के एक्सप्रेशन का उपयोग करने की जरूरत है। मैं अंत में इस पर वापस आऊंगा।


अब एक बेहतर उदाहरण के लिए आइए विचार करें:

$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

मैं उन लोगों को तर्क के रूप में कैसे पास करूं program?

बस कर यह चाल नहीं है

$ ./program $(find -L some/dir -name \*.abc | sort)

programनिम्नलिखित तर्क मिलने के बाद विफल रहता है:

[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc

जैसा कि देखा जा सकता है, अंतरिक्ष के साथ पथ विभाजित था और programइसे दो अलग-अलग तर्क मानता है।

काम होने तक बोली

ऐसा लगता है कि नौसिखिए उपयोगकर्ता अपने आप को, जब ऐसी समस्याओं का सामना करते हैं, तब तक बेतरतीब ढंग से उद्धरण जोड़ते हैं जब तक कि यह अंत में काम नहीं करता - केवल यहाँ यह मदद करने के लिए प्रतीत नहीं होता है ...

"$(…)"

$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

क्योंकि उद्धरण शब्द-विभाजन को रोकते हैं, सभी फाइलें एक ही तर्क के रूप में पारित की जाती हैं।

अलग-अलग रास्तों का हवाला देते हुए

एक आशाजनक दृष्टिकोण:

$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"

उद्धरण हैं, सुनिश्चित करें। लेकिन अब उनकी व्याख्या नहीं है। वे तार का हिस्सा हैं। इसलिए न केवल उन्होंने शब्द विभाजन को नहीं रोका, बल्कि वे बहस में पड़ गए!

IFS बदलें

फिर मैंने साथ खेलने की कोशिश की IFS। मैं पसंद करेंगे findके साथ -print0और sortसाथ -zवैसे भी - इतना है कि वे "वायर्ड पथ" खुद पर कोई मुद्दों होगा। तो क्यों नहीं nullचरित्र पर शब्द बंटवारे और यह सब किया है?

$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc

तो यह अभी भी अंतरिक्ष पर बंटता है और पर विभाजित नहीं होता है null

मैंने IFSअसाइनमेंट को $(…)(जैसा कि ऊपर दिखाया गया है) और उससे पहले दोनों जगह रखने की कोशिश की ./program। इसके अलावा, मैं अन्य वाक्य रचना की कोशिश की तरह \0, \x0, \x00दोनों के साथ उद्धृत किया 'और "के साथ या बिना के रूप में भी $। उन लोगों में से कोई भी कोई फर्क नहीं लग रहा था ...


और यहाँ मैं विचारों से बाहर हूँ। मैंने कुछ और चीजों की कोशिश की, लेकिन सभी को सूचीबद्ध करने के लिए नीचे की समस्याओं की तरह लग रहा था।

इसके अलावा मेरे द्वारा और क्या किया जा सकता है? क्या यह सब करने योग्य है?

ज़रूर, मैं programपैटर्न को स्वीकार कर सकता हूं और खुद खोज कर सकता हूं । लेकिन एक विशिष्ट सिंटैक्स के लिए इसे ठीक करते समय यह बहुत दोहरा काम है। ( grepउदाहरण के लिए फाइलें उपलब्ध कराने के बारे में क्या ?)।

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


अंत में, मैं आपको फिर से याद दिला दूं कि find+ xargs(और एक जैसे) ट्रिक्स मेरे मामले में काम नहीं करेंगे। विवरण सादगी के लिए मैं केवल एक तर्क दिखा रहा हूं। लेकिन मेरा असली मामला इस तरह दिखता है:

$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES

तो xargsएक खोज से कर मुझे अभी भी कैसे दूसरे के साथ सौदा करने के लिए छोड़ देता है ...

जवाबों:


13

सरणियों का उपयोग करें।

यदि आपको अपने फ़ाइल नाम में नए समाचारों की संभावना को संभालने की आवश्यकता नहीं है, तो आप दूर हो सकते हैं

mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)

फिर

./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"

यदि आप कर फ़ाइल नामों के भीतर संभाल नई-पंक्तियों की जरूरत है, और बैश> = 4.4, तो आप उपयोग कर सकते हैं -print0और -d ''करने के लिए अशक्त-समाप्त सरणी निर्माण के दौरान नाम:

mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)

(और इसी तरह के लिए XYZ_FILES)। यदि आपके पास नया बैश नहीं है, तो आप फ़ाइल नाम को सरणियों में संलग्न करने के लिए एक अशक्त-समाप्त रीड लूप का उपयोग कर सकते हैं।

ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)

अति उत्कृष्ट! मैं सरणियों के बारे में सोच रहा था। लेकिन किसी तरह मुझे उस mapfile(या इसके पर्यायवाची readarray) पर कुछ नहीं मिला । लेकिन यह काम करता है!
एडम बदुरा

फिर भी आप इसे थोड़ा सुधार सकते हैं। बैश <4.4 संस्करण (जो मेरे पास होता है ...) एक whileलूप के साथ सरणी को स्पष्ट नहीं करता है। जिसका अर्थ है कि यदि कोई फाइल नहीं मिली है तो सरणी अपरिभाषित है। जबकि अगर यह पहले से ही परिभाषित है तो नई फाइलें संलग्न की जाएंगी (पुराने की जगह)। ऐसा लगता है कि जोड़ने declare -a ABC_FILES='()';से पहले whileचाल है। (जबकि जोड़ने का ABC_FILES='()';मतलब यह नहीं है।)
एडम बदुरा

यहाँ भी क्या < <मतलब है? क्या यह वैसा ही है <<? मुझे नहीं लगता कि इसे <<सिंटैक्स त्रुटि ("अप्रत्याशित टोकन` ('") पैदावार में बदलने के रूप में । तो यह क्या है और यह कैसे काम करता है?
एडम बदुरा

एक और सुधार (मेरे विशेष उपयोग के साथ) अभी तक एक और सरणी का निर्माण करना है। तो हमारे पास वे हैं ABC_FILES। यह ठीक है। लेकिन यह भी उपयोगी है ABS_ARGSकि एक खाली सरणी है जो ABC_FILESखाली है या अन्यथा यह एक सरणी है ('--abc-files' "${ABC_FILES[@]}")। इस तरह बाद में मैं इसे इस तरह से उपयोग कर सकता हूं: ./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"और सुनिश्चित करें कि यह सही ढंग से काम करेगा चाहे समूहों में से कोई भी (यदि कोई हो) खाली हो। या इसे अलग तरह से बताने के लिए: यह तरीका --abc-files(और --xyz-files) केवल तभी प्रदान किया जाएगा जब इसका पालन किसी वास्तविक मार्ग से किया जाएगा।
एडम बदुरा

1
@ अदमबदुरा: PROCESS SUBSTITUTION द्वारा बनाई गई एक विशेष फ़ाइल से while read ... done < <(find blah)सामान्य शेल पुनर्निर्देशन <है । यह पाइपिंग से अलग है क्योंकि पाइपलाइन एक उप-प्रकार में लूप को चलाती है इसलिए इसमें सेट किया गया var (s) बाद के कमांड के लिए बरकरार नहीं रहता है। find blah | while read ... donewhile
dave_thompson_085

3

आप IFS = newline का उपयोग कर सकते हैं (यह मानते हुए कि किसी फ़ाइल नाम में newline नहीं है), लेकिन आपको इसे बाहरी शेल में सेट करना होगा प्रतिस्थापन से पहले:

$ ls -1
a file with spaces
able
alpha
baker
boo hoo hoo
bravo
$ # note semicolon here; it's not enough to be in the environment passed
$ # to printf, it must be in the environment OF THE SHELL WHILE PARSING
$ IFS=$'\n'; printf '%s\n' --afiles $(find . -name 'a*') --bfiles $(find . -name 'b*')
--afiles
./able
./a file with spaces
./alpha
--bfiles
./bravo
./boo hoo hoo
./baker

zshलेकिन नहीं के साथ bashआप अशक्त $'\0'रूप से भी उपयोग कर सकते हैं । यहां तक ​​कि bashअगर तुम एक पर्याप्त अजीब चरित्र है कि कभी नहीं की तरह इस्तेमाल किया है newline संभाल सकता है

 IFS=$'\1'; ... $(find ... -print0 | tr '\0' '\1') ...

हालाँकि, यह दृष्टिकोण आपके द्वारा किए गए अतिरिक्त अनुरोध को हैंडल नहीं करता है, अगर यह एक खाली है तो - theafiles को छोड़ने के लिए @ स्टीलड्राइवर के उत्तर पर टिप्पणी करें।


तो जैसा कि मैं समझ रहा हूँ कि बैश में बल IFSदेने का कोई तरीका नहीं है null?
एडम बदुरा

@ अदमबुरा: मुझे पूरा यकीन है कि नहीं; बैश IFS सहित किसी भी चर में अशक्त बाइट की अनुमति नहीं देता है। ध्यान दें कि read -d ''स्टीलड्राइवर के तरीकों में इस्तेमाल किया जाने वाला एक रिक्त स्ट्रिंग है जिसमें एक शून्य बाइट नहीं है। (और एक कमांड विकल्प वैसे भी एक
संस्करण नहीं है

आपको set -o noglobउस विभाजन + ग्लोब ऑपरेटर (को छोड़कर zsh) का उपयोग करने से पहले ग्लोबिंग ( ) को भी अक्षम करना होगा ।
स्टीफन चेज़लस


@AdamBadura हां, बैश में, एक नल ठीक वैसा ही है $'\0'और जैसा है ''
इसहाक

1

मुझे यकीन नहीं है कि मुझे समझ में आया कि आपने क्यों दिया xargs

तो xargsएक खोज से कर मुझे अभी भी कैसे दूसरे के साथ सौदा करने के लिए छोड़ देता है ...

स्ट्रिंग --xyz-filesकई तर्कों में से एक है और आपके कार्यक्रम द्वारा व्याख्या किए जाने से पहले इसे विशेष मानने का कोई कारण नहीं है। मुझे लगता है कि आप इसे xargsदोनों findपरिणामों के बीच से गुजर सकते हैं :

{ find -L some/dir -name \*.abc -print0 | sort -z; echo -ne "--xyz-files\0"; find -L other/dir -name \*.xyz -print0 | sort -z; } | xargs -0 ./program --abc-files

तुम सही हो! यह भी काम करता है! हालांकि ध्यान दें कि आप -print0दूसरे में चूक गए find। अगर इस तरह से जा रहा हूँ मैं भी --abc-filesएक के echoरूप में अच्छी तरह से रखा जाएगा - सिर्फ स्थिरता के लिए।
एडम बदुरा

यह दृष्टिकोण सरल और कुछ हद तक सरणी दृष्टिकोण की तुलना में एक-लाइनर लगता है। हालाँकि, इस मामले को कवर करने के लिए कुछ अतिरिक्त तर्क की आवश्यकता होगी कि यदि कोई .abcफाइल नहीं है, तो कोई भी नहीं होना चाहिए --abc-files(साथ ही .xyz)। सरणी-आधारित समाधान द्वारा steeldriver भी इसके लिए अतिरिक्त तर्क की आवश्यकता है लेकिन, जबकि यह यहाँ नहीं-तो तुच्छ हो सकता है इस समाधान का मुख्य लाभ को नष्ट कि तर्क वहाँ मामूली बात है - सादगी।
एडम बडूरा

इसके अलावा, मुझे वास्तव में यकीन नहीं है, लेकिन मुझे लगता है कि xargsकभी भी एक के बजाय तर्क को विभाजित करने और कुछ कमांड बनाने की कोशिश नहीं की जाएगी, जब तक कि स्पष्ट रूप से ऐसा करने के लिए निर्देश नहीं दिया जाता है -L, --max-lines( -l) --max-args( -n) या --max-chars( -s) तर्क। क्या मैं सही हू? या कुछ चूक हैं? जैसा कि मेरा कार्यक्रम इस तरह के विभाजन को सही ढंग से नहीं संभाल पाएगा और मुझे इसे कॉल करने में विफलता होगी ...
एडम बडूरा

1
@AadBadura लापता -print0- तय, धन्यवाद। मैं सभी उत्तरों को नहीं जानता लेकिन मैं मानता हूं कि मेरा समाधान अतिरिक्त तर्क को शामिल करना कठिन बनाता है। मैं संभवत: स्वयं सरणियों के साथ जाऊंगा, अब जब मैं इस दृष्टिकोण को जानता हूं। मेरा जवाब वास्तव में आपके लिए नहीं था। आपने दूसरे उत्तर को पहले ही स्वीकार कर लिया था और मैंने मान लिया कि आपकी समस्या हल हो गई है। मैं सिर्फ यह बताना चाहता था कि आप कई स्रोतों से तर्क पारित कर सकते हैं xargs, जो पहली नज़र में स्पष्ट नहीं था। आप इसे अवधारणा के प्रमाण के रूप में मान सकते हैं। अब हम सभी कुछ अलग दृष्टिकोणों को जानते हैं और हम सचेत रूप से यह चुन सकते हैं कि हर विशेष मामले में हमें क्या फिट बैठता है।
कामिल मैकियोरोस्की

हां, मैंने पहले से ही सरणी-आधारित समाधान लागू किया है और यह आकर्षण की तरह काम करता है। मुझे विशेष रूप से गर्व है कि यह कितनी सफाई से वैकल्पिकता के साथ व्यवहार करता है (यदि कोई फाइल नहीं तो नहीं --abc-files)। लेकिन आप सही हैं - अपने विकल्पों को जानना अच्छा है! खासकर यह कि मैंने गलती से सोचा कि यह संभव नहीं है।
एडम बदुरा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.