কমান্ড বিকল্পের জন্য শেল ভেরিয়েবল ব্যবহার করে


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?

যে কোনও পসিক্স শেলের মধ্যে, কেউ এর জন্য অবস্থানগত পরামিতিগুলির তালিকা ব্যবহার করতে পারে:

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

rsync "$@" source/ target

আবার, ডাবল উদ্ধৃতি এখানে সম্প্রসারণ $@গুরুত্বপূর্ণ।

জড়িত সম্পর্কিত:


সমস্যাটি হ'ল আপনি যখন বিকল্পের দুটি সেটকে স্ট্রিংয়ে রাখবেন তখন --excludeবিকল্পটির মানটির একক উদ্ধৃতিগুলি সেই মানটির অংশ হয়ে যায়। তাই,

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

কাজ করেছে ... ... তবে স্বতন্ত্রভাবে উদ্ধৃত এন্ট্রি সহ একটি অ্যারে বা অবস্থানগত পরামিতি ব্যবহার করা ভাল (নিরাপদ হিসাবে)। এটি করার ফলে আপনাকে যদি প্রয়োজন হয় তবে এগুলিতে ফাঁকা জায়গাগুলির জিনিস ব্যবহার করার অনুমতি দেয় এবং অপশনগুলিতে শেল ফাইলনেম জেনারেশন (গ্লোব্বিং) করা এড়ানো যায়।


¹ প্রদত্ত যেটি $IFSসংশোধিত নয় এবং এমন কোনও ফাইল নেই যার নাম --exclude=.বর্তমান ডিরেক্টরিতে শুরু হয় এবং nullglobবা failglobশেল বিকল্পগুলি সেট করা নেই।


একটি অ্যারের ব্যবহার দুর্দান্ত কাজ করে, আপনার বিস্তারিত উত্তরের জন্য আপনাকে ধন্যবাদ!
ফ্লোরিয়ান ব্রুকার

3

@ কুসালানন্দ ইতিমধ্যে প্রাথমিক সমস্যা এবং এটি কীভাবে সমাধান করবেন সে সম্পর্কে ব্যাখ্যা করেছেন এবং @্লেইন জ্যাকম্যানের সাথে লিঙ্কযুক্ত বাশ FAQ এন্ট্রিও প্রচুর দরকারী তথ্য সরবরাহ করে। এই সংস্থানগুলির উপর নির্ভর করে আমার সমস্যার মধ্যে কী ঘটছে তার একটি বিশদ বিবরণ এখানে।

আমরা একটি ছোট স্ক্রিপ্ট ব্যবহার করব যা এর প্রতিটি আর্গুমেন্টকে আলাদা লাইনে প্রিন্ট করে জিনিসগুলি ( 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

যখন আমরা ফাংশন এবং শেল স্ক্রিপ্টগুলি লিখি, যেখানে আর্গুমেন্টগুলি প্রক্রিয়াজাতকরণের জন্য পাস করা হয়, তখন আর্গুমেন্টগুলি সংখ্যাসূচক-নামযুক্ত ভেরিয়েবলগুলি যেমন পাস হবে passed 1, $ 2, $ 3

উদাহরণস্বরূপ :

bash my_script.sh Hello 42 World

ভিতরে my_script.sh, কমান্ডগুলি $1হ্যালো, $2তে 42এবং এর $3জন্য উল্লেখ করতে ব্যবহার করবেWorld

ভেরিয়েবল রেফারেন্স, $0বর্তমান স্ক্রিপ্টের নাম, যেমন প্রসারিত হবেmy_script.sh

ভেরিয়েবল হিসাবে কমান্ড সহ পুরো কোডটি খেলবেন না।

মনে রাখবেন :

1 স্ক্রিপ্টগুলিতে সমস্ত-রাজধানী পরিবর্তনশীল নামগুলি ব্যবহার করা এড়িয়ে চলুন।

2 ব্যাককোটিস ব্যবহার করবেন না, পরিবর্তে $ (...) ব্যবহার করুন, এটি আরও ভাল বাসা বেঁধেছে।

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"
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.