एक निर्देशिका में सभी फाइलों पर कमांड निष्पादित करें


290

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

उदाहरण के लिए, 1 फ़ाइल पर कमांड चलाने के लिए:

$ cmd [option] [filename] > results.out

3
मैं प्रश्न में जोड़ना चाहूंगा। यह xargs का उपयोग करके किया जा सकता है? जैसे, ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out
ओजैर काफ़रे

2
यह कर सकते हैं, लेकिन आप शायद ड्राइव करने के लिए उपयोग नहीं करना चाहते हैंlsxargs । यदि cmdसभी सक्षम रूप से लिखे गए हैं, तो शायद आप बस कर सकते हैं cmd <wildcard>
ट्रिपलए

जवाबों:


425

निम्नलिखित बैश कोड $ फ़ाइल को कमांड करने के लिए पारित करेगा जहाँ $ फ़ाइल / dir में प्रत्येक फ़ाइल का प्रतिनिधित्व करेगी

for file in /dir/*
do
  cmd [option] "$file" >> results.out
done

उदाहरण

el@defiant ~/foo $ touch foo.txt bar.txt baz.txt
el@defiant ~/foo $ for i in *.txt; do echo "hello $i"; done
hello bar.txt
hello baz.txt
hello foo.txt

23
यदि कोई फाइल मौजूद नहीं है /dir/, तो लूप अभी भी एक बार '*' के मान से चलता है $file, जो अवांछनीय हो सकता है। इससे बचने के लिए, लूप की अवधि के लिए नलग्लोब सक्षम करें। इस लाइन को लूप से पहले और लूप के shopt -s nullglobबाद इस लाइन को जोड़ें shopt -u nullglob #revert nullglob back to it's normal default state
स्टू-औ

43
+1, और यह सिर्फ मेरे पूरे वॉलपेपर संग्रह की लागत है। मेरे बाद हर कोई, दोहरे का उपयोग करें। "$ फ़ाइल"
बेहरोज

यदि आउटपुट फ़ाइल लूप के अंदर समान है, तो यह लूप के बाहर रीडायरेक्ट करने के लिए बहुत अधिक कुशल है done >results.out(और शायद तब आप ऐपेंड के बजाय ओवरराइट कर सकते हैं, जैसे मैंने यहां ग्रहण किया है)।
ट्रिपलए

आपको व्यक्तिगत परिणाम फ़ाइलें कैसे मिलती हैं जो कस्टम नाम उनके इनपुट फ़ाइलों के लिए हैं?
तिमोथी स्वान

1
dir में बड़ी मात्रा में फ़ाइलों के लिए इस कमांड का उपयोग करके सावधानी बरतें। इसके बजाय ढूँढें -exec का उपयोग करें।
kolisko

181

इस बारे में कैसा है:

find /some/directory -maxdepth 1 -type f -exec cmd option {} \; > results.out
  • -maxdepth 1तर्क किसी भी उपनिर्देशिका में पुनरावर्ती रूप से उतरने से रोकता है। (यदि आप चाहते हैं कि ऐसी नेस्टेड निर्देशिकाएँ संसाधित हों, तो आप इसे छोड़ सकते हैं।)
  • -type -f निर्दिष्ट करता है कि केवल सादे फ़ाइलों को संसाधित किया जाएगा।
  • -exec cmd option {}इसे फ़ाइल नाम के साथ प्रतिस्थापित प्रत्येक फ़ाइल के लिए cmdनिर्दिष्ट के साथ चलाने के लिए कहता हैoption{}
  • \; कमांड के अंत को दर्शाता है।
  • अंत में, सभी व्यक्तिगत cmdनिष्पादन से उत्पादन को पुनर्निर्देशित किया जाता है results.out

हालाँकि, यदि आप उस क्रम की परवाह करते हैं जिसमें फ़ाइलों को संसाधित किया जाता है, तो आप लूप लिखने से बेहतर हो सकते हैं। मुझे लगता findहै कि फाइल इनोड ऑर्डर को प्रोसेस करता है (हालांकि मैं उसके बारे में गलत हो सकता है), जो कि आप जो चाहते हैं वह नहीं हो सकता है।


1
यह फाइलों को प्रोसेस करने का सही तरीका है। लूप के लिए उपयोग कई कारणों से त्रुटि-प्रवण है। साथ ही अन्य कमांड जैसे , statऔर sortकौन-सा कोर्स किस तरह के मापदंड पर निर्भर करता है, इसका उपयोग करके छँटाई की जा सकती है ।
tuxdna

1
अगर मैं दो कमांड चलाना चाहता हूं, तो मैं उन्हें -execविकल्प के बाद कैसे लिंक करूंगा ? क्या मुझे उन्हें सिंगल कोट्स या किसी चीज़ में लपेटना है?
फ़्रेइ

findहमेशा सबसे अच्छा विकल्प होता है क्योंकि आप फ़ाइल नाम पैटर्न को विकल्प के साथ फ़िल्टर -nameकर सकते हैं और आप इसे एक ही आदेश में कर सकते हैं।
जोओ पिमेंटेल फरेरा

3
@frei आपके प्रश्न का उत्तर यहां है: stackoverflow.com/a/6043896/124343247 लेकिन मूल रूप से सिर्फ -execविकल्प जोड़ें :find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;
जोओ पिमेंटेल फेरेरा

2
आप विकल्प के रूप में फ़ाइल नाम का संदर्भ कैसे दे सकते हैं?
तोस्कान

54

मैं अपनी रास्पबेरी पाई पर कमांड लाइन से चला कर कर रहा हूँ:

for i in *;do omxplayer "$i";done

7

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

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

 shopt -s nullglob

 for file in ./*; do
     cmdToRun [option] -- "$file"
 done

यह आपको लूप के लिए सुरक्षित रूप से बाहर निकलने देता है जब अभिव्यक्ति ./*किसी भी फाइल को नहीं लौटाती है (यदि निर्देशिका खाली है)

या एक POSIX शिकायत तरह से ( nullglobहै bashविशिष्ट)

 for file in ./*; do
     [ -f "$file" ] || continue
     cmdToRun [option] -- "$file"
 done

यह आपको लूप के अंदर जाने देता है जब अभिव्यक्ति एक बार के लिए विफल हो जाती है और स्थिति की [ -f "$file" ]जांच करता है कि क्या संयुक्त राष्ट्र का विस्तारित स्ट्रिंग ./*उस निर्देशिका में एक वैध फ़ाइल नाम है, जो नहीं होगा। इस स्थिति में विफलता के कारण, continueहम forलूप में फिर से शुरू करते हैं जो बाद में नहीं चलेगा।

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

--संकेत है कि मामले में जो साधन में कमांड लाइन विकल्प के अंत में, आदेश आदेश झंडे के रूप में इस बिंदु से परे है, लेकिन केवल फ़ाइल नाम के रूप में किसी भी तार पार्स नहीं करना चाहिए।


फ़ाइलनामों को डबल-कोट करना उन मामलों को ठीक से हल करता है जब नामों में ग्लोब कैरेक्टर या व्हाइट-स्पेस होते हैं। लेकिन * निक्स फाइलनाम भी उनमें नई भूमिकाएं शामिल कर सकते हैं। इसलिए हम एकमात्र चरित्र वाले फ़ाइलनाम को डी-लिमिट करते हैं जो मान्य फ़ाइल नाम का हिस्सा नहीं हो सकता है - नल बाइट ( \0)। चूंकि bashआंतरिक रूप से Cस्टाइल स्ट्रिंग्स का उपयोग करता है जिसमें स्ट्रिंग के अंत को इंगित करने के लिए नल बाइट्स का उपयोग किया जाता है, यह इसके लिए सही उम्मीदवार है।

तो कमांड printfके -dविकल्प का उपयोग करके इस NULL बाइट के साथ फ़ाइलों को सीमांकित करने के लिए शेल के विकल्प का उपयोग करना read, हम नीचे कर सकते हैं

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
    cmdToRun [option] -- "$file"
done

nullglobऔर printfचारों ओर लिपटा कर रहे हैं (..), जिसका अर्थ है कि वे मूल रूप से एक उप खोल (बच्चे खोल) में चलाए जा रहे हैं क्योंकि से बचने के लिए nullglobएक बार आदेश बाहर निकलता है, माता पिता के खोल पर प्रतिबिंबित करने के लिए विकल्प। -d ''का विकल्प readआदेश है नहीं POSIX शिकायत है, तो एक की जरूरत है bashखोल यह किया जाए। findकमांड का उपयोग करके इसे किया जा सकता है

while IFS= read -r -d '' file; do
    cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)

उन findकार्यान्वयनों के लिए जो समर्थन नहीं करते -print0(GNU और FreeBSD कार्यान्वयन के अलावा), इसका उपयोग करके अनुकरण किया जा सकता हैprintf

find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --

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

इस सुधार के साथ उपरोक्त कोड का विस्तार, आप कर सकते हैं

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
    cmdToRun [option] -- "$file"
done > results.out

जो मूल रूप से stdout में आपकी फ़ाइल इनपुट के प्रत्येक पुनरावृत्ति के लिए आपकी कमांड की सामग्री को डाल देगा और जब लूप समाप्त हो जाएगा, तो स्टडआउट की सामग्री लिखने और उसे सहेजने के लिए एक बार लक्ष्य फ़ाइल खोलें। findउसी के बराबर संस्करण होगा

while IFS= read -r -d '' file; do
    cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out

1
+1 जाँचने के लिए कि फाइल मौजूद है। यदि एक गैर मौजूदा डायर में खोज की जाती है, तो $ फ़ाइल में रेगेक्स स्ट्रिंग "/ invald_dir / *" होता है न कि एक वैध फ़ाइल नाम।
cdalxndr

3

एक त्वरित और गंदा तरीका है जो कभी-कभी काम पूरा कर लेता है:

find directory/ | xargs  Command 

उदाहरण के लिए वर्तमान निर्देशिका में सभी फ़ाइलों की संख्या ज्ञात करने के लिए, आप यह कर सकते हैं:

find . | xargs wc -l

8
@Hubert आपके फ़ाइलनामों में नए कथानक क्यों हैं ?!
म्यूज़िक

2
यह "क्यों" का सवाल नहीं है, यह शुद्धता का सवाल है - फ़ाइल नाम को मुद्रण योग्य वर्णों को शामिल करने की आवश्यकता नहीं है, उन्हें UTF-8 अनुक्रमों को मान्य करने की आवश्यकता नहीं है। इसके अलावा, जो एक नई लाइन बहुत अधिक एन्कोडिंग पर निर्भर है, एक एनकोडिंग line दूसरे की नईलाइन है। कोड पेज 437 देखें
ह्यूबर्ट करियो

2
cmon, वास्तव में? यह समय का 99.9% काम करता है, और उसने कहा कि "त्वरित और गंदा"
एडोआर्डो

मैं "त्वरित और गंदे" (AKA "टूटा हुआ") बैश स्क्रिप्ट का प्रशंसक नहीं हूं। जल्दी या बाद में यह प्रसिद्ध "मूव्ड ~/.local/share/steamरैन स्टीम " जैसी चीजों में समाप्त होता है । इसने उपयोगकर्ता के स्वामित्व वाली प्रणाली पर सब कुछ हटा दिया। बग रिपोर्ट।
गतिविधि

यह उन फ़ाइलों के साथ भी काम नहीं करेगा जिनके नाम में रिक्त स्थान हैं।
शेमस एस -

2

मुझे एक निर्देशिका से सभी .md फ़ाइलों को दूसरे में कॉपी करने की आवश्यकता थी, इसलिए यहां मैंने वही किया है।

for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done

जो पढ़ने में काफी कठिन है, इसलिए इसे तोड़ दें।

अपनी फ़ाइलों के साथ निर्देशिका में पहले सीडी,

for i in **/*.md; अपने पैटर्न में प्रत्येक फ़ाइल के लिए

mkdir -p ../docs/"$i"उस निर्देशिका को अपनी फ़ाइलों वाले फ़ोल्डर के बाहर डॉक्स फ़ोल्डर में बनाएं। जो उस फ़ाइल के समान नाम के साथ एक अतिरिक्त फ़ोल्डर बनाता है।

rm -r ../docs/"$i" के परिणामस्वरूप बनाई गई अतिरिक्त फ़ोल्डर को निकालें mkdir -p

cp "$i" "../docs/$i" वास्तविक फ़ाइल की प्रतिलिपि बनाएँ

echo "$i -> ../docs/$i" जो तुमने किया, उसकी प्रतिध्वनि करो

; done इसके बाद हमेशा खुश रहें


नोट: **काम करने के लिए, globstarशेल विकल्प को सेट करने की आवश्यकता है:shopt -s globstar
ह्यूबर्ट करियो

2

आप उपयोग कर सकते हैं xarg

ls | xargs -L 1 -d '\n' your-desired-command

-L 1 एक बार में 1 आइटम पास करने का कारण बनता है

-d '\n'के मेकअप उत्पादन lsहै नई लाइन के आधार पर split'ed।


1

@ जिम लुईस के दृष्टिकोण पर आधारित:

यहाँ एक त्वरित समाधान का उपयोग किया गया है findऔर उनकी संशोधन तिथि के अनुसार फाइलों को भी छाँटा गया है:

$ find  directory/ -maxdepth 1 -type f -print0 | \
  xargs -r0 stat -c "%y %n" | \
  sort | cut -d' ' -f4- | \
  xargs -d "\n" -I{} cmd -op1 {} 

छँटाई के लिए देखें:

http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time


यह काम नहीं करेगा यदि फ़ाइलों के नाम में नई सुर्खियाँ हों
ह्यूबर्ट करियो

1
@HubertKario आप के बारे में अधिक पढ़ने के लिए चाहते हो सकता है -print0के लिए findऔर -0के लिए xargs(नई-पंक्तियों सहित) किसी भी खाली स्थान के के बजाय नल केरेक्टर उपयोग करता है।
tuxdna

हां, उपयोग करने -print0से कुछ ऐसा होता है जो मदद करता है, लेकिन पूरी पाइपलाइन को कुछ इस तरह का उपयोग करने की आवश्यकता होती है, और sortनहीं
ह्यूबर्ट करियो

1

मुझे लगता है कि सरल उपाय है:

sh /dir/* > ./result.txt

2
क्या आपने प्रश्न को सही ढंग से समझा है? यह निर्देशिका में प्रत्येक फ़ाइल को शेल के माध्यम से चलाने की कोशिश करेगा - जैसे कि यह एक स्क्रिप्ट थी।
rdas

1

अधिकतम गहराई

मैंने पाया कि यह जिम लेविस के जवाब के साथ अच्छी तरह से काम करता है बस इस तरह से थोड़ा सा जोड़ें:

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
$ find . -maxdepth 1 -type f -name '*.sh' -exec {} \; > results.out

क्रमबद्ध आदेश

यदि आप सॉर्ट क्रम में निष्पादित करना चाहते हैं, तो इसे इस तरह से संशोधित करें:

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -maxdepth 2 -type f -name '*.sh' | sort | bash > results.out

केवल एक उदाहरण के लिए, यह निम्नलिखित आदेश के साथ निष्पादित करेगा:

bash: 1: ./assets/main.sh
bash: 2: ./builder/clean.sh
bash: 3: ./builder/concept/compose.sh
bash: 4: ./builder/concept/market.sh
bash: 5: ./builder/concept/services.sh
bash: 6: ./builder/curl.sh
bash: 7: ./builder/identity.sh
bash: 8: ./concept/compose.sh
bash: 9: ./concept/market.sh
bash: 10: ./concept/services.sh
bash: 11: ./product/compose.sh
bash: 12: ./product/market.sh
bash: 13: ./product/services.sh
bash: 14: ./xferlog.sh

असीमित गहराई

यदि आप निश्चित स्थिति में असीमित गहराई में निष्पादित करना चाहते हैं, तो आप इसका उपयोग कर सकते हैं:

export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -type f -name '*.sh' | sort | bash > results.out

फिर इस तरह से बच्चे निर्देशिका में प्रत्येक फ़ाइलों के शीर्ष पर रखें:

#!/bin/bash
[[ "$(dirname `pwd`)" == $DIR ]] && echo "Executing `realpath $0`.." || return

और मूल फ़ाइल के शरीर में कहीं:

if <a condition is matched>
then
    #execute child files
    export DIR=`pwd`
fi
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.