VonC द्वारा अब हटाए गए उत्तर को फिर से लिखना ।
रॉबर्ट गैंबल का संक्षिप्त जवाब सीधे सवाल से संबंधित है। यह एक रिक्त स्थान वाले फ़ाइल नाम के साथ कुछ मुद्दों पर प्रवर्धित करता है।
यह भी देखें: / बिन / श में $ {1: + "$ @"}
मूल थीसिस: "$@"
सही है, और $*
(अयोग्य) लगभग हमेशा गलत है। इसका कारण यह है कि "$@"
जब तर्क स्थान होते हैं तो ठीक काम करते हैं, और $*
जब वे नहीं करते हैं तो उसी तरह काम करते हैं। कुछ परिस्थितियों में, "$*"
ठीक भी है, लेकिन "$@"
आमतौर पर (लेकिन हमेशा नहीं) एक ही स्थान पर काम करता है। निर्विवाद, $@
और $*
समतुल्य हैं (और लगभग हमेशा गलत)।
तो, के बीच अंतर क्या है $*
, $@
, "$*"
, और "$@"
? वे सभी 'शेल के सभी तर्कों' से संबंधित हैं, लेकिन वे अलग-अलग चीजें करते हैं। जब अयोग्य, $*
और $@
एक ही काम करते हैं। वे प्रत्येक 'शब्द' (गैर-व्हाट्सएप का क्रम) को एक अलग तर्क मानते हैं। उद्धृत रूप काफी भिन्न हैं, यद्यपि: "$*"
तर्क सूची को एक एकल स्थान-पृथक स्ट्रिंग के "$@"
रूप में मानते हैं , जबकि तर्कों को लगभग वैसा ही मानते हैं जैसा वे कमांड लाइन पर निर्दिष्ट करते समय थे।
"$@"
जब कोई स्थितिगत तर्क नहीं होते हैं, तो कुछ भी नहीं फैलता है; "$*"
एक खाली स्ट्रिंग के लिए फैलता है - और हाँ, वहाँ एक अंतर है, हालांकि इसे समझना मुश्किल हो सकता है। (गैर-मानक) कमांड की शुरुआत के बाद, नीचे अधिक जानकारी देखें al
।
द्वितीयक थीसिस: यदि आपको रिक्त स्थान के साथ तर्कों को संसाधित करने और फिर उन्हें अन्य आदेशों पर पास करने की आवश्यकता है, तो आपको सहायता के लिए कभी-कभी गैर-मानक टूल की आवश्यकता होती है। (या आपको सरणियों का उपयोग करना चाहिए, ध्यान से: के "${array[@]}"
अनुरूप व्यवहार करता है "$@"
।)
उदाहरण:
$ mkdir "my dir" anotherdir
$ ls
anotherdir my dir
$ cp /dev/null "my dir/my file"
$ cp /dev/null "anotherdir/myfile"
$ ls -Fltr
total 0
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir/
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir/
$ ls -Fltr *
my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ ls -Fltr "./my dir" "./anotherdir"
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ var='"./my dir" "./anotherdir"' && echo $var
"./my dir" "./anotherdir"
$ ls -Fltr $var
ls: "./anotherdir": No such file or directory
ls: "./my: No such file or directory
ls: dir": No such file or directory
$
वह काम क्यों नहीं करता है? यह काम नहीं करता क्योंकि शेल प्रक्रियाएं चर को फैलाने से पहले बोली लगाती हैं। तो, खोल में एम्बेडेड उद्धरण पर ध्यान देने के लिए शेल प्राप्त करने के लिए $var
, आपको उपयोग करना होगा eval
:
$ eval ls -Fltr $var
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$
यह वास्तव में मुश्किल हो जाता है जब आपके पास फ़ाइल नाम होते हैं जैसे " He said,
"Don't do this!"
" (उद्धरण और दोहरे उद्धरण और रिक्त स्थान के साथ)।
$ cp /dev/null "He said, \"Don't do this!\""
$ ls
He said, "Don't do this!" anotherdir my dir
$ ls -l
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 15:54 He said, "Don't do this!"
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir
$
गोले (वे सभी) इस तरह के सामान को संभालने के लिए विशेष रूप से आसान नहीं बनाते हैं, इसलिए (मज़ेदार रूप से) कई यूनिक्स कार्यक्रम उन्हें संभालने का एक अच्छा काम नहीं करते हैं। यूनिक्स पर, एक फ़ाइल नाम (एकल घटक) में स्लैश और NUL को छोड़कर कोई भी वर्ण हो सकता है '\0'
। हालाँकि, गोले एक पथ नाम में कहीं भी रिक्त स्थान या newlines या टैब को दृढ़ता से प्रोत्साहित नहीं करते हैं। यह भी है कि मानक यूनिक्स फ़ाइल नामों में रिक्त स्थान क्यों नहीं हैं, आदि।
जब उन फ़ाइल नामों के साथ काम करना जिनमें रिक्त स्थान और अन्य परेशानी वाले वर्ण हो सकते हैं, तो आपको बेहद सावधान रहना होगा, और मैंने बहुत पहले पाया कि मुझे एक ऐसे कार्यक्रम की आवश्यकता थी जो यूनिक्स पर मानक नहीं है। मैं इसे कहता हूं escape
(संस्करण 1.1 दिनांक 1989-08-23T16: 01: 45Z)।
यहां escape
SCCS नियंत्रण प्रणाली के साथ - उपयोग में एक उदाहरण है। यह एक कवर स्क्रिप्ट है जो ए delta
(थिंक चेक-इन ) और ए
get
(थिंक चेक-आउट ) दोनों करती है। विभिन्न तर्क, विशेष रूप से -y
(इस कारण से कि आपने बदलाव किया है) में रिक्त स्थान और नए अंक शामिल होंगे। ध्यान दें कि स्क्रिप्ट 1992 से है, इसलिए यह $(cmd ...)
नोटेशन के बजाय बैक-टिक का
उपयोग करता है और #!/bin/sh
पहली पंक्ति पर उपयोग नहीं करता है ।
: "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
# Delta and get files
# Uses escape to allow for all weird combinations of quotes in arguments
case `basename $0 .sh` in
deledit) eflag="-e";;
esac
sflag="-s"
for arg in "$@"
do
case "$arg" in
-r*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
-e) gargs="$gargs `escape \"$arg\"`"
sflag=""
eflag=""
;;
-*) dargs="$dargs `escape \"$arg\"`"
;;
*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
esac
done
eval delta "$dargs" && eval get $eflag $sflag "$gargs"
(मैं शायद इन दिनों बहुत अच्छी तरह से भागने का उपयोग नहीं करूंगा - यह -e
तर्क के साथ आवश्यक नहीं है, उदाहरण के लिए - लेकिन कुल मिलाकर, यह उपयोग करने वाली मेरी सरल लिपियों में से एक है escape
।)
escape
कार्यक्रम बस बल्कि तरह, अपने तर्कों आउटपुट echo
करता है, लेकिन यह सुनिश्चित करता है कि तर्क के साथ उपयोग के लिए सुरक्षित है
eval
(में से एक स्तर eval
है, मैं एक प्रोग्राम है जो दूरदराज के खोल निष्पादन किया है, और है कि के उत्पादन से बचने के लिए की जरूरत है escape
)।
$ escape $var
'"./my' 'dir"' '"./anotherdir"'
$ escape "$var"
'"./my dir" "./anotherdir"'
$ escape x y z
x y z
$
मेरे पास एक और कार्यक्रम है al
जो इसके तर्कों को प्रति पंक्ति एक सूची देता है (और यह और भी प्राचीन है: संस्करण 1.1 दिनांक 1987-01-27T14: 35: 49)। स्क्रिप्ट को डीबग करते समय यह सबसे अधिक उपयोगी होता है, क्योंकि यह कमांड लाइन में प्लग किया जा सकता है कि यह देखने के लिए कि वास्तव में कमांड में क्या तर्क दिए गए हैं।
$ echo "$var"
"./my dir" "./anotherdir"
$ al $var
"./my
dir"
"./anotherdir"
$ al "$var"
"./my dir" "./anotherdir"
$
[ जोड़ा गया:
और अब विभिन्न "$@"
सूचनाओं के बीच अंतर दिखाने के लिए , यहाँ एक और उदाहरण है:
$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ sh xx.sh * */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$
ध्यान दें कि कमांड लाइन पर *
और उसके बीच के मूल रिक्त को कुछ भी संरक्षित नहीं करता है */*
। यह भी ध्यान दें कि आप शेल में 'कमांड लाइन आर्ग्युमेंट्स' को बदलकर उपयोग कर सकते हैं:
set -- -new -opt and "arg with space"
यह 4 विकल्प सेट करता है, ' -new
', ' -opt
', ' and
', और ' arg with space
'।
]
हम्म, यह काफी लंबा जवाब है - शायद एक्जेसिस बेहतर शब्द है। escape
अनुरोध पर उपलब्ध स्रोत कोड (जीमेल डॉट कॉम पर फर्स्टनाम डॉटनाम के लिए ईमेल)। स्रोत कोड al
अविश्वसनीय रूप से सरल है:
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return(0);
}
बस इतना ही। यह उस test.sh
स्क्रिप्ट के बराबर है जिसे रॉबर्ट गैम्बल ने दिखाया था, और इसे शेल फ़ंक्शन के रूप में लिखा जा सकता था (लेकिन शेल फ़ंक्शन बॉर्न शेल के स्थानीय संस्करण में मौजूद नहीं था जब मैंने पहली बार लिखा था al
)।
यह भी ध्यान दें कि आप al
एक सरल शेल स्क्रिप्ट के रूप में लिख सकते हैं :
[ $# != 0 ] && printf "%s\n" "$@"
सशर्त की जरूरत है ताकि कोई तर्क न दिए जाने पर कोई आउटपुट न आए। printf
आदेश केवल प्रारूप स्ट्रिंग तर्क के साथ एक रिक्त पंक्ति का उत्पादन करेगा, लेकिन सी कार्यक्रम कुछ भी नहीं पैदा करता है।