यदि कोई स्क्रिप्ट निष्पादित करने के लिए पैटर्न से मेल खाती फाइलें हैं तो परीक्षण करें


29

मैं ifयह जांचने के लिए एक बयान लिखने की कोशिश कर रहा हूं कि क्या कोई फाइल एक निश्चित पैटर्न से मेल खाती है। यदि निर्देशिका में कोई पाठ फ़ाइल है तो उसे दी गई स्क्रिप्ट को चलाना चाहिए।

वर्तमान में मेरा कोड:

if [ -f /*.txt ]; then ./script fi

कृपया कुछ विचार दें; मैं केवल स्क्रिप्ट चलाना चाहता हूं यदि .txtनिर्देशिका में है।


3
क्या आप सुनिश्चित हैं कि "निर्देशिका" माना जाता है /? इसके अलावा, आप पहले एक अर्धविराम याद कर रहे हैं fi
depquid

सबसे साफ मजबूत समाधान जिसका मैंने उपयोग किया है, findजैसा कि स्टैकओवरफ्लो पर यहाँ बताया गया है
जोशुआ गोल्डबर्ग

जवाबों:


39
[ -f /*.txt ]

यदि कोई एक (और केवल एक) गैर-छुपी हुई फ़ाइल /जिसका नाम समाप्त होता है, .txtऔर यदि वह फ़ाइल एक नियमित फ़ाइल है या एक नियमित फ़ाइल के लिए सिम्लिंक है, तो केवल तभी वापस आएगा ।

ऐसा इसलिए है क्योंकि वाइल्डकार्ड कमांड (यहां [) में पारित होने से पहले शेल द्वारा विस्तारित किए जाते हैं ।

अगर वहाँ एक है तो /a.txtऔर /b.txt, [5 तर्क पारित हो जाएगा: [, -f, /a.txt, /b.txtऔर ][फिर शिकायत करेंगे कि -fबहुत सारे तर्क दिए जाते हैं।

आपको लगता है कि जाँच करना चाहते हैं *.txtपैटर्न कम से कम एक गैर छिपा फ़ाइल (नियमित या नहीं) के लिए विस्तारित:

shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
  ./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"

shopt -s nullglobहै bashविशिष्ट है, लेकिन गोले की तरह ksh93, zsh, yash, tcshबराबर बयान है।

ध्यान दें कि यह निर्देशिका की सामग्री को पढ़कर उन फ़ाइलों को ढूँढता है, यह उन फ़ाइलों की कोशिश नहीं करता है और उन सभी तक पहुँच प्राप्त करता है जो इसे समाधानों की तुलना में अधिक कुशल बनाता है जो शेल द्वारा गणना की गई फ़ाइलों की तरह lsया statउस सूची पर आदेशों को कहते हैं ।

मानक shसमतुल्य होगा:

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "$@"
esac

समस्या यह है कि बॉर्न या पोसिक्स गोले के साथ, यदि कोई पैटर्न मेल नहीं खाता है, तो यह अपने आप फैलता है। इसलिए अगर इसका *.txtविस्तार होता है *.txt, तो आप नहीं जानते कि क्या यह है क्योंकि .txtनिर्देशिका में कोई फ़ाइल नहीं है या क्योंकि वहाँ एक फ़ाइल कहा जाता है *.txt। उपयोग [*].txt *.txtकरने से दोनों के बीच भेदभाव करने की अनुमति मिलती है।


[ -f /*.txt ]की तुलना में काफी तेज है compgen
डैनियल बॉहेम

@ डैनियलबोहमर [ -f /*.txt ]गलत होगा, लेकिन एक निर्देशिका पर मेरे परीक्षण में 3425फाइलें शामिल हैं , 94जिनमें से गैर-छिपी हुई txt फाइलें हैं, मेरे मामले में जितनी बार (दोनों बार 10000 बार दोहराया गया) दोनों के compgen -G "*.txt" > /dev/null 2>&1रूप में तेजी से दिखाई देती हैं set -- *.txt; [ "$#" -gt 0 ]
स्टीफन चेज़लस

11

आप हमेशा उपयोग कर सकते हैं find:

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

स्पष्टीकरण:

  • find . : वर्तमान निर्देशिका खोजें
  • -maxdepth 1: उपनिर्देशिका नहीं खोजें
  • -type f : केवल नियमित फ़ाइलें खोजें
  • name "*.txt" : में समाप्त होने वाली फ़ाइलों के लिए खोज .txt
  • 2>/dev/null : त्रुटि संदेशों को पुनर्निर्देशित करें /dev/null
  • | grep -q . : किसी भी वर्ण के लिए grep, यदि कोई वर्ण नहीं मिला, तो वह गलत हो जाएगा।
  • && ./script: ./scriptपिछली कमांड सफल होने पर ही निष्पादित करें ( &&)

2
findकेवल गलत है अगर यह फ़ाइलों की तलाश में परेशानी है, अगर यह किसी भी फ़ाइल नहीं मिलता है। आप आउटपुट grep -q .को जांचने के लिए पाइप करना चाहते हैं कि क्या वह कुछ पाता है।
स्टीफन चेज़लस

@StephaneChazelas आप बिल्कुल सही हैं। अजीब हालांकि, मैंने इसका परीक्षण किया और यह काम करने लगा। कुछ अजीब किया जाना चाहिए क्योंकि यह किसी भी अधिक नहीं है। कब मिलेगा "फाइलों को खोजने में परेशानी"?
terdon

@terdon, जैसे कि जब कुछ निर्देशिका अप्राप्य होती है, या I / O त्रुटियां या किसी भी सिस्टम द्वारा की गई किसी भी त्रुटि के कारण वह वापस आती है। उस मामले में, बाद की कोशिश करें chmod a-x .
स्टीफन चेज़लस

8

एक संभावित समाधान भी बैश बिलिन है compgen। यह कमांड एक ग्लोबिंग पैटर्न के लिए सभी संभावित मैचों को लौटाता है और इसमें एक एक्जिट कोड होता है जो बताता है कि क्या कोई फाइल मेल खाती है।

compgen -G "/*.text" > /dev/null && ./script

मुझे यह प्रश्न ऐसे समाधानों की तलाश करते हुए मिला जो हालांकि तेज हैं।


1
अच्छा खोजो! यदि आप एक बहु-बाइट स्थान में हैं, तो आप इसे थोड़ा सुधार सकते हैं LC_ALL=C compgen -G "*.txt" > /dev/null
स्टीफन चेज़लस

7

यहाँ यह करने के लिए एक लाइनर है:

$ ls
file1.pl  file2.pl

फाइलें मौजूद हैं

$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists

फ़ाइलें मौजूद नहीं हैं

$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist

यह दृष्टिकोण बैश में ऑपरेटरों ||और &&ऑपरेटरों का उपयोग करता है । ये "या" और "और" ऑपरेटर हैं।

इसलिए यदि स्टेट कमांड $?0 के बराबर लौटता है तो पहले echoको बुलाया जाता है, यदि यह 1 रिटर्न करता है, तो दूसरा echoकहा जाता है।

स्टेट से परिणाम

# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1

# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0

यह प्रश्न स्टैकओवरफ़्लो पर बड़े पैमाने पर कवर किया गया है:


1
statजब ls -dवही कर सकते हैं तो गैर मानक का उपयोग क्यों करें ?
स्टीफन चेजलस

मैंने सोचा था कि ls -dएक निर्देशिका सूची? काम नहीं कर रहा था जब मैंने ls -d *.plउदाहरण के लिए फाइलों के साथ एक निर्देशिका को सूचीबद्ध करने की कोशिश की ।
स्लम

आप के बाईं ओर बयान की जगह ले सकता &&द्वारा ls *.txtऔर यह अच्छी तरह के रूप में काम करेंगे। सुनिश्चित करें कि आप /dev/null@slm द्वारा सुझाए गए अनुसार स्टडआउट और stderr भेजें।
अंक

1
आप का उपयोग करते हैं ls *.txtऔर कोई भी फ़ाइलें निर्देशिका के भीतर उपस्थित देखते हैं कि यह एक वापस आ जाएगी $? = 2, जो अगर अभी भी साथ काम तो होगा, लेकिन यह चुनने के लिए मेरी कारणों में से एक था statसे अधिक ls। मुझे सफलता के लिए 0 और असफलता के लिए 1 चाहिए था।
slm

ls -dउनकी सामग्री के बजाय निर्देशिका को सूचीबद्ध करना है। तो ls -dबस lstatफ़ाइल पर करता है, जैसे GNU statकरता है। गैर-शून्य निकास स्थिति आदेश विफलता पर क्या लौटाते हैं, सिस्टम विशिष्ट है, यह उन पर धारणा बनाने के लिए बहुत कम समझ में आता है।
स्टीफन चेजलस

4

जैसा कि चेज़लस बताते हैं, वाइल्डकार्ड विस्तार एक से अधिक फ़ाइल से मेल खाता है, तो आपकी स्क्रिप्ट विफल हो जाएगी।

हालाँकि, एक ट्रिक है जिसका मैं उपयोग करता हूं ( यहां तक ​​कि मुझे यह बहुत पसंद नहीं है )

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

यह काम किस प्रकार करता है?

वाइल्डकार्ड विस्तार एक फ़ाइल नाम के साथ मेल खाता है, हम पहले वाले से मिलते हैं अगर कुछ है, अन्यथा शून्य अगर कोई मैच नहीं है।


IMO यह यहाँ सबसे कम बुरा जवाब है। वे सभी बहुत भयानक लग रहे हैं, हालांकि एक मूल विशेषता भाषा से गायब है।
प्लगवस

@plugwash यह जानबूझकर है ... * निक्स शेल स्क्रिप्ट में कुछ मूल प्रवाह नियंत्रण और कुछ अन्य बाधाएं हैं और समाप्त होती हैं, लेकिन दिन के अंत में यह काम एक साथ अन्य कमांड को गोंद करना है। अगर बैश चूसता है ... यह इसलिए है क्योंकि आप इसे से जो कमांड का उपयोग करते हैं वह चूसना है
cb88

2
यह गलत तर्क है (और आप उद्धरण याद कर रहे हैं)। यह जाँचता है कि पहली मिलान फ़ाइल एक नियमित फ़ाइल है या नहीं। यह अच्छी तरह से एक गैर-नियमित फ़ाइल हो सकती है लेकिन कई अन्य .txtफाइलें भी हो सकती हैं जो नियमित रूप से होती हैं । उदाहरण के लिए प्रयास करें mkdir a.txt; mkfifo b.txt; echo regular > c.txt
स्टीफन चेज़लस

1

के रूप में सरल:

cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi

wc -l विस्तारित वाइल्डकार्ड में लाइनों को गिना जाता है।


1
डाउनवोट: यह कोड की एक छोटी मात्रा में शुरुआती एंटीपैटर्न की एक अद्भुत संख्या एकत्र करता है। आपको lsआउटपुट को पार्स नहीं करना चाहिए और लगभग कभी भी $?सीधे जांच नहीं करनी चाहिए क्योंकि ifपहले से ही ऐसा है। इसके अलावा, यह देखने के लिए कि क्या कुछ हुआ है उसी तरह गलत तरीके से उपयोग wcकिया गया है।
ट्रिपलआई

0

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

यहाँ एक वैकल्पिक संरचना है जिसे मैंने कल परीक्षण किया था:

$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi yes $ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi no

ग्रेप से निकास मूल्य नियंत्रण संरचना के माध्यम से मार्ग का निर्धारण करता प्रतीत होता है। यह शेल पैटर्न के बजाय नियमित अभिव्यक्तियों के साथ भी परीक्षण करता है। मेरे कुछ सिस्टम में "pcregrep" कमांड है जो बहुत अधिक परिष्कृत रेगेक्स मैचों की अनुमति देता है।

(मैंने इसे पार्स करने के लिए उपरोक्त आलोचना पढ़ने के बाद कमांड प्रतिस्थापन में "ls" हटाने के लिए इस उत्तर को संपादित किया।)


-2

यदि आप एक खंड का उपयोग करना चाहते हैं, तो गणना का मूल्यांकन करें:

if (( `ls *.txt 2> /dev/null|wc -l` ));then...
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.