Bu, unix hakkındaki bir takım sorularda tartışıldı. SE, burada bulabildiğim tüm sorunları toplamaya çalışacağım. Sonunda referanslar.
Neden başarısız
Bu problemlerle yüzleşmenizin sebebi kelime bölmek ve değişkenlerden genişletilmiş alıntıların tırnak işareti olarak görülmemesi, sadece sıradan karakterler olması.
Soruda sunulan durumlar:
$ abc='ls -l "/tmp/test/my dir"'
Burada, $abc
bölünmüş ve ls
iki argüman alır "/tmp/test/my
ve dir"
(birinci ve ikinci sıranın arkasındaki tırnak ile):
$ $abc
ls: cannot access '"/tmp/test/my': No such file or directory
ls: cannot access 'dir"': No such file or directory
Burada, genişleme alıntı, bu yüzden tek bir kelime olarak tutulur. Kabuk ls -l "/tmp/test/my dir"
, boşluklar ve alıntılar dahil bir program bulmaya çalışır .
$ "$abc"
bash: ls -l "/tmp/test/my dir": No such file or directory
Ve burada, sadece ilk kelime veya $abc
argüman olarak kabul edilir -c
, bu yüzden Bash sadece ls
geçerli dizinde çalışır . Başka bir deyişle bash argümanlar vardır ve doldurmak için kullanılır $0
, $1
vb
$ bash -c $abc
'my dir'
Ve bash -c "$abc"
, ve eval "$abc"
, tırnakların çalışmasını sağlayan ek bir kabuk işleme adımı vardır, ancak aynı zamanda tüm kabuk genişletmelerin yeniden işlenmesine neden olur , bu nedenle, siz değilseniz, kullanıcı tarafından sağlanan verilerden yanlışlıkla bir komut genişletme çalıştırma riski vardır. alıntı yapmak konusunda dikkatli olun.
Bunu yapmanın daha iyi yolları
Bir komutu kaydetmenin daha iyi iki yolu a) bunun yerine bir işlevi kullanmak, b) bir dizi değişkenini (veya konumsal parametreleri) kullanmaktır.
Bir işlev kullanma:
İçindeki komutla bir fonksiyon tanımlamanız ve fonksiyonu bir komutmuş gibi çalıştırmanız yeterlidir. İşlev içindeki komutlardaki genişlemeler, yalnızca komut çalıştığında yapılır, tanımlandığında değil ve ayrı ayrı komut vermeniz gerekmez.
# define it
myls() {
ls -l "/tmp/test/my dir"
}
# run it
myls
Bir dizi kullanarak:
Diziler, tek tek kelimelerin beyaz boşluk içerdiği çok kelimeli değişkenler oluşturmaya izin verir. Burada, tek tek kelimeler farklı dizi öğeleri olarak depolanır ve "${array[@]}"
genişleme her bir öğeyi ayrı bir kabuk sözcük olarak genişletir:
# define the array
mycmd=(ls -l "/tmp/test/my dir")
# run the command
"${mycmd[@]}"
Sözdizimi biraz korkunç, ancak diziler komut satırını parça parça yapmanıza da izin veriyor. Örneğin:
mycmd=(ls) # initial command
if [ "$want_detail" = 1 ]; then
mycmd+=(-l) # optional flag
fi
mycmd+=("$targetdir") # the filename
"${mycmd[@]}"
veya komut satırının bölümlerini sabit tutun ve dizinin sadece bir kısmını, seçenekleri veya dosya adlarını doldurmasını kullanın:
options=(-x -v)
files=(file1 "file name with whitespace")
target=/somedir
transmutate "${options[@]}" "${files[@]}" "$target"
Dizilerin dezavantajı, standart bir özellik olmaması, dolayısıyla düz POSIX mermileri ( Debian / Ubuntu'daki dash
varsayılan /bin/sh
) onları desteklemiyor (aşağıya bakınız). Ancak Bash, ksh ve zsh bunu yapar, bu nedenle sisteminizde dizileri destekleyen bir kabuk vardır.
kullanma "$@"
Adlandırılmış dizileri desteklemeyen kabuklarda "$@"
, bir komutun argümanlarını tutmak için konumsal parametreleri (sözde dizi ) kullanabilirsiniz.
Aşağıdaki, önceki bölümdeki kod bitlerinin eşdeğerini yapan taşınabilir komut dosyası bitleri olmalıdır. Dizi, "$@"
konumsal parametrelerin listesi ile değiştirilir . Ayar "$@"
ile yapılır set
ve etrafındaki çift tırnak "$@"
önemlidir (bunlar listedeki öğelerin tek tek alıntılanmasına neden olur).
Öncelikle, sadece argümanları içeren bir komutu saklamak "$@"
ve çalıştırmak
set -- ls -l "/tmp/test/my dir"
"$@"
Bir komut için komut satırı seçeneklerinin bölümlerini koşullu olarak ayarlama:
set -- ls
if [ "$want_detail" = 1 ]; then
set -- "$@" -l
fi
set -- "$@" "$targetdir"
"$@"
Sadece "$@"
seçenekler ve işlenenler için kullanma :
set -- -x -v
set -- "$@" file1 "file name with whitespace"
set -- "$@" /somedir
transmutate "$@"
(Elbette "$@"
, genellikle betiğin kendisinin argümanları ile doldurulur, bu nedenle yeniden işlemden önce onları bir yere kaydetmeniz gerekir "$@"
.)
Dikkatli ol eval
!
As eval
alıntı ve genişleme işleme tanıtır ek seviyede, kullanıcı girişi dikkatli olmak gerekir. Örneğin, kullanıcı herhangi bir tekli tırnak işareti yazmadığı sürece çalışır:
read -r filename
cmd="ls -l '$filename'"
eval "$cmd";
Ancak giriş '$(uname)'.txt
yaparlarsa, komut dosyanız mutlu bir şekilde komut değiştirmeyi çalıştırır.
Dizileri olan bir sürüm, kelimeler tüm zaman için ayrı tutulduğundan, içeriğinin alıntı veya başka işlemlerinin olmadığı bağışıklık kazanır filename
.
read -r filename
cmd=(ls -ld -- "$filename")
"${cmd[@]}"
Referanslar