VonC tarafından silinen bir cevabın yeniden yazılması .
Robert Gamble'ın özlü yanıtı doğrudan soru ile ilgilidir. Bu, boşluk içeren dosya adları ile ilgili bazı konularda güçlendirir.
Ayrıca bkz: / bin / sh içinde $ {1: + "$ @"}
Temel tez: "$@"
doğrudur ve $*
(alıntılanmamış) neredeyse her zaman yanlıştır. Bunun nedeni "$@"
, bağımsız değişkenler boşluk içerdiğinde iyi çalışır ve içermedikleri gibi çalışır $*
. Bazı durumlarda "$*"
da sorun yoktur, ancak "$@"
genellikle (ancak her zaman değil) aynı yerlerde çalışır. Tırnaksız $@
ve $*
eşdeğerdir (ve neredeyse her zaman yanlıştır).
Yani, arasındaki fark nedir $*
, $@
, "$*"
, ve "$@"
? Hepsi 'kabuğun tüm argümanlarıyla' ilişkilidir, ancak farklı şeyler yaparlar. Alıntı yapıldığında $*
ve $@
aynı şeyi yapın. Her bir 'sözcüğü' (boşluk olmayan diziyi) ayrı bir argüman olarak ele alırlar. Alıntılanan formlar oldukça farklıdır: "$*"
argüman listesini boşlukla ayrılmış tek bir dize olarak "$@"
ele alırken argümanları komut satırında belirtildiği gibi hemen hemen aynı şekilde ele alır.
"$@"
konumsal argüman olmadığında hiç bir şeye genişlemez; "$*"
boş bir dizeye genişler - ve evet, bir fark vardır, ancak onu algılamak zor olabilir. (Standart dışı) komutun kullanılmasından sonra aşağıdaki daha fazla bilgi edinin al
.
İkincil tez: boşluklarla argümanları işlemeniz ve daha sonra diğer komutlara iletmeniz gerekiyorsa, bazen yardımcı olmak için standart olmayan araçlara ihtiyacınız vardır. (Veya dizileri dikkatli bir şekilde kullanmalısınız: "${array[@]}"
benzer şekilde davranır "$@"
.)
Misal:
$ 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
$
Neden çalışmıyor? Kabuk, değişkenleri genişletmeden önce tırnak işlemlerini gerçekleştirdiği için çalışmaz. Bu nedenle, kabuğun gömülü tırnaklara dikkat etmesini sağlamak için $var
şunları kullanmalısınız 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!"
" ( Dosya ve çift tırnak ve boşluklarla) gibi dosya adlarına sahip olduğunuzda bu gerçekten zor olur .
$ 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
$
Kabuklar (hepsi) bu tür şeyleri işlemeyi özellikle kolaylaştırmaz, bu nedenle (yeterince komik) birçok Unix programı bunları işlemek için iyi bir iş yapmaz. Unix'te, bir dosya adı (tek bileşenli), eğik çizgi ve NUL dışında herhangi bir karakter içerebilir '\0'
. Ancak, mermiler yol adlarının hiçbir yerinde boşluk, satır, çizgi veya sekme olmamasını şiddetle teşvik eder. Ayrıca standart Unix dosya adlarının boşluk vb. İçermemesinin nedeni de budur.
Boşluklar ve diğer zahmetli karakterler içerebilecek dosya adları ile uğraşırken, son derece dikkatli olmalısınız ve uzun zaman önce Unix'te standart olmayan bir programa ihtiyacım olduğunu buldum. Ona diyorumescape
(sürüm 1.1 tarihli 1989-08-23T16: 01: 45Z).
escape
SCCS kontrol sistemi ile kullanımda bir örnek . Bu a hem yapan bir kapak script delta
(düşünmek giriş ) ve
get
(düşünmek çıkış ). Çeşitli argümanlar, özellikle -y
(değişikliği yapma nedeniniz) boşluklar ve yeni satırlar içerecektir. Komut dosyasının 1992'den kalma olduğunu unutmayın, bu nedenle $(cmd ...)
gösterim yerine geri keneler kullanır
#!/bin/sh
ve ilk satırda kullanılmaz.
: "@(#)$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"
(Muhtemelen bu günlerde kaçmayı çok fazla kullanmazdım - -e
örneğin argümanla gerekli değildir - ancak genel olarak, bu benim kullandığım basit komut dosyalarından biridir escape
.)
escape
Program sadece oldukça gibi, bağımsız değişkenlerini verir echo
, ama bu argümanlar ile kullanım için korunmasını sağlamaktadır
eval
(Düz bir eval
, Uzak kabuk yürütmesini bir program var ve çıkışını kaçmak için gerekli escape
).
$ escape $var
'"./my' 'dir"' '"./anotherdir"'
$ escape "$var"
'"./my dir" "./anotherdir"'
$ escape x y z
x y z
$
al
Argümanlarını her satırda bir tane olarak listeleyen başka bir programım var (ve daha da eski: 1987-01-27T14: 35: 49 tarihli 1.1 sürümü). Komut dosyalarında hata ayıklama sırasında en kullanışlıdır, çünkü aslında hangi argümanların komuta iletildiğini görmek için bir komut satırına eklenebilir.
$ echo "$var"
"./my dir" "./anotherdir"
$ al $var
"./my
dir"
"./anotherdir"
$ al "$var"
"./my dir" "./anotherdir"
$
[ Eklendi:
Şimdi çeşitli "$@"
gösterimler arasındaki farkı göstermek için , bir örnek daha:
$ 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
$
Komut satırındaki *
ve arasındaki orijinal boşlukları hiçbir şeyin saklamadığına dikkat edin */*
. Ayrıca, kabuk kullanarak 'komut satırı bağımsız değişkenlerini' değiştirebileceğinize dikkat edin:
set -- -new -opt and "arg with space"
Bu, ' -new
', ' -opt
', ' and
' ve ' arg with space
' olmak üzere 4 seçenek ayarlar .
]
Hmm, bu oldukça uzun bir cevap - belki de tefsir daha iyi bir terimdir. escape
İstek üzerine temin edilebilen kaynak kodu (gmail dot com adresinden ad soyadının soyadına e-posta). İçin kaynak kodu al
inanılmaz derecede basit:
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return(0);
}
Bu kadar. test.sh
Robert Gamble'ın gösterdiği senaryoya eşdeğerdir ve bir kabuk işlevi olarak yazılabilir (ancak ilk yazdığımda Bourne kabuğunun yerel sürümünde kabuk işlevleri yoktu al
).
Ayrıca al
basit bir kabuk komut dosyası olarak yazabileceğinizi unutmayın :
[ $# != 0 ] && printf "%s\n" "$@"
Koşullu, argüman iletilmediğinde çıktı üretmemesi için gereklidir. printf
Komut sadece biçim dizesi bağımsız değişkenle boş satır üretecek, ancak C programı şey üretir.