कमांड विकल्पों के लिए शेल चरों का उपयोग करना


19

एक बैश स्क्रिप्ट में, मैं उन विकल्पों को संग्रहीत करने की कोशिश कर रहा हूं जो मैं rsyncएक अलग चर में उपयोग कर रहा हूं । यह सरल विकल्पों (जैसे --recursive) के लिए ठीक काम करता है , लेकिन मैं इसके साथ समस्याओं में चल रहा हूं --exclude='.*':

$ find source
source
source/.bar
source/foo

$ rsync -rnv --exclude='.*' source/ dest
sending incremental file list
foo

sent 57 bytes  received 19 bytes  152.00 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

$ RSYNC_OPTIONS="-rnv --exclude='.*'"

$ rsync $RSYNC_OPTIONS source/ dest
sending incremental file list
.bar
foo

sent 78 bytes  received 22 bytes  200.00 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

जैसा कि आप देख सकते हैं, "मैन्युअल रूप से" पास --exclude='.*'करना rsyncठीक काम करता है ( .barकॉपी नहीं किया गया है), यह तब काम नहीं करता जब विकल्प पहले एक चर में संग्रहीत होते हैं।

मैं अनुमान लगा रहा हूं कि यह या तो उद्धरण या वाइल्डकार्ड (या दोनों) से संबंधित है, लेकिन मैं यह पता लगाने में सक्षम नहीं हूं कि वास्तव में क्या गलत है।



1
या इस उत्तर को हमारी अपनी साइट पर देखें ।
स्कॉट

जवाबों:


38

सामान्य तौर पर, अलग-अलग वस्तुओं की सूची को एक ही तार में बदलना एक बुरा विचार है, फिर चाहे वह कमांड लाइन के विकल्पों की सूची हो या पथनामों की सूची हो।

इसके बजाय एक सरणी का उपयोग करना:

rsync_options=( -rnv --exclude='.*' )

या

rsync_options=( -r -n -v --exclude='.*' )

और बादमें...

rsync "${rsync_options[@]}" source/ target

इस तरह, व्यक्तिगत विकल्पों का उद्धरण बनाए रखा जाता है (जब तक आप दोहरे विस्तार का विस्तार करते हैं ${rsync_options[@]} )। यह आपको आसानी से सरणी के व्यक्तिगत प्रविष्टियों में हेरफेर करने की अनुमति देता है, क्या आपको कॉल करने से पहले ऐसा करने की आवश्यकता होगी rsync

किसी भी POSIX शेल में, कोई इसके लिए स्थितीय मापदंडों की सूची का उपयोग कर सकता है:

set -- -rnv --exclude='.*'

rsync "$@" source/ target

फिर, के विस्तार का दोहराव $@ यहाँ महत्वपूर्ण है।

संबंधित संबंधित:


मुद्दा यह है कि जब आप विकल्प के दो सेट को एक स्ट्रिंग में रखते हैं, तो --excludeविकल्प के मूल्य के एकल उद्धरण उस मूल्य का हिस्सा बन जाते हैं। इसलिये,

RSYNC_OPTIONS='-rnv --exclude=.*'

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


¹ प्रदान की है कि $IFSसंशोधित किया गया है नहीं और कोई फ़ाइल जिसका नाम के साथ शुरू होता है कि --exclude=.मौजूदा निर्देशिका में, और कहा कि nullglobया failglobखोल विकल्पों को सेट नहीं कर रहे हैं।


सरणी का उपयोग करना ठीक काम करता है, आपके विस्तृत उत्तर के लिए धन्यवाद!
फ्लोरियन ब्रूकर

3

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

हम एक छोटी स्क्रिप्ट का उपयोग करेंगे जो चीजों को चित्रित करने के लिए एक अलग लाइन पर अपने प्रत्येक तर्क को प्रिंट करती है ( argtest.bash):

#!/bin/bash

for var in "$@"
do
    echo "$var"
done

पासिंग विकल्प "मैन्युअल रूप से":

$ ./argtest.bash -rnv --exclude='.*'
-rnv
--exclude=.*

जैसी उम्मीद थी, भागों -rnvऔर --exclude='.*', दो तर्क में विभाजित हैं के रूप में वे गैर उद्धृत खाली स्थान के द्वारा अलग किया जाता है (यह भी कहा जाता है शब्द बंटवारे )।

यह भी ध्यान दें कि आसपास के उद्धरण .*हटा दिए गए हैं: एकल उद्धरण विशेष व्याख्या के बिना अपनी सामग्री को पारित करने के लिए शेल को बताते हैं , लेकिन खुद को उद्धरण कमांड में पारित नहीं किया जाता है

यदि हम अब एक चर में स्ट्रिंग के रूप में विकल्पों को संग्रहीत करते हैं (जैसा कि एक सरणी का उपयोग करने के लिए विरोध किया जाता है), तो उद्धरण हटाए नहीं जाते हैं :

$ OPTS="--exclude='.*'"

$ ./argtest.bash $OPTS
--exclude='.*'

यह दो कारणों से है: डबल कोट का उपयोग करते समय परिभाषित $OPTSएकल उद्धरण के विशेष उपचार को रोकते हैं, इसलिए उत्तरार्द्ध मूल्य का हिस्सा हैं:

$ echo $OPTS
--exclude='.*'

जब हम अब $OPTSएक कमांड के तर्क के रूप में उपयोग करते हैं तो पैरामीटर विस्तार से पहले उद्धरण संसाधित होते हैं , इसलिए उद्धरण में$OPTS "बहुत देर से" होते हैं।

इसका मतलब यह है कि (मेरी मूल समस्या में) rsyncपैटर्न के '.*'बजाय बहिष्कृत पैटर्न (उद्धरण के साथ!) का उपयोग करता है .*- यह उन फ़ाइलों को बाहर करता है जिनका नाम एकल उद्धरण के साथ शुरू होता है और डॉट के साथ समाप्त होता है। जाहिर है कि ऐसा नहीं था।

परिभाषित करते समय वर्कअराउंड डबल कोट्स को छोड़ना होगा $OPTS:

$ OPTS2=--exclude='.*'

$ ./argtest.bash $OPTS2
--exclude=.*

हालांकि, अधिक जटिल मामलों में सूक्ष्म अंतर के कारण हमेशा चर असाइनमेंट को उद्धृत करना एक अच्छा अभ्यास है ।

जैसा कि @ कुसलानंद ने कहा, उद्धृत .*न करने से भी काम होता। मैंने पैटर्न विस्तार को रोकने के लिए उद्धरण जोड़े थे , लेकिन इस विशेष मामले में यह आवश्यक नहीं था :

$ ./argtest.bash --exclude=.*
--exclude=.*

यह पता चलता है कि बैश पैटर्न का विस्तार करता है , लेकिन पैटर्न --exclude=.*किसी भी फाइल से मेल नहीं खाता है, इसलिए पैटर्न कमांड पर पारित किया जाता है। की तुलना करें:

$ touch some_file

$ ./argtest.bash some_*
some_file

$ ./argtest.bash does_not_exit_*
does_not_exit_*

हालाँकि, पैटर्न को उद्धृत करना खतरनाक नहीं है, क्योंकि यदि (जो भी कारण से) कोई फ़ाइल मिलान होता है --exclude=.*तो पैटर्न का विस्तार हो जाता है:

$ touch -- --exclude=.special-filenames-happen

$ ./argtest.bash --exclude=.*
--exclude=.special-filenames-happen

अंत में, आइए देखें कि एक सरणी का उपयोग मेरी उद्धरण समस्या को क्यों रोकता है (कमांड तर्कों को संग्रहीत करने के लिए सरणियों का उपयोग करने के अन्य लाभों के अलावा)।

सरणी को परिभाषित करते समय, शब्द बंटवारा और उद्धरण हैंडलिंग उम्मीद के अनुसार होता है:

$ ARRAY_OPTS=( -rnv --exclude='.*' )

$ echo length of the array: "${#ARRAY_OPTS[@]}"
length of the array: 2

$ echo first element: "${ARRAY_OPTS[0]}"
first element: -rnv

$ echo second element: "${ARRAY_OPTS[1]}"
second element: --exclude=.*

कमांड के विकल्पों को पास करते समय, हम सिंटैक्स का उपयोग करते हैं "${ARRAY[@]}", जो एरे के प्रत्येक तत्व को एक अलग शब्द में विस्तारित करता है:

$ ./argtest.bash "${ARRAY_OPTS[@]}"
-rnv
--exclude=.*

इस सामान ने मुझे लंबे समय तक भ्रमित किया था, इसलिए इस तरह की एक विस्तृत व्याख्या सहायक है।
जो

0

जब हम फ़ंक्शन और शेल स्क्रिप्ट लिखते हैं, जिसमें तर्क संसाधित होने के लिए पारित किए जाते हैं, तो तर्क को अंकीय रूप से नामित चर नाम दिया जाएगा, जैसे $ 1, $ 2, $ 3

उदाहरण के लिए :

bash my_script.sh Hello 42 World

अंदर my_script.sh, आदेशों का उपयोग होगा $1, नमस्ते का उल्लेख करने $2के लिए 42और, $3के लिएWorld

चर संदर्भ, $0वर्तमान स्क्रिप्ट के नाम में विस्तार करेगा, जैसेmy_script.sh

चर के रूप में आदेशों के साथ पूरा कोड न खेलें।

ध्यान रखें :

1 लिपियों में सभी राजधानियों के चर नाम का उपयोग करने से बचें।

2 backquotes का उपयोग न करें, इसके बजाय $ (...) का उपयोग करें, यह बेहतर तरीके से घोंसला बनाता है।

if [ $# -ne 2 ]
then
    echo "Usage: $(basename $0) DIRECTORY BACKUP_DIRECTORY"
    exit 1
fi

directory=$1
backup_directory=$2
current_date=$(date +%Y-%m-%dT%H-%M-%S)
backup_file="${backup_directory}/${current_date}.backup"

tar cv "$directory" | openssl des3 -salt | split -b 1024m - "$backup_file"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.