मेकफाइल में शेल कमांड का उपयोग कैसे करें


99

मैं lsअन्य आदेशों (जैसे गूंज, rsync) के परिणाम का उपयोग करने की कोशिश कर रहा हूं :

all:
    <Building, creating some .tgz files - removed for clarity>
    FILES = $(shell ls)
    echo $(FILES)

लेकिन मुझे मिलता है:

make
FILES = Makefile file1.tgz file2.tgz file3.tgz
make: FILES: No such file or directory
make: *** [all] Error 1

मैंने प्रयोग किया है echo $$FILES, echo ${FILES}और echo $(FILES), बिना किसी भाग्य के।

जवाबों:


149

साथ में:

FILES = $(shell ls)

इस allतरह के नीचे इंडेंट , यह एक बिल्ड कमांड है। तो यह फैलता है $(shell ls), फिर कमांड चलाने की कोशिश करता है FILES ...

यदि FILESएक makeचर माना जाता है , तो इन चर को नुस्खा भाग के बाहर सौंपा जाना चाहिए, जैसे:

FILES = $(shell ls)
all:
        echo $(FILES)

बेशक, इसका मतलब है कि .tgz फ़ाइलों को बनाने वाली किसी भी कमांड को चलाने FILESसे पहलेls " से आउटपुट" पर सेट किया जाएगा । (हालांकि के रूप में Kaz नोटों चर हर बार फिर से विस्तार किया गया है, इसलिए अंत में यह .tgz फ़ाइलों को शामिल करेगा, कुछ मेकअप विविधताएं हैं FILES := ...दक्षता और / या शुद्धता के लिए इस से बचने के लिए,। 1 )

यदि FILESशेल चर माना जाता है, तो आप इसे सेट कर सकते हैं, लेकिन आपको शेल-एईएस में करने की आवश्यकता है, जिसमें कोई स्थान नहीं है, और उद्धृत किया गया है:

all:
        FILES="$(shell ls)"

हालाँकि, प्रत्येक पंक्ति एक अलग शेल द्वारा चलाई जाती है, इसलिए यह चर अगली पंक्ति तक नहीं टिकेगा, इसलिए आपको इसे तुरंत उपयोग करना चाहिए:

        FILES="$(shell ls)"; echo $$FILES

यह सब थोड़ा मूर्खतापूर्ण है क्योंकि *पहली बार में आपके लिए शेल (और अन्य शेल ग्लोब एक्सप्रेशन) का विस्तार होगा , इसलिए आप निम्न कर सकते हैं:

        echo *

अपने शेल कमांड के रूप में।

अंत में, एक सामान्य नियम के रूप में (वास्तव में इस उदाहरण पर लागू नहीं होता है): जैसा कि टिप्पणियों में एस्परेंटो नोट करता है, से आउटपुट का उपयोग lsपूरी तरह से विश्वसनीय नहीं है (कुछ विवरण फ़ाइल नामों पर निर्भर करते हैं और कभी-कभी संस्करण भी ls; lsआउटपुट को सैनिटाइज़ करने के प्रयास के कुछ संस्करण; कुछ मामलों में)। इस प्रकार, l0b0 और आइडियल नोट के रूप में, यदि आप GNU का उपयोग कर रहे हैं, तो आप उपयोग कर सकते हैं $(wildcard)और स्वयं के $(subst ...)अंदर सब कुछ पूरा कर सकते हैं make(फ़ाइल नाम में किसी "अजीब पात्रों से बचना")। ( shस्क्रिप्ट में, मेकफाइल्स के रेसिपी भाग सहित, एक अन्य विधि का उपयोग find ... -print0 | xargs -0ब्लैंक, न्यूलाइन, कंट्रोल कैरेक्टर और इसी तरह ट्रिपिंग से बचने के लिए किया जाता है।)


1 GNU दस्तावेज़ नोट करें कि POSIX ::=2012 में अतिरिक्त असाइनमेंट बनाये । मुझे इसके लिए एक POSIX दस्तावेज़ का त्वरित संदर्भ लिंक नहीं मिला है, और न ही मुझे पता है कि कौन से makeवेरिएंट ::=असाइनमेंट का समर्थन करते हैं , हालांकि GNU आज भी करता है, जिसका अर्थ है कि :=, विस्तार के साथ अभी असाइनमेंट करें।

ध्यान दें कि कई वेरिएंट में VAR := $(shell command args...)वर्तनी भी हो सकती है , जिसमें सभी आधुनिक GNU और BSD वेरिएंट शामिल हैं जहाँ तक मुझे पता है। इन अन्य वेरिएंट का उपयोग नहीं किया जाता है, दोनों छोटे होने और अधिक वेरिएंट में काम करने में बेहतर है।VAR != command args...make$(shell)VAR != command args...


धन्यवाद। मैं एक विस्तृत कमांड ( उदाहरण के लिए और कट के lsसाथ sed) का उपयोग करना चाहता हूं , और फिर rsync और अन्य कमांड में परिणामों का उपयोग करना चाहता हूं । क्या मुझे बार-बार लंबी कमांड दोहरानी होगी? क्या मैं परिणामों को आंतरिक मेक वैरिएबल में संग्रहीत नहीं कर सकता हूं?
एडम मटन

1
Gnu बनाने के लिए ऐसा करने का एक तरीका हो सकता है, लेकिन मैंने इसका उपयोग कभी नहीं किया है, और सभी जटिल जटिल मेकफाइल्स का उपयोग हम केवल शेल चर और विशाल वन-लाइनर शेल कमांड का उपयोग करते हैं, जो प्रत्येक पंक्ति के अंत में "; \" के रूप में बनाया गया है। जरूरत है। (यहाँ बैकलैश अनुक्रम के साथ काम करने के लिए कोड एन्कोडिंग नहीं मिल सकता है, हम्म)
torek

1
शायद कुछ इस तरह: FILE = $(shell ls *.c | sed -e "s^fun^bun^g")
विलियम मॉरिस

2
@William: makeशेल का उपयोग किए बिना ऐसा कर सकते हैं FILE = $(subst fun,bun,$(wildcard *.c)):।
इदिकेल

1
मैं यह बताना चाहता हूं कि हालांकि इस मामले में यह बहुत महत्वपूर्ण नहीं लगता है, आपको स्वचालित रूप से ls के आउटपुट को पार्स नहीं करना चाहिए। ls का अभिप्राय मानव को जानकारी दिखाने के लिए है, न कि लिपियों में जंजीर बांधने के लिए। अधिक जानकारी यहाँ: mywiki.wooledge.org/ParsingLs शायद, अगर 'मेक' आपको वाइल्डकार्ड विस्तार की पेशकश नहीं करता है, तो "ढूंढें" "ls" से बेहतर है।
राउल सालिनास-मोंटियागुडो 16

53

इसके अलावा, टॉर्क के जवाब के अलावा: एक बात जो सामने आती है वह यह है कि आप एक आलसी-मूल्यांकनित मैक्रो असाइनमेंट का उपयोग कर रहे हैं।

यदि आप GNU मेक पर हैं, तो :=इसके बजाय असाइनमेंट का उपयोग करें =। यह असाइनमेंट दाहिने हाथ की ओर तुरंत विस्तारित होने का कारण बनता है, और बाएं हाथ चर में संग्रहीत होता है।

FILES := $(shell ...)  # expand now; FILES is now the result of $(shell ...)

FILES = $(shell ...)   # expand later: FILES holds the syntax $(shell ...)

यदि आप =असाइनमेंट का उपयोग करते हैं , तो इसका मतलब है कि हर एक घटना सिंटैक्स का $(FILES)विस्तार करेगी $(shell ...)और इस प्रकार शेल कमांड को लागू करेगी । यह आपकी नौकरी के काम को धीमा कर देगा, या यहां तक ​​कि कुछ आश्चर्यजनक परिणाम भी हो सकते हैं।


1
अब जब हमारे पास सूची है तो हम सूची में प्रत्येक आइटम पर कैसे पुनरावृति करते हैं और उस पर एक कमांड निष्पादित करते हैं? जैसे कि बिल्ड या टेस्ट?
1958 में anon58192932

3
@ anon58192932 एक कमांड निष्पादित करने के लिए यह विशिष्ट पुनरावृत्ति, आमतौर पर एक शेल नुस्खा में एक शेल सिंटैक्स टुकड़े में किया जाता है, इसलिए यह शेल में होता है, बजाय मेक for x in $(FILES); do command $$x; done:। ध्यान दें कि दोगुना हो जाता है, $$जो $शेल के लिए एकल पास करता है। इसके अलावा, शेल टुकड़े एक-लाइनर हैं; मल्टी-लाइन शेल कोड लिखने के लिए, आप बैकस्लैश निरंतरता का उपयोग करते हैं जो makeस्वयं द्वारा संसाधित होता है और एक लाइन में बदल जाता है। जिसका अर्थ है शेल-कमांड-सेपरिंग सेमीकोलन अनिवार्य है।
काज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.