क्या POSIX में कमांड लाइन आर्गुमेंट्स के बीच स्पेस की संख्या प्राप्त करना किसी प्रोग्राम के लिए संभव है?


23

कहो कि क्या मैंने निम्नलिखित पंक्ति के साथ एक कार्यक्रम लिखा है:

int main(int argc, char** argv)

अब यह पता है कि किस कमांड लाइन के तर्क इसके लिए दिए गए हैं argv

क्या कार्यक्रम यह पता लगा सकता है कि तर्कों के बीच कितने स्थान हैं? जैसे जब मैं इन्हें बैश में टाइप करता हूं:

ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog       aaa      bbb

पर्यावरण एक आधुनिक लिनक्स है (जैसे कि उबंटू 16.04), लेकिन मुझे लगता है कि उत्तर किसी भी POSIX- अनुरूप प्रणालियों पर लागू होना चाहिए।


22
बस जिज्ञासा के लिए, आपके कार्यक्रम को यह जानने की आवश्यकता क्यों होगी?
nxnev

2
@nxnev मैं कुछ विंडोज प्रोग्राम लिखता था और मुझे पता है कि यह वहां संभव है, इसलिए मुझे आश्चर्य है कि क्या लिनक्स (या निक्स) में भी ऐसा ही कुछ है।
आईबग

9
मैं सीपी / एम में अस्पष्ट रूप से याद करता हूं कि कार्यक्रमों को अपनी स्वयं की कमांड लाइनों को पार्स करना था - इसका मतलब था कि प्रत्येक सी रनटाइम को शेल पार्सर को लागू करना था। और वे सभी इसे थोड़ा अलग तरीके से करते थे।
टोबे स्पाइट

3
@iBug है, लेकिन आपको कमांड को लागू करते समय तर्क को उद्धृत करने की आवश्यकता है। यह कैसे POSIX (और इसी तरह) गोले पर किया जाता है।
कोनराड रूडोल्फ

3
@ आईबग, ... विंडोज में वही डिज़ाइन है जो टोबी सीपी / एम से ऊपर उल्लेख करता है। यूनिक्स ऐसा नहीं करता है - कहा जाता प्रक्रिया के नजरिए से, वहाँ है कोई आदेश पंक्ति इसे चलाने में शामिल किया गया।
चार्ल्स डफी

जवाबों:


39

"तर्कों के बीच रिक्त स्थान" की बात करना सार्थक नहीं है; यह एक शेल अवधारणा है।

शेल का काम इनपुट की पूरी लाइनों को लेना है और उन्हें कमांड शुरू करने के लिए तर्कों के सरणियों में बनाना है। इसमें पार्सिंग उद्धृत स्ट्रिंग्स, विस्तार वाले चर, फ़ाइल वाइल्डकार्ड और टिल्ड एक्सप्रेशन, और बहुत कुछ शामिल हो सकते हैं। कमांड को एक मानक execसिस्टम कॉल के साथ शुरू किया जाता है , जो स्ट्रिंग के एक वेक्टर को स्वीकार करता है।

तार के वेक्टर बनाने के लिए अन्य तरीके मौजूद हैं। कई कार्यक्रम पूर्व निर्धारित कमांड इनवोकेशन के साथ अपनी उप-प्रक्रियाओं को कांटे और निष्पादित करते हैं - जिस स्थिति में, "कमांड लाइन" जैसी कोई चीज नहीं होती है। इसी तरह, एक ग्राफिकल (डेस्कटॉप) शेल एक प्रक्रिया शुरू कर सकता है जब कोई उपयोगकर्ता किसी फ़ाइल आइकन को खींचता है और इसे कमांड विजेट पर छोड़ देता है - फिर से, "तर्कों के बीच" वर्णों के लिए कोई पाठ लाइन नहीं होती है।

जहाँ तक आह्वान किए गए आदेश का सवाल है, एक शेल या अन्य अभिभावक / अग्रदूत प्रक्रिया में जो कुछ भी होता है वह निजी और छिपा हुआ होता है - हम केवल उन स्ट्रिंग्स के सरणी को देखते हैं जो मानक C निर्दिष्ट main()करते हैं जो स्वीकार कर सकते हैं।


अच्छा जवाब - यह यूनिक्स न्यूबॉकों के लिए इंगित करने के लिए महत्वपूर्ण है, जो अक्सर यह मानते हैं कि, अगर वे चलते हैं tar cf texts.tar *.txtतो टार प्रोग्राम को दो तर्क मिलते हैं और दूसरे को *.txtस्वयं ( ) का विस्तार करना पड़ता है। बहुत से लोग महसूस नहीं करते हैं कि यह वास्तव में कैसे काम करता है जब तक कि वे अपनी स्क्रिप्ट / प्रोग्राम लिखना शुरू नहीं करते हैं जो तर्कों को संभालते हैं।
लॉरेंस रेनशॉ

58

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


9
आप उल्लेख करना चाह सकते हैं execve(2)
IBug

3
आप सही कह रहे हैं, एक लंगड़े बहाने के रूप में, मैं कह सकता हूं कि मैं वर्तमान में एक फोन का उपयोग कर रहा हूं और मैन पेज देख रहा हूं, थोड़ा थकाऊ है :-)
हंस-मार्टिन मोजर

1
यह POSIX का प्रासंगिक अनुभाग है।
स्टीफन किट

1
@ हंस-मार्टिनमोसनर: टर्मक्स ...? ;-)
देवसोलर

9
"सामान्य रूप से" का अर्थ एक विशेष विक्षेपित मामले का हवाला देते हुए सुरक्षित था, जहां यह संभव है - उदाहरण के लिए, एक ठोस जड़ प्रक्रिया कॉलिंग शेल की मेमोरी का निरीक्षण करने और अनपार्टेड कमांड लाइन स्ट्रिंग खोजने में सक्षम हो सकती है।
हंस-मार्टिन मोजनेर

16

नहीं, यह संभव नहीं है, जब तक कि रिक्त स्थान एक तर्क का हिस्सा नहीं हैं ।

आदेश एक सरणी (व्यक्तिगत रूप में या प्रोग्रामिंग भाषा के आधार पर) से व्यक्तिगत तर्कों को एक्सेस करता है और वास्तविक कमांड लाइन को इतिहास फ़ाइल में सहेजा जा सकता है (यदि शेल में एक इंटरेक्टिव प्रॉम्प्ट पर टाइप किया गया है जिसमें इतिहास फाइलें हैं), लेकिन है किसी भी रूप में कमांड पर कभी नहीं पारित किया।

यूनिक्स पर सभी कमांड अंत exec()में फ़ंक्शन के परिवार में से एक द्वारा निष्पादित होते हैं । ये कमांड नाम और तर्कों की एक सूची या सरणी लेते हैं। शेल प्रॉम्प्ट पर टाइप की गई कमांड लाइन को उनमें से कोई भी नहीं लेता है। system()समारोह करता है, लेकिन इसके स्ट्रिंग तर्क बाद में द्वारा निष्पादित किया जाता है execve(), जो, फिर से, के बजाय एक कमांड लाइन स्ट्रिंग तर्क की एक सरणी लेता है।


2
@LightnessRacesinOrbit मैंने कहा कि वहाँ "बस तर्कों के बीच रिक्त स्थान" के बारे में कुछ भ्रम था। के बीच उद्धरण में रिक्त स्थान लाना helloऔर worldहै सचमुच दो तर्क के बीच रिक्त स्थान।
Kusalananda

5
@Kusalananda - ठीक है, नहीं ... रिक्त स्थान के बीच उद्धरण में लाना helloऔर worldहै सचमुच तीन तर्कों के दूसरे की आपूर्ति।
जेरेमी

@ जेरेमी जैसा कि मैंने कहा, मामले में "तर्कों के बीच" के माध्यम से कोई भ्रम था। हाँ, यदि आप करेंगे तो अन्य दो के बीच एक दूसरे तर्क के रूप में ।
Kusalananda

आपके उदाहरण ठीक थे, और शिक्षाप्रद थे।
जेरेमी

1
खैर, दोस्तों, उदाहरण भ्रम और गलतफहमी का एक स्पष्ट स्रोत थे। मैंने उन्हें हटा दिया है क्योंकि उत्तर के मूल्य में नहीं जोड़ा गया था।
Kusalananda

9

सामान्य तौर पर, यह संभव नहीं है, जैसे कई अन्य उत्तर बताए गए हैं।

हालांकि, यूनिक्स के गोले हैं साधारण प्रोग्राम (और वे कमांड लाइन व्याख्या कर रहे हैं और ग्लोबिंग यह अर्थात के विस्तार करने से पहले आदेश forkऔर execveइसके लिए)। शेल ऑपरेशन के बारे मेंbash यह स्पष्टीकरण देखें । आप अपना स्वयं का शेल लिख सकते हैं (या आप कुछ मौजूदा मुफ्त सॉफ्टवेयर शेल, जैसे GNU बैश ) को पैच कर सकते हैं और इसे अपने शेल (या यहां तक ​​कि आपके लॉगिन शेल के रूप में उपयोग कर सकते हैं, पासवार्ड (5) और शेल (5) देखें )।

उदाहरण के लिए, हो सकता है कि आपका अपना शेल प्रोग्राम पूरी कमांड लाइन को कुछ पर्यावरण चर ( MY_COMMAND_LINEउदाहरण के लिए कल्पना करें) में डाल दे, और शेल से चाइल्ड प्रोसेस तक कमांड लाइन को संचारित करने के लिए किसी अन्य प्रकार की अंतर-प्रक्रिया संचार का उपयोग करें-।

मुझे समझ नहीं आ रहा है कि आप ऐसा क्यों करना चाहते हैं, लेकिन आप इस तरह से व्यवहार करने वाले शेल को कोड कर सकते हैं (लेकिन मैं ऐसा नहीं करने की सलाह देता हूं)।

BTW, एक प्रोग्राम कुछ प्रोग्राम द्वारा शुरू किया जा सकता है जो एक शेल नहीं है (लेकिन जो फोर्क (2) करते हैं तो निष्पादित करें (2) , या बस execveइसकी वर्तमान प्रक्रिया में प्रोग्राम शुरू करने के लिए)। उस स्थिति में कमांड लाइन बिल्कुल नहीं है, और आपका प्रोग्राम बिना कमांड के शुरू किया जा सकता है ...

ध्यान दें कि आपके पास कुछ (विशेष) लिनक्स सिस्टम हो सकता है बिना किसी शेल के। यह अजीब और असामान्य है, लेकिन संभव है। फिर आप एक विशेष लिखने के लिए की आवश्यकता होगी init के रूप में अन्य कार्यक्रमों के शुरू करने से कार्यक्रम की जरूरत - किसी भी खोल का उपयोग किए बिना लेकिन ऐसा करके forkऔर execveसिस्टम कॉल।

यह भी पढ़ें आपरेटिंग सिस्टम: तीन आसान टुकड़े और भूल नहीं है कि execveव्यावहारिक रूप से हमेशा एक है सिस्टम कॉल (लिनक्स पर, वे में सूचीबद्ध हैं syscalls (2) , देखना भी परिचय (2) ) जो reinitialize वर्चुअल ऐड्रेस स्पेस (और कुछ अन्य चीजें) यह कर की प्रक्रिया


यह सबसे अच्छा जवाब है। मैं मानता हूं (बिना देखे हुए) कि argv[0] कार्यक्रम के नाम के लिए और तर्कों के लिए शेष तत्व पोसिक्स विनिर्देश हैं और उन्हें बदला नहीं जा सकता। एक रनटाइम वातावरण argv[-1]कमांड लाइन के लिए निर्दिष्ट कर सकता है , मुझे लगता है ...
पीटर - मोनिका

नहीं, यह नहीं हो सका। और अधिक ध्यान से execveप्रलेखन पढ़ें । आप उपयोग नहीं कर सकते argv[-1], इसका उपयोग करने के लिए अपरिभाषित व्यवहार है।
बेसिल स्टारीनेविच

हाँ, अच्छा बिंदु (यह भी संकेत है कि हमारे पास एक syscall है) - विचार थोड़ा विरोधाभासी है। रनटाइम के सभी तीन घटकों (शेल, stdlib और OS) को सहयोग करने की आवश्यकता होगी। शेल को execvepluscmdअतिरिक्त पैरामीटर (या argv कन्वेंशन) के साथ एक विशेष गैर-पॉसिक्स फ़ंक्शन को कॉल करने की आवश्यकता होती है , syscall मुख्य के लिए एक तर्क वेक्टर का निर्माण करता है जिसमें प्रोग्राम नाम के लिए पॉइंटर से पहले कमांड लाइन के लिए एक पॉइंटर होता है, और फिर पता पास करें के रूप में कार्यक्रम का नाम करने के लिए सूचक की argvजब कार्यक्रम के बुला main...
पीटर - को पुनः स्थापित मोनिका

शेल को फिर से लिखने की ज़रूरत नहीं है, बस उद्धरणों का उपयोग करें। यह सुविधा बोरन शेल से उपलब्ध थी sh। तो नया नहीं है।
ctrl-alt-delor-

उद्धरणों का उपयोग करके कमांड लाइन को बदलना आवश्यक है । और ओपी नहीं चाहता है कि
बेसिल स्ट्रायनेविच

3

आप अपने शेल को हमेशा अनुप्रयोगों को बता सकते हैं कि शेल कोड उनके निष्पादन के लिए क्या नेतृत्व करता है। उदाहरण के लिए, हुक का उपयोग करके पर्यावरण चर zshमें उस जानकारी को पास $SHELL_CODEकरके preexec()( printenvउदाहरण के रूप में, आप getenv("SHELL_CODE")अपने कार्यक्रम में उपयोग करेंगे ):

$ preexec() export SHELL_CODE=$1
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv  SHELL_CODE
printenv  CODE
$ $(echo printenv SHELL_CODE)
$(echo printenv SHELL_CODE)
$ for i in SHELL_CODE; do printenv "$i"; done
for i in SHELL_CODE; do printenv "$i"; done
$ printenv SHELL_CODE; : other command
printenv SHELL_CODE; : other command
$ f() printenv SHELL_CODE
$ f
f

उन सभी के printenvरूप में निष्पादित करेंगे:

execve("/usr/bin/printenv", ["printenv", "SHELL_CODE"], 
       ["PATH=...", ..., "SHELL_CODE=..."]);

उन तर्कों printenvके printenvसाथ निष्पादन के लिए नेतृत्व करने वाले zsh कोड को पुनः प्राप्त करने की अनुमति । आप उस जानकारी के साथ क्या करना चाहेंगे, यह मेरे लिए स्पष्ट नहीं है।

साथ bash, सुविधा के लिए निकटतम zshकी preexec()अपनी का उपयोग करेंगे $BASH_COMMANDएक में DEBUGजाल है, लेकिन ध्यान दें कि bashकि में पुनर्लेखन (और विशेष रूप refactors में सीमांकक के रूप में इस्तेमाल करने से व्हाइटस्पेस से कुछ) और कहा कि हर के लिए आवेदन किया (अच्छी तरह से, कुछ) के कुछ स्तर करता आदेश पूरी कमांड लाइन को प्रॉम्प्ट पर दर्ज करते समय चलाएं ( functraceविकल्प भी देखें )।

$ trap 'export SHELL_CODE="$BASH_COMMAND"' DEBUG
$ printenv SHELL_CODE
printenv SHELL_CODE
$ printenv $(echo 'SHELL_CODE')
printenv $(echo 'SHELL_CODE')
$ for i in SHELL_CODE; do printenv "$i"; done; : other command
printenv "$i"
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printf '%s\n' "$(printenv "SHELL_CODE")"
$ set -o functrace
$ printf '%s\n' "$(printenv "SHELL_CODE")"
printenv "SHELL_CODE"
$ print${-+env  }    $(echo     'SHELL_CODE')
print${-+env  } $(echo     'SHELL_CODE')

देखें कि शेल भाषा सिंटैक्स में परिसीमन करने वाले कुछ रिक्त स्थान को 1 में निचोड़ दिया गया है और कैसे पूरी कमांड लाइन को हमेशा कमांड में पारित नहीं किया जाता है। तो शायद आपके मामले में उपयोगी नहीं है।

ध्यान दें कि मैं इस तरह की बात करने की सलाह नहीं दूंगा, क्योंकि आप संभावित रूप से प्रत्येक कमांड के लिए संवेदनशील जानकारी लीक कर रहे हैं:

echo very_secret | wc -c | untrustedcmd

उस रहस्य को दोनों के लिए wcऔर लीक करेगा untrustedcmd

बेशक, आप शेल की तुलना में अन्य भाषाओं के लिए उस तरह का काम कर सकते थे। उदाहरण के लिए, C में, आप कुछ मैक्रोज़ का उपयोग कर सकते हैं जो C कोड को निर्यात करते हैं जो पर्यावरण के लिए एक कमांड निष्पादित करता है:

#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define WRAP(x) (setenv("C_CODE", #x, 1), x)

int main(int argc, char *argv[])
{
  if (!fork()) WRAP(execlp("printenv", "printenv", "C_CODE", NULL));
  wait(NULL);
  if (!fork()) WRAP(0 + execlp("printenv",   "printenv", "C_CODE", NULL));
  wait(NULL);
  if (argc > 1 && !fork()) WRAP(execvp(argv[1], &argv[1]));
  wait(NULL);
  return 0;
}

उदाहरण:

$ ./a.out printenv C_CODE
execlp("printenv", "printenv", "C_CODE", NULL)
0 + execlp("printenv", "printenv", "C_CODE", NULL)
execvp(argv[1], &argv[1])

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


जब मैं यह परीक्षण कर रहा था BASH_COMMAND, तो मूल व्हाट्सएप में अलग-अलग तर्क नहीं थे, इसलिए यह ओपी के शाब्दिक अनुरोध के लिए उपयोग करने योग्य नहीं था। क्या इस उत्तर में उस विशेष उपयोग के मामले के लिए कोई प्रदर्शन शामिल है?
चार्ल्स डफी

@CharlesDuffy, मैं बस चाहता था कि zsh के preexec () के सबसे समतुल्य को bash में इंगित किया जाए (जैसा कि शेल ओपी को संदर्भित कर रहा था) और इंगित करता है कि इसका उपयोग उस विशिष्ट उपयोग के मामले में नहीं किया जा सकता था, लेकिन मैं इसे स्वीकार नहीं करता था बहुत साफ़। संपादित देखें। यह उत्तर स्रोत कोड (यहाँ zsh / bash / C में) पास करने के तरीके के बारे में अधिक सामान्य होने का इरादा है, जिसके कारण कमांड को निष्पादित किया जा रहा है (ऐसा कुछ जो उपयोगी नहीं है, लेकिन मुझे उम्मीद है कि ऐसा करते समय, और विशेष रूप से उदाहरणों के साथ, मैं दर्शाता है कि यह बहुत उपयोगी नहीं है)
स्टीफन चेज़लस

0

मैं सिर्फ वही जोड़ूंगा जो अन्य उत्तरों में गायब है।

नहीं

अन्य उत्तर देखें

हो सकता है, की तरह

ऐसा कुछ भी नहीं है जो प्रोग्राम में किया जा सकता है, लेकिन कुछ ऐसा भी है जो प्रोग्राम को चलाने पर शेल में किया जा सकता है।

आपको उद्धरण का उपयोग करने की आवश्यकता है। इसलिए इसके बजाय

./myprog      aaa      bbb

आपको इनमें से एक करने की जरूरत है

./myprog "     aaa      bbb"
./myprog '     aaa      bbb'

यह सभी रिक्त स्थान के साथ कार्यक्रम के लिए एक एकल तर्क पारित करेगा। दोनों के बीच एक अंतर है, दूसरा शाब्दिक है, बिल्कुल स्ट्रिंग जैसा कि यह दिखाई देता है (इसके अलावा 'टाइप किया जाना चाहिए \')। पहला व्यक्ति कुछ पात्रों की व्याख्या करेगा, लेकिन कई तर्कों में विभाजित होगा। अधिक जानकारी के लिए शैल उद्धरण देखें। तो शेल को फिर से लिखने की कोई आवश्यकता नहीं है, शेल डिजाइनरों ने पहले ही सोचा है। हालाँकि, क्योंकि यह अब एक तर्क है, आपको कार्यक्रम के भीतर अधिक पासिंग करनी होगी।

विकल्प 2

स्टड के माध्यम से डेटा पास करें। यह कमांड में बड़ी मात्रा में डेटा प्राप्त करने का सामान्य तरीका है। जैसे

./myprog << EOF
    aaa      bbb
EOF

या

./myprog
Tell me what you want to tell me:
aaaa bbb
ctrl-d

(इटैलिक प्रोग्राम के आउटपुट हैं)


तकनीकी रूप से, शेल कोड: ./myprog␣"␣␣␣␣␣aaa␣␣␣␣␣␣bbb"(आमतौर पर एक बच्चे की प्रक्रिया में) फ़ाइल को संग्रहीत किया जाता है ./myprogऔर इसमें दो तर्क दिए जाते हैं: ./myprogऔर ␣␣␣␣␣aaa␣␣␣␣␣␣bbb( argv[0]और argc[1], argc2) और ओपी के अनुसार, उन दो तर्कों को अलग करने वाला स्थान किसी भी तरह से पारित नहीं होता है। को myprog
स्टीफन चेज़लस

लेकिन आप कमांड बदल रहे हैं, और ओपी इसे बदलना नहीं चाहता है
बेसिल स्टायरनेविच

@BasileStarynkevitch आपकी टिप्पणी के बाद, मैंने प्रश्न को फिर से पढ़ा। आप एक धारणा बना रहे हैं। ओपी कहीं नहीं कहते हैं कि वे कार्यक्रम चलाने के तरीके को बदलना नहीं चाहते हैं। शायद यह सच है, लेकिन उनके पास इस पर कहने के लिए कुछ नहीं था। इसलिए यह जवाब हो सकता है कि उन्हें क्या चाहिए।
ctrl-alt-delor

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