कैसे एक उपधारा के लिए बैश स्क्रिप्ट तर्क पारित करने के लिए


12

मेरे पास एक रैपर स्क्रिप्ट है जो कुछ काम करती है और फिर मूल मापदंडों को किसी अन्य टूल पर पास करती है:

#!/bin/bash
# ...
other_tool -a -b "$@"

यह ठीक काम करता है, जब तक कि "अन्य उपकरण" एक सब-टाइम में न चला हो:

#!/bin/bash
# ...
bash -c "other_tool -a -b $@"

अगर मैं अपनी रैपर स्क्रिप्ट को इस तरह कहता हूं:

wrapper.sh -x "blah blup"

उसके बाद, केवल पहला मूल तर्क (-x) "other_tool" को सौंपा गया है। वास्तव में, मैं एक सब-टाइम नहीं बनाता, लेकिन एंड्रॉइड फोन पर एक शेल में मूल तर्क पास करता हूं, जिससे कोई फर्क नहीं पड़ना चाहिए:

#!/bin/bash
# ...
adb sh -c "other_tool -a -b $@"

जवाबों:


14

बैश के printfकमांड में एक विशेषता है जो बोली / भागने / जो भी एक स्ट्रिंग है, जब तक कि माता-पिता और उप-दोनों वास्तव में बैश हैं, तब तक यह काम करना चाहिए:

[संपादित करें: जैसा कि siegi ने एक टिप्पणी में बताया है, अगर आप इसे स्पष्ट तरीके से करते हैं तो कोई समस्या है जब कोई तर्क नहीं दिया जाता है, जहां यह कार्य करता है जैसे वास्तव में एक एकल खाली तर्क था। मैंने प्रारूप स्ट्रिंग के साथ लपेटकर नीचे एक वर्कअराउंड जोड़ा है ${1+}, जिसमें केवल प्रारूप स्ट्रिंग शामिल है यदि पहला तर्क परिभाषित किया गया है । यह एक कुलीग का एक सा है, लेकिन यह काम करता है।]

#!/bin/bash

quoted_args="$(printf "${1+ %q}" "$@")" # Note: this will have a leading space before the first arg
# echo "Quoted args:$quoted_args" # Uncomment this to see what it's doing
bash -c "other_tool -a -b$quoted_args"

ध्यान दें कि आप इसे एक ही पंक्ति में भी कर सकते हैं: bash -c "other_tool -a -b$(printf "${1+ %q}" "$@")"


धन्यवाद! इसने मेरे लिए बहुत अच्छा काम किया।
DribblzAroundU82

यह सही तरीके से काम नहीं करता है, जब कोई तर्क पारित नहीं होता है (यह बिना तर्क के एक खाली तर्क पारित करता है)।
सिगी

1
@siegi अच्छी पकड़; मैंने इसके लिए वर्कअराउंड जोड़ा है।
गॉर्डन डेविसन

4

कोई भी समाधान अच्छी तरह से काम नहीं करता है। बस पैरामीटर के रूप में x / \ \ "b \" / aaaaa / \ 'xxx \ yyyy \' / zz \ ​​"offf \" पास करें और वे विफल हो जाते हैं।

यहां एक सरल आवरण है जो हर मामले को संभालता है। ध्यान दें कि यह प्रत्येक तर्क से दो बार कैसे बचता है।

#!/usr/bin/env bash
declare -a ARGS
COUNT=$#
for ((INDEX=0; INDEX<COUNT; ++INDEX))
do
    ARG="$(printf "%q" "$1")"
    ARGS[INDEX]="$(printf "%q" "$ARG")"
    shift
done

ls -l ${ARGS[*]}

1
उपयोगी भी यदि आप '@ बिन / फू' '' $ @ '' जैसे अंतिम उद्धृत स्ट्रिंग तर्क के भाग के रूप में $ @ को अवगत कराने का प्रयास कर रहे हैं - अपनाएं '' और यह पता चलता है कि यह वैसा नहीं है जैसा आप अपेक्षा करते हैं। ।
गुड़

1
ओह भगवान का शुक्र है। इस मुद्दे से जूझ रहे थे और लगता है कुछ भी काम नहीं किया। इससे यह हल हो गया। अच्छा होगा यदि यह किसी भी तरह से बैश बनाया गया हो तो इस मुद्दे पर बहुत सोच समझकर दोहरे पलायन को रोकने के लिए।
मूगू

2

बदले $@के लिए $*। मैंने एक छोटा स्थानीय परीक्षण किया और यह मेरे मामले में काम करता है।

#!/bin/sh
bash -c "echo $*"
bash -c "echo $@"

के रूप में सहेजा जा रहा है test.shऔर इसे निष्पादन योग्य बनाता है

$ ./test.sh foo bar
foo bar
foo

वहाँ के बीच एक सूक्ष्म अंतर नहीं है $*और $@जैसा कि आप देख सकते हैं। उदाहरण देखें http://ss64.com/bash/syntax-parameters.html


टिप्पणियों में अनुवर्ती प्रश्न के लिए: आपको भागने की आवश्यकता है जैसे सफेद-स्थान "दो बार" एक विभाजक के साथ एक स्ट्रिंग को पास करने के लिए एक संयुक्त तर्क के रूप में, उदाहरण के test.shलिए एक wcआवरण के साथ संशोधित :

#!/bin/sh
bash -c "wc $*"

यह काम:

$ touch test\ file
$ ./test.sh -l "test\ file"
0 test file

परंतु:

$ ./test.sh -l "test file"
wc: test: No such file or directory
wc: file: No such file or directory
0 total

दुर्भाग्य से, यह भी काम नहीं करता है। यदि आप रैपर को इस तरह से बुलाते हैं: wrapper.sh -x "blah blup"तो सब्सक्रिप्शन को दो मापदंडों (-x, blah, blup) के बजाय TWO (-x, "blah blup") मिलता है
राल्फ होली

यदि आप चाहते हैं कि अंतरिक्ष विभाजक को संरक्षित रखा जाए, तो आपको तर्क को "डबल-एस्केप" करना होगा "Blah\ blup"। इसे आज़माएं और देखें कि क्या यह काम करता है।
डैनियल एंडरसन

2
ऐसा लगता है कि यह काम करता है, हालांकि, इससे बचने के लिए आवरण के कॉलर की आवश्यकता होगी। और मैं एक पारदर्शी समाधान की तलाश कर रहा हूं।
राल्फ होली

2

यह विफल हो रहा है क्योंकि आप एक सरणी (स्थितीय मापदंडों) को एक स्ट्रिंग में ले जा रहे हैं। "$@"जादुई है क्योंकि यह आपको प्रत्येक अलग-अलग पैरामेटर को एक ठीक से उद्धृत स्ट्रिंग के रूप में देता है। अतिरिक्त पाठ जोड़ने से जादू टूट जाता है: "blah $@"सिर्फ एक स्ट्रिंग है।

यह आपको करीब ला सकता है:

cmd="other_tool -a -b"
for parm in "$@"; do cmd+=" '$parm'"; done
adb sh -c "$cmd"

बेशक, कोई भी पैरामीटर जिसमें एक उद्धरण शामिल है, परेशानी का कारण होगा।


2

ठीक है, अधिक स्पष्टीकरण:

$ cat /tmp/test.sh
#! /bin/bash

echo '$@='"$@"

set -v # "debug"

sh -c 'echo $@' "$@" # gremlins steal the first option

sh -c 'echo $@' -- "$@" # We defend the option with two knifes

$ bash -e /tmp/test.sh first second third
$@=first second third

sh -c 'echo $@' "$@" # gremlins steal the first option
second third

sh -c 'echo $@' -- "$@" # We defend the option with two knifes
first second third

1

मैंने कई अलग-अलग समाधानों की कोशिश की, पृष्ठभूमि जानकारी और विकल्पों सहित एक अच्छा संसाधन उदाहरण के लिए है ग्रेग (उर्फ। ग्रेकैट के) विकी पर BashFAQ / 096 । कुल मिलाकर मुझे निम्नलिखित दो सबसे अधिक पठनीय लगे (काम करने वालों से):


चूँकि Bash 4.4 (जहाँ तक मैं NEWS से बता सकता हूँ ) इस तरह के पैरामीटर विस्तार का उपयोग करना संभव है @Q:

adb sh -c "other_tool -a -b ${*@Q}"

ध्यान दें कि मैं $*यहाँ का उपयोग करता हूँ $@क्योंकि आप "other_tool -a -b ${*@Q}"एक स्ट्रिंग के बजाय एक सिंगल स्ट्रिंग होना चाहते हैं ।

यदि आप बैश एरे चर के साथ भी ऐसा ही करना चाहते हैं तो आपको सिंटैक्स ${ARRAY[*]@Q}(उद्धरण चिह्नों के भीतर) की आवश्यकता होगी ।


यदि आपके पास बश 4.4 नहीं है या बाद में उपलब्ध है या सुनिश्चित नहीं है, तो यह मेरा पसंदीदा समाधान है:

function escapeBashArgs() {
    local arg separator=""
    for arg
    do
        printf "%s%q" "$separator" "$arg"
        separator=" "
    done
}

adb sh -c "other_tool -a -b $(escapeBashArgs "$@")"

नोट यहाँ है कि आप उपयोग करने की आवश्यकता "$@"के बजाय $@या "$*"या $*क्योंकि आप तर्क के भीतर शब्द बंटवारे नहीं करना चाहते, तो हवाले से बिना वेरिएंट नहीं किया जा सकता है, और आप तर्क संरक्षित किये जाने की संख्या चाहते हैं, इसलिए "$*"के रूप में यह शामिल हो जाएगा नहीं किया जा सकता एक ही स्ट्रिंग के सभी तर्क। फ़ंक्शन तब एक ही स्ट्रिंग में सभी तर्क देता है।

यदि आप पहले तर्क के सामने अतिरिक्त स्थान की परवाह नहीं करते हैं, तो आप चर printfको स्वरूप स्ट्रिंग को बदल सकते हैं " %q"और हटा सकते separatorहैं। या आप गॉर्डन डेविसन्स जवाब से वन-लाइनर का उपयोग कर सकते हैं ।


यह समाधान उन सभी मामलों के साथ काम करता है जो मैं विशेष रूप से कर सकता हूं:

  • कोई तर्क नहीं: escapeBashArgsकुछ भी नहीं
  • खाली तर्क: escapeBashArgs "" ""'' ''
  • बस स्थान के साथ तर्क: escapeBashArgs " " " "' ' ' 'या \ \ \ \ \( अंतिम स्थान इस साइट रेंडरर द्वारा खाया जाता है )
  • विशेष रिक्ति और न्यूलाइन्स के साथ तर्क: escapeBashArgs "a b" c\ d "arg with newline"'a b' 'c d' $'arg with\nnewline'या a\ \ \ \ \ \ b c\ d $'arg with\nnewline'( इस साइट पर लाइन रैपिंग के कारण न्यूलाइन के बीच withऔर newline, अन्य पदों पर है )
  • विशेष वर्णों के साथ तर्क: escapeBashArgs '$"'\''({:})''$"'\''({:})'या\$\"\'\(\{:\}\)
  • Jcayzacs उत्तर से उदाहरण : escapeBashArgs x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"'x/ "b"/aaaaa/'\''xxx yyyy'\''/zz"offf"'याx/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"

(GNU बैश 5.0.3 (1) -release के साथ परीक्षण किया गया।)


-2
#! /bin/bash
sh -c 'other_tool -a -b "$@"' -- "$@"

3
सुपर उपयोगकर्ता में आपका स्वागत है! हम लंबे उत्तर की तलाश कर रहे हैं जो कुछ स्पष्टीकरण और संदर्भ प्रदान करते हैं। केवल एक-पंक्ति का उत्तर न दें; समझाएं कि आपका उत्तर सही क्यों है, आदर्श रूप से उद्धरणों के साथ। स्पष्टीकरण को शामिल नहीं करने वाले उत्तरों को हटाया जा सकता है।
जी-मैन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.