bash स्क्रिप्ट के लिए dd-style पैरामीटर


19

मैं पैराशूट को बैश स्क्रिप्ट, डीडी-स्टाइल में पास करना चाहूंगा। मूल रूप से, मुझे चाहिए

./script a=1 b=43

के रूप में एक ही प्रभाव है

a=1 b=43 ./script

मुझे लगा कि मैं इसे हासिल कर सकता हूं:

for arg in "$@"; do
   eval "$arg";
done

यह सुनिश्चित करने का एक अच्छा तरीका क्या है कि evalसुरक्षित है, यानी "$arg"एक स्थिर (कोई कोड निष्पादन), चर असाइनमेंट से मेल खाता है?

या ऐसा करने का एक बेहतर तरीका है? (मैं इसे सरल रखना चाहूंगा)।


इसे बैश के साथ टैग किया गया है। क्या आप एक Posix शिकायत समाधान चाहते हैं, या आप बैश समाधान स्वीकार करेंगे?
रिसी

टैग क्या कहता है मेरा क्या मतलब है :)
PSkocik

ठीक है आप इसे एक =विभाजक के साथ एक पैटर्न के रूप में पार्स कर सकते हैं और अधिक सावधानी से निर्मित निष्कासन के साथ असाइनमेंट कर सकते हैं। बस सुरक्षा के लिए, निजी उपयोग के लिए, मैं इसे वैसे ही करूंगा जैसे आपने किया था।
ओरियन

जवाबों:


16

आप इसे बेद के बिना भी कर सकते हैं (और बिना कृत्रिम पलायन के):

for arg in "$@"; do
  if [[ $arg =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; then
    declare +i +a +A "$arg"
  fi
done

संपादित करें: स्टीफन चेज़ेलस की एक टिप्पणी के आधार पर, मैंने झंडे को घोषणा में जोड़े जाने से बचने के लिए चर को पहले से ही एक सरणी या पूर्णांक चर के रूप में घोषित किया है, जो कई मामलों से बचेंगे जिनमें तर्क declareके मूल्य भाग का मूल्यांकन किया जाएगा key=val। ( +aउदाहरण के लिए, यदि सेट होने वाले चर को पहले से ही एक चर चर के रूप में घोषित किया गया है, तो एक त्रुटि होगी।) ये सभी कमजोरियाँ मौजूदा (सरणी या पूर्णांक) चर को पुन: असाइन करने के लिए इस सिंटैक्स का उपयोग करने से संबंधित हैं, जो आमतौर पर अच्छी तरह से जाना जाता है। खोल चर।

वास्तव में, यह इंजेक्शन हमलों के एक वर्ग का एक उदाहरण है जो समान रूप से- evalआधारित समाधानों को प्रभावित करेगा : यह वास्तव में केवल ज्ञात तर्क नामों को अनुमति देने के लिए बेहतर होगा, जो कमांड-लाइन में मौजूद होने वाले चर को नेत्रहीन रूप से सेट करने के लिए होता है। ( PATHउदाहरण के लिए, कमांड लाइन सेट होने पर क्या होता है, इस पर विचार करें । या PS1कुछ मूल्यांकन शामिल करने के लिए रीसेट करता है जो अगले प्रॉम्प्ट डिस्प्ले पर होगा।)

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

बाद के दृष्टिकोण के एक उदाहरण के रूप में:

# Could use this array for default values, too.
declare -A options=([bs]= [if]= [of]=)
for arg in "$@"; do
  # Make sure that it is an assignment.
  # -v is not an option for many bash versions
  if [[ $arg =~ ^[[:alpha:]_][[:alnum:]_]*= &&
        ${options[${arg%%=*}]+ok} == ok ]]; then
    declare "$arg"
    # or, to put it into the options array
    # options[${arg%%=*}]=${arg#*=}
  fi
done

1
लगता है रेक्सक्स कोष्ठक गलत है। शायद इसके बजाय इसका उपयोग करें ^[[:alpha:]_][[:alnum:]_]*=:?
lcd047

1
@ lcd047: foo=खाली स्ट्रिंग के लिए फू सेट करने का एकमात्र तरीका है, इसलिए इसे अनुमति दी जानी चाहिए (IMHO)। मैंने कोष्ठक तय किया, धन्यवाद।
रिसी

3
declareके रूप में के रूप में खतरनाक है eval(एक भी बुरा कह सकता है क्योंकि यह स्पष्ट नहीं है कि यह उतना ही खतरनाक है)। उदाहरण के लिए उस 'DIRSTACK=($(echo rm -rf ~))'तर्क के साथ कॉल करने का प्रयास करें ।
स्टीफन चेज़लस

1
@PSkocik: +x"नहीं -x" है। -a= अनुक्रमित सरणी, -A= साहचर्य सरणी, -i= पूर्णांक चर। इस प्रकार: अनुक्रमित सरणी नहीं, सहयोगी सरणी नहीं, पूर्णांक नहीं।
lcd047

1
ध्यान दें कि अगले संस्करण के साथ bash, आपको +cकंपाउंड वैरिएबल +Fको अक्षम करने या फ्लोटिंग को अक्षम करने की आवश्यकता हो सकती है। मैं अभी भी उपयोग करूँगा evalजहाँ आप जानते हैं कि आप कहाँ खड़े हैं।
स्टीफन चेज़लस

9

POSIX एक ( विशेष चर जैसे समस्याओं से बचने $<prefix>varके $varलिए सेट IFS/ PATH...):

prefix=my_prefix_
for var do
  case $var in
    (*=*)
       case ${var%%=*} in
         "" | *[!abcdefghijiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]*) ;;
         (*) eval "$prefix${var%%=*}="'${var#*=}'
       esac
  esac
done

कहा जाता है myscript x=1 PATH=/tmp/evil %=3 blah '=foo' 1=2, यह असाइन करेगा:

my_prefix_x <= 1
my_prefix_PATH <= /tmp/evil
my_prefix_1 <= 2

6

lcd047 का समाधान हार्डकोड DD_OPT_पूर्वसर्ग के साथ फिर से जोड़ा गया :

while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
  eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
done

फ्रॉस्ट्सचुट्ज़ अधिकांश रिफैक्टिंग के लिए ऋण के हकदार हैं।

मैंने इसे वैश्विक चर के रूप में एक स्रोत फ़ाइल में रखा है:

DD_OPTS_PARSE=$(cat <<'EOF'
  while [[ $1 =~ ^[[:alpha:]_][[:alnum:]_]*= ]]; do
    eval "DD_OPT_${1%%=*}"='${1#*=}'; shift;
  done
EOF
)

eval "$DD_OPTS_PARSE" सभी जादू करता है।

कार्यों के लिए एक संस्करण होगा:

DD_OPTS_PARSE_LOCAL="${PARSE_AND_REMOVE_DD_OPTS/DD_OPT_/local DD_OPT_}"

उपयोग में:

eval "$DD_OPTS_PARSE_LOCAL"

मैंने इसमें से एक रेपो बनाया , परीक्षण और एक README.md के साथ पूरा किया। तब मैंने इसका उपयोग एक जीथब एपीआई सीएलआई रैपर मैं लिख रहा था, और मैंने उसी रेपर का उपयोग करके रेपो (बूटस्ट्रैपिंग मजेदार है) के जीथब क्लोन को सेटअप किया ।

बस एक लाइन में बैश स्क्रिप्ट के लिए सुरक्षित पैरामीटर गुजर रहा है। का आनंद लें। :)


1
लेकिन आप *=*कुंजी / वैल को प्रतिस्थापित करने से रोक सकते हैं और रोक सकते हैं जहां कोई = नहीं है। (जब से आप
परिक्रमा

1
वास्तव में आप लूप और अगर के लिए छुटकारा पा सकते हैं और $ 1 के बजाय उपयोग करते हैं, क्योंकि आप शिफ्टिंग और सभी कर रहे हैं ...
frostschutz

1
हेह, इस बात का प्रमाण कि बुद्धिशीलता काम करती है। :)
lcd047

1
सुबह के विचारों की कटाई: आप छुटकारा भी पा सकते हैं keyऔर val, और बस लिख सकते हैं eval "${1%%=*}"=\${1#*=}। लेकिन यह बहुत अधिक है जहाँ तक यह जाता है, eval "$1"जैसा कि @ रिसी के declare "$arg"काम नहीं करेगा, जाहिर है। इसके अलावा तरह बातें स्थापित करने की सावधान रहना PATHया PS1
lcd047

1
धन्यवाद - मुझे लगा कि मूल्यांकन किया गया चर था। मैं मेरे साथ आपके धैर्य की सराहना करता हूं - यह बहुत स्पष्ट रूप से स्पष्ट है। वैसे भी, नहीं - उस कल्पना के अलावा, यह अच्छा लग रहा है। आप जानते हैं कि आप इसे किसी भी शेल में काम करने के लिए बढ़ा सकते हैं case। शायद यह कोई फर्क नहीं पड़ता है, लेकिन सिर्फ मामले में आप नहीं जानते ...
mikeserv

5

क्लासिक बॉर्न शेल समर्थित, और बैश और कॉर्न शेल अभी भी समर्थन करते हैं, एक -kविकल्प। जब यह प्रभाव में होता है, ddतो कमांड लाइन पर कहीं भी किसी भी तरह का 'कमांड' विकल्प स्वचालित रूप से पर्यावरण चर में बदल दिया जाता है:

$ set -k
$ echo a=1 b=2 c=3
$ 

यह आश्वस्त होना थोड़ा कठिन है कि वे पर्यावरण चर रहे हैं; यह काम मेरे लिए चल रहा है:

$ set -k
$ env | grep '^[a-z]='   # No environment a, b, c
$ bash -c 'echo "Args: $*" >&2; env' a=1 b=2 c=3 | grep '^[a-z]='
Args: 
a=1
b=2
c=3
$ set +k
$ bash -c 'echo "Args: $*" >&2; env' a=1 b=2 c=3 | grep '^[a-z]='
Args: b=2 c=3
$

पहला env | grepएकल लोअर-केस अक्षर के साथ कोई पर्यावरण चर नहीं दिखाता है। पहले से bashपता चलता है कि स्क्रिप्ट के माध्यम से निष्पादित कोई तर्क नहीं है -c, और पर्यावरण में तीन एकल-अक्षर चर शामिल हैं। set +kरद्द -k, और शो है कि एक ही आदेश अब यह करने के लिए पारित कर दिया तर्क हैं। ( स्क्रिप्ट के a=1रूप में व्यवहार किया गया था $0; आप उचित प्रतिध्वनि के साथ यह भी साबित कर सकते हैं।)

यह वही प्राप्त करता है जो सवाल पूछता है - कि टाइपिंग ./script.sh a=1 b=2के समान होना चाहिए a=1 b=2 ./script.sh

इस बात से अवगत रहें कि यदि आप स्क्रिप्ट के अंदर इस तरह की कोशिश करते हैं तो आप समस्याओं में भाग जाते हैं:

if [ -z "$already_invoked_with_minus_k" ]
then set -k; exec "$0" "$@" already_invoked_with_minus_k=1
fi

"$@"शब्दशः व्यवहार किया जाता है; यह नहीं काम शैली चर (दोनों में खोजने के लिए फिर से विश्लेषण किया जाता है bashऔर ksh)। मैंने कोशिश की:

#!/bin/bash

echo "BEFORE"
echo "Arguments:"
al "$@"
echo "Environment:"
env | grep -E '^([a-z]|already_invoked_with_minus_k)='
if [ -z "$already_invoked_with_minus_k" ]
then set -k; exec "$0" "$@" already_invoked_with_minus_k=1
fi

echo "AFTER"
echo "Arguments:"
al "$@"
echo "Environment:"
env | grep -E '^([a-z]|already_invoked_with_minus_k)='

unset already_invoked_with_minus_k

और केवल already_invoked_with_minus_kपर्यावरण चर exec'डी स्क्रिप्ट में सेट किया गया है ।


बहुत अच्छा जवाब! यह दिलचस्प है कि यह PATH को नहीं बदलेगा, हालांकि गृह परिवर्तनशील है, इसलिए env vars की ब्लैकलिस्ट (PATH कम से कम) जैसी कोई चीज होनी चाहिए जो इस तरह से सेट करना बहुत खतरनाक होगा। मैं प्यार करता हूँ कि यह कैसे अल्ट्रा-शॉर्ट है और सवाल का जवाब देता है, लेकिन मैं sanitize + eval + prefix सॉल्यूशन के साथ जाऊंगा क्योंकि यह अभी भी सुरक्षित है और इससे अधिक सार्वभौमिक रूप से प्रयोग करने योग्य है (ऐसे वातावरण में जहां आप उपयोगकर्ताओं को पर्यावरण के साथ खिलवाड़ नहीं करना चाहते हैं )। धन्यवाद और +1।
PSkocik

2

मेरा प्रयास:

#! /usr/bin/env bash
name='^[a-zA-Z][a-zA-Z0-9_]*$'
count=0
for arg in "$@"; do
    case "$arg" in
        *=*)
            key=${arg%%=*}
            val=${arg#*=}

            [[ "$key" =~ $name ]] && { let count++; eval "$key"=\$val; } || break

            # show time
            if [[ "$key" =~ $name ]]; then
                eval "out=\${$key}"
                printf '|%s| <-- |%s|\n' "$key" "$out"
            fi
            ;;
        *)
            break
            ;;
    esac
done
shift $count

# show time again   
printf 'arg: |%s|\n' "$@"

यह RHS पर (लगभग) मनमाने कचरे के साथ काम करता है:

$ ./assign.sh Foo_Bar33='1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0' '1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0=33'
|Foo_Bar33| <-- |1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0|
arg: |1 2;3`4"5~6!7@8#9$0 1%2^3&4*5(6)7-8=9+0=33|

$ ./assign.sh a=1 b=2 c d=4
|a| <-- |1|
|b| <-- |2|
arg: |c|
arg: |d=4|

शिफ्ट गलत चीजों को मार देगा यदि आप पहले गैर-x = y पैरामीटर पर लूप नहीं तोड़ते हैं
ठंढाचुट्ज़

@frostschutz अच्छा बिंदु, संपादित।
lcd047

इसे सामान्य करने में अच्छा काम किया। मुझे लगता है कि इसे थोड़ा सरल किया जा सकता है।
PSkocik

क्या आपको मेरे संपादन पर एक नज़र डालने का मौका मिला?
पीएसकोलिक

कृपया मेरे संपादन पर एक नज़र डालें। इस तरह से मुझे यह पसंद है (+ शायद इसके shiftबजाय shift 1)। अन्यथा धन्यवाद!
PSkocik

0

कुछ समय पहले मैं aliasइस तरह के काम के लिए बस गया । यहाँ मेरा एक और जवाब है:


हालांकि कभी-कभी ऐसे बयानों के मूल्यांकन और निष्पादन को अलग करना संभव हो सकता है। उदाहरण के लिए, aliasकमांड का पूर्व-मूल्यांकन करने के लिए उपयोग किया जा सकता है। निम्नलिखित उदाहरण में चर परिभाषा को एक उपनाम में सहेजा जाता है जिसे केवल तभी सफलतापूर्वक घोषित किया जा सकता है जब $varचर का मूल्यांकन करने वाला कोई बाइट नहीं होता है जो ASCII अल्फ़ान्यूमेरिक्स या _ से मेल नहीं खाता है।

LC_OLD=$LC_ALL LC_ALL=C
for var do    val=${var#*=} var=${var%%=*}
    alias  "${var##*[!_A-Z0-9a-z]*}=_$var=\$val" &&
    eval   "${var##[0-9]*}" && unalias "$var"
done;       LC_ALL=$LC_OLD

evalaliasएक उद्धृत varname संदर्भ से नए को लागू करने के लिए यहां उपयोग किया जाता है - बिल्कुल असाइनमेंट के लिए नहीं। और evalकेवल तभी बुलाया जाता है यदि पिछली aliasपरिभाषा सफल होती है, और जब मैं जानता हूं कि बहुत सारे अलग-अलग कार्यान्वयन बहुत अधिक भिन्न प्रकार के मूल्यों को स्वीकार करेंगे, उपनामों के लिए, मुझे अभी तक एक शेल नहीं मिला है जो पूरी तरह से खाली स्वीकार करेगा ।

अलियास के भीतर की परिभाषा _$var, हालांकि, और यह सुनिश्चित करने के लिए है कि कोई महत्वपूर्ण पर्यावरण मूल्य नहीं लिखे गए हैं। मैं किसी भी उल्लेखनीय पर्यावरण मूल्यों के बारे में नहीं जानता, जो कि _ से शुरू होता है और यह आमतौर पर अर्ध-निजी घोषणा के लिए एक सुरक्षित शर्त है।

वैसे भी, यदि उपनाम की परिभाषा सफल होती है, तो यह एक उपनाम घोषित करेगा जिसका $varमूल्य है। और evalकेवल aliasयह कहेंगे कि अगर यह भी एक संख्या से शुरू नहीं होता है - तो evalकेवल एक अशक्त तर्क मिलता है। इसलिए यदि दोनों स्थितियां पूरी होती evalहैं aliasऔर कॉल की गई चर परिभाषा को सहेज लिया aliasजाता है, जिसके बाद नया उपनाम तुरंत हैश तालिका से हटा दिया जाता है।


aliasइस संदर्भ में भी उपयोगी है कि आप अपने काम को प्रिंट कर सकते हैं। पूछे जाने पर एक डबलalias -उद्धृत सुरक्षित-फॉर-शेल-रेक्सकेशन स्टेटमेंट प्रिंट करेगा ।

sh -c "IFS=\'
    alias q=\"\$*\" q" -- \
    some args which alias \
    will print back at us

आउटपुट

q='some'"'"'args'"'"'which'"'"'alias'"'"'will'"'"'print'"'"'back'"'"'at'"'"'us'
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.