GNU में पुनरावर्ती वाइल्डकार्ड्स?


92

जब से मैंने उपयोग किया है make, तब से यह मेरे साथ है ...

मुझे एक निर्देशिका मिली है flac, जिसमें .FLAC फाइलें हैं। मुझे एक संबंधित निर्देशिका मिली है, mp3जिसमें एमपी 3 फाइलें हैं। यदि कोई FLAC फ़ाइल संबंधित MP3 फ़ाइल (या संबंधित एमपी 3 फ़ाइल मौजूद नहीं है) की तुलना में नई है, तो मैं FLAC फ़ाइल को एक एमपी 3 फ़ाइल में बदलने के लिए और टैग को कॉपी करने के लिए कमांड का एक गुच्छा चलाना चाहता हूं।

किकर: मुझे flacनिर्देशिका को पुन : खोज करने की आवश्यकता है , और mp3निर्देशिका में संबंधित उपनिर्देशिकाएं बनाएं । निर्देशिका और फ़ाइलों के नाम में स्थान हो सकते हैं, और UTF-8 में नाम दिए गए हैं।

और मैं makeइसे चलाने के लिए उपयोग करना चाहता हूं ।


1
इस उद्देश्य के लिए चयन करने का कोई कारण? मैंने सोचा था कि एक बैश स्क्रिप्ट को लिखना सरल होगा

4
@ नया, पैटर्न आधारित फाइल सिस्टम परिवर्तन के रूप में अवधारणा को मूल समस्या से निपटने का सबसे अच्छा तरीका है। शायद इस दृष्टिकोण के कार्यान्वयन की अपनी सीमाएं हैं, लेकिन makeनंगे की तुलना में इसे लागू करने के करीब है bash
पी शेव

1
@Pavel ठीक है, एक shस्क्रिप्ट है कि flac फ़ाइलें (की सूची के माध्यम चलता है find | while read flacname), एक बनाता है mp3nameकि से, चलाता है "mkdir -p" पर dirname "$mp3name", और फिर, if [ "$flacfile" -nt "$mp3file"]धर्मान्तरित "$flacname"में "$mp3name"वास्तव में जादू नहीं है। एकमात्र विशेषता जिसे आप वास्तव में एक makeआधारित समाधान की तुलना में खो रहे हैं N, समानांतर में फ़ाइल रूपांतरण प्रक्रियाओं को चलाने की संभावना है make -jN
ndim

4
@ और यह पहली बार है जब मैंने कभी मेक के सिंटैक्स को "अच्छा" के रूप में वर्णित करने के लिए सुना है :-)

1
makeफ़ाइल नामों में रिक्त स्थान का उपयोग करना और विरोधाभासी आवश्यकताएं हैं। समस्या डोमेन के लिए उपयुक्त उपकरण का उपयोग करें।
जेन्स

जवाबों:


117

मैं इन पंक्तियों के साथ कुछ करने की कोशिश करूंगा

FLAC_FILES = $(shell find flac/ -type f -name '*.flac')
MP3_FILES = $(patsubst flac/%.flac, mp3/%.mp3, $(FLAC_FILES))

.PHONY: all
all: $(MP3_FILES)

mp3/%.mp3: flac/%.flac
    @mkdir -p "$(@D)"
    @echo convert "$<" to "$@"

makeशुरुआती लोगों के लिए त्वरित नोट्स की एक जोड़ी :

  • @आदेशों को रोकता के सामने makeवास्तव में चलाने से पहले आदेश मुद्रण से।
  • $(@D)लक्ष्य फ़ाइल नाम का निर्देशिका हिस्सा है ( $@)
  • सुनिश्चित करें कि उनमें शेल कमांड वाली लाइनें स्पेस के साथ नहीं, एक टैब से शुरू होती हैं।

यहां तक ​​कि अगर यह सभी UTF-8 वर्णों और सामान को संभालना चाहिए, तो यह फ़ाइल या निर्देशिका नामों में रिक्त स्थान पर विफल हो जाएगा, क्योंकि makeमेकफाइल्स में सामान को अलग करने के लिए रिक्त स्थान का उपयोग करता है और मुझे इसके आसपास काम करने के तरीके के बारे में पता नहीं है। तो यह आपको एक शेल स्क्रिप्ट के साथ छोड़ देता है, मुझे डर है: - /


यह वह जगह है जहां मैं जा रहा था ... जीत के लिए तेज उंगलियां। हालांकि ऐसा लग रहा है कि आप कुछ चतुर करने में सक्षम हो सकते हैं vpath। इन दिनों में से एक का अध्ययन करना चाहिए।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली

1
निर्देशिका में नामों के स्थान होने पर काम करने के लिए प्रकट नहीं होता है।
रोजर लिप्सकॉम्ब

पता ही नहीं चला कि मुझे फिर findसे नाम पाने के लिए खोलना होगा ...
रोजर लिप्सकॉम्ब

1
@PaulKonova: रन make -jN। समानांतर में चलने वाले Nरूपांतरणों की संख्या का उपयोग करने के लिए make। सावधानी: make -jबिना Nइच्छा के दौड़ना एक बार में सभी रूपांतरण प्रक्रियाओं को समानांतर में शुरू करेगा जो कांटा बम के बराबर हो सकता है।
ndim

1
@ एड्रियन: .PHONY: allरेखा बताती है कि allलक्ष्य के लिए नुस्खा निष्पादित किया जाना है, भले ही allसभी की तुलना में नई नामक एक फ़ाइल हो $(MP3_FILES)
ndim

52

आप अपने स्वयं के पुनरावर्ती वाइल्डकार्ड फ़ंक्शन को इस तरह परिभाषित कर सकते हैं:

rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))

पहला पैरामीटर ( $1) निर्देशिकाओं की एक सूची है, और दूसरा ( $2) उन पैटर्नों की सूची है जिन्हें आप मिलान करना चाहते हैं।

उदाहरण:

वर्तमान निर्देशिका में सभी C फ़ाइलों को खोजने के लिए:

$(call rwildcard,.,*.c)

सभी फाइलों .cऔर .hफाइलों को खोजने के लिए src:

$(call rwildcard,src,*.c *.h)

यह फ़ंक्शन कुछ सुधारों के साथ इस लेख के कार्यान्वयन पर आधारित है ।


यह मेरे लिए काम नहीं लगता है। मैंने सटीक फ़ंक्शन की प्रतिलिपि बनाई है और यह अभी भी पुनरावर्ती नहीं लगेगा।
जेरोएन

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

2
यह वास्तव में एक उदाहरण है, makeयह एक ट्यूरिंग टार पिट है (यहाँ देखें: yosefk.com/blog/fun-at-the-turing-tar-pit.html )। यह इतना कठिन भी नहीं है, लेकिन किसी को यह पढ़ना है: gnu.org/software/make/manual/html_node/Call-Function.html और फिर "पुनरावृत्ति को समझें"। आपको इसे पुनरावर्ती, शब्दशः अर्थ में लिखना था; यह "स्वचालित रूप से उप-विभाजनों से सामान शामिल करने" की रोजमर्रा की समझ नहीं है। यह वास्तविक मान्यता है। लेकिन याद रखें - "पुनरावृत्ति को समझने के लिए, आपको पुनरावृत्ति को समझना होगा"।
टॉमस गैंडर

@TomaszGandor आपको पुनरावृत्ति को समझने की आवश्यकता नहीं है। आपको पुनरावृत्ति को समझना होगा और ऐसा करने के लिए आपको पहले पुनरावृत्ति को समझना होगा।
user1129682

मेरा बुरा, मैं एक भाषाई झूठे-दोस्त के लिए गिर गया। टिप्पणियों को इतने लंबे समय के बाद संपादित नहीं किया जा सकता है, लेकिन मुझे उम्मीद है कि हर किसी को बात मिल गई। और वे पुनरावृत्ति को भी समझते हैं।
टॉमस गैंडर

2

Fwiw, मैं एक में कुछ इस तरह का उपयोग किया है Makefile :

RECURSIVE_MANIFEST = `find . -type f -print`

उपरोक्त उदाहरण सभी "सादे फ़ाइलों" ( '-type f' ) के लिए वर्तमान निर्देशिका ( '।' ) से खोज करेगा और हर फ़ाइल को खोजने के लिए परिवर्तनशील चर सेट करेगा । फिर आप इस सूची को कम करने के लिए पैटर्न प्रतिस्थापन का उपयोग कर सकते हैं, या वैकल्पिक रूप से, यह जो रिटर्न देता है उसे संकीर्ण करने के लिए अधिक तर्कों की आपूर्ति करते हैंखोजने के लिए मैन पेज देखें ।RECURSIVE_MANIFEST


1

मेरा समाधान ऊपर के एक पर आधारित है, और आउटपुट से बचने और रिक्त स्थान से बाहर निकलने के sedबजाय उपयोग करता है ।patsubstfind

Flac / ogg से जा रहे हैं /

OGGS = $(shell find flac -type f -name "*.flac" | sed 's/ /\\ /g;s/flac\//ogg\//;s/\.flac/\.ogg/' )

चेतावनियां:

  1. फिर भी अगर वहाँ फ़ाइल नाम में अर्ध-कॉलन हैं, लेकिन वे बहुत दुर्लभ हैं।
  2. $ (@ D) चाल काम नहीं करेगी (आउटपुट gibberish), लेकिन oggenc आपके लिए निर्देशिका बनाता है!

1

यहां एक पायथन स्क्रिप्ट है जिसे मैंने मूल समस्या को हल करने के लिए जल्दी से एक साथ हैक किया: एक संगीत पुस्तकालय की एक संकुचित प्रतिलिपि रखें। जब तक AAC फ़ाइल पहले से मौजूद है और ALAC फ़ाइल की तुलना में नई है, स्क्रिप्ट AAC प्रारूप में .m4a फ़ाइलों (ALAC होना मानती है) में बदल जाएगी। पुस्तकालय में एमपी 3 फाइलें जुड़ी होंगी, क्योंकि वे पहले से ही संपीड़ित हैं।

बस सावधान रहें कि स्क्रिप्ट को निरस्त करना ( ctrl-c) एक अर्ध-परिवर्तित फ़ाइल को पीछे छोड़ देगा।

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

from __future__ import print_function


import glob
import os
import subprocess


UNCOMPRESSED_DIR = 'Music'
COMPRESSED = 'compressed_'

UNCOMPRESSED_EXTS = ('m4a', )   # files to convert to lossy format
LINK_EXTS = ('mp3', )           # files to link instead of convert


for root, dirs, files in os.walk(UNCOMPRESSED_DIR):
    out_root = COMPRESSED + root
    if not os.path.exists(out_root):
        os.mkdir(out_root)
    for file in files:
        file_path = os.path.join(root, file)
        file_root, ext = os.path.splitext(file_path)
        if ext[1:] in LINK_EXTS:
            if not os.path.exists(COMPRESSED + file_path):
                print('Linking {}'.format(file_path))
                link_source = os.path.relpath(file_path, out_root)
                os.symlink(link_source, COMPRESSED + file_path)
            continue
        if ext[1:] not in UNCOMPRESSED_EXTS:
            print('Skipping {}'.format(file_path))
            continue
        out_file_path = COMPRESSED + file_path
        if (os.path.exists(out_file_path)
            and os.path.getctime(out_file_path) > os.path.getctime(file_path)):
            print('Up to date: {}'.format(file_path))
            continue
        print('Converting {}'.format(file_path))
        subprocess.call(['ffmpeg', '-y', '-i', file_path,
                         '-c:a', 'libfdk_aac', '-vbr', '4',
                         out_file_path])

बेशक, यह समानांतर में एन्कोडिंग प्रदर्शन करने के लिए बढ़ाया जा सकता है। कि पाठक के लिए एक अभ्यास के रूप में छोड़ दिया है; ;-)


0

यदि आप बैश 4.x का उपयोग कर रहे हैं, तो आप एक नए ग्लोबिंग विकल्प का उपयोग कर सकते हैं , उदाहरण के लिए:

SHELL:=/bin/bash -O globstar
list:
  @echo Flac: $(shell ls flac/**/*.flac)
  @echo MP3: $(shell ls mp3/**/*.mp3)

इस तरह के पुनरावर्ती वाइल्डकार्ड आपकी रुचि की सभी फाइलें ( .flac , .mp3 या जो भी हो) पा सकते हैं। हे


1
मेरे लिए, यहां तक ​​कि सिर्फ $ (वाइल्डकार्ड flac / ** / *। Flac) काम करने लगता है। ओएस एक्स, ग्नू मेक 3.81
उक्प्पी

2
मैंने $ (वाइल्डकार्ड ./**/*.py) की कोशिश की और इसने $ (वाइल्डकार्ड ./*/*.py) के समान व्यवहार किया। मुझे नहीं लगता कि वास्तव में ** का समर्थन करता है, और यह तब विफल नहीं होता जब आप एक दूसरे के बगल में दो * का उपयोग करते हैं।
लहरवां

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

@kenorb नहीं नहीं, मैंने आपकी बात की कोशिश भी नहीं की क्योंकि मैं इस विशेष चीज़ के लिए शेल इनवोकेशन से बचना चाहता था। मैं उक्प्पी की सुझाई हुई बात का उपयोग कर रहा था। मैं जिस चीज के साथ गया था वह लार्स्कहोल के उत्तर की तरह लग रही थी, हालांकि मुझे यह कहीं और से मिला क्योंकि यहां टिप्पणियों ने कहा कि यह एक सूक्ष्म रूप से टूट गया था।
श्रग

@lahwran इस मामले में **काम नहीं करेगा, क्योंकि विस्तारित ग्लोबिंग एक bash / zsh चीज़ है।
kenorb
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.