Kelime bölmeyi önlemek için bir dizenin içindeki değişken genişletmeyi nasıl teklif edebilirim?


9
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"

Böyle bir durumda, $myvardeğerindeki boşluklardan dolayı kelime bölünmesini önlemek için nasıl teklif verebilirim myvar?


4
Bunu nasıl çözerseniz seçin, hala pek bir şey yapmaz. cdİçinde yalnızca bir etkiye sahip bash -ckabuk.
Kusalananda

Ben sadece soru için sadece minimal bir örnek veriyorum, gerçek sorunum için çalışan bir çözüm değil, bu unix.stackexchange.com/a/269080/674 artı verilen komut dizesinde sudo bash -cdeğişken bir genişleme var boşluklar içerebilecek bir yol adı.
Tim

Yanıtlar:


13

Hiçbir var sözcüklere ayırma o kodda olduğu gibi (tırnaksız açılımları üzerine böler değişkenler bu özelliği gibi) $myvartırnak içine edilmez.

Bununla birlikte $myvar, geçirilmeden önce genişletilen bir komut enjeksiyon güvenlik açığı bulunmaktadır bash. Yani içeriği bash kodu olarak yorumlanıyor!

Buradaki boşluklar cd, kelime bölme nedeniyle değil , kabuk sözdiziminde birkaç jeton olarak ayrıştırılacağı için birkaç argümanın iletilmesine neden olur . Değeri ile bye;rebootbu yeniden başlatılacak!

Burada şunu istersiniz:

sudo bash -c 'cd -P -- "$1"' bash "$myvar"

( $myvarbu satır içi komut dosyasının içeriğini ilk argüman olarak geçtiğinizde, IFS kelimesinin bölünmesini (ve globbing'i) önlemek için her ikisinin $myvarve $1kendi kabukları için nasıl alıntılandığını not edin ).

Veya:

sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'

(içeriğini $myvarbir ortam değişkeninde ilettiğiniz yer).

Tabii ki, sadece cd bu satır içi komut dosyasında ( oraya girip giremeyeceğini kontrol etmek dışında) faydalı bir şey elde rootedemezsiniz cd. Muhtemelen, o betiğin cdoraya gitmesini ve ardından başka bir şey yapmasını istersiniz:

sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"

Niyeti kullanmak olsaydı sudoedebilmek için cdaksi sonra, o olamaz gerçekten işi için erişimi olmayan bir dizine.

sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"

bashgeçerli dizini ile bir etkileşimli başlatır $myvar. Ama bu kabuk şöyle çalışacak root.

Şunları yapabilirsiniz:

sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"

bashGeçerli dizinin ayrıcalıklı bir etkileşimli olmasını sağlamak için $myvar, ancak cdilk etapta o dizine girme izniniz yoksa, geçerli dizininiz olsa bile bu dizinde hiçbir şey yapamazsınız.

$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied

Bir istisna , dizinin kendisine arama izniniz varsa , ancak yolunun dizin bileşenlerinden birine sahip değilseniz:

$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied

Speaking açık konuşmak gerekirse, $myvarbenzer değerler için $(seq 10)(kelimenin tam anlamıyla), bu komut ikamesinin bash kabuk tarafından genişletilmesi üzerine elbette kelime bölünmesi olacaktır.root


4

İstediğinizi daha doğrudan yapmanıza izin veren yeni (bash 4.4) alıntı sihri var. Daha büyük bağlama bağlı olarak, diğer tekniklerden birini kullanmak daha iyi olabilir, ancak bu sınırlı bağlamda çalışır.

sudo bash -c "cd ${myvar@Q}; pwd"

4

İçeriğine güvenemiyorsanız, $myvartek makul yaklaşım, printfkaçan bir sürümünü oluşturmak için GNU'yu kullanmaktır :

#!/bin/bash

myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"

Gibi printfadam sayfada şunlar belirtiliyor:

%q

ARGUMENT, kabuk girişi olarak yeniden kullanılabilecek bir formatta yazdırılır ve yazdırılamayan karakterlerden önerilen POSIX $''sözdiziminden kaçar .


Bunun taşınabilir bir sürümü için cevabımı görün.
R .. GitHub BUZA YARDIMCI DURDUR

GNU printfyalnızca GNU sistemlerinde ve bu yerleşik olmayan $(printf '%q' "$myvar")bir kabukta çalıştırılırsa çalıştırılacaktır printf. Çoğu mermi printfgünümüzde inşa edilmiştir. Hepsi bir %qdüşünceyi desteklemez ve yaptıkları zaman, karşılık gelen kabuk tarafından desteklenen bir formatta bir şey teklif ederlerdi, bu da anlaşıldığı gibi olmayabilir bash. Aslında bash'ın printfbile kendisinin bazı değerler için düzgün şeyler alıntı başarısız $myvarbazı yerlerde.
Stéphane Chazelas

İle bash-4.3en azından bir komut enjeksiyon açığı ile hala myvar=$'\xa3``reboot`\xa3`' bir zh_HK.big5hkscs örneğin yerel ayarında içinde.
Stéphane Chazelas

3

Her şeyden önce, diğerlerinin belirttiği gibi, cdkomutunuz işe yaramaz çünkü hemen çıkan bir kabuk bağlamında gerçekleşir. Ama genel olarak soru mantıklı. İhtiyacınız olan şey, kabuklu bir dize olarak yorumlanacağı bir bağlamda kullanılabilmesi için isteğe bağlı bir dizeyi kabuk olarak alıntılamanın bir yoludur . Benim hileler sayfamda bunun bir örneği var :

quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }

Bu işlevle şunları yapabilirsiniz:

myvar="/path to/my directory"
sudo bash -c "cd $(quote "$myvar")"

3

Stéphane Chazelas'ın önerdiği şey kesinlikle bunu yapmanın en iyi yoludur. Ben sadece sedR .. cevap gibi bir alt işlem kullanmak yerine parametre genişletme sözdizimi ile güvenle alıntı nasıl bir cevap sağlayacaktır .

myvar='foo \'\''"bar'

quote() {
  printf "'%s'" "${1//\'/\'\\\'\'}"
}

printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"

Bu komut dosyasının çıktısı:

value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar

1

Evet, kelime bölme var.
Teknik olarak komut satırını bash üzerinde bölen komut satırı.

Önce doğru alıntıları yapalım:

İsteyeceğinize inandığım gibi komutun çalışmasını sağlamak için çözümlerin en basit (ve güvensiz):

bash -c "cd \"$myvar\""

Ne olacağını doğrulayalım (varsayalım myvar="/path to/my directory"):

$ bash -c "printf '<%s>\n' $myvar"
<./path>
<to/my>
<directory>

Gördüğünüz gibi, yol dizesi boşluklara bölünmüştü. Ve:

$ bash -c "printf '<%s>\n' \"$myvar\""
<./path to/my directory>

Bölünmüş değil.

Ancak, komut (yazıldığı gibi) güvensizdir. Daha iyi kullanım:

$ bash -c "cd -P -- \"$myvar\"; pwd"
/home/user/temp/path to/my directory

Bu, cd'yi tire (-) ile başlayan veya sorunlara yol açabilecek bağlantıları izleyen dosyalara karşı koruyacaktır.

Bu, dizenin $myvarbir yol olduğuna güvenebiliyorsanız işe yarayacaktır .
Ancak, "kod yerleştirme", "bir dize çalıştırdığınız" için hala mümkündür:

$ myvar='./path to/my directory";date;"true'
$ bash -c "printf '<%s> ' \"$myvar\"; echo"
<./path to/my directory> Tue May  8 12:25:51 UTC 2018

Dize için daha güçlü bir alıntı yapmanız gerekir, örneğin:

$ bash -c 'printf "<%s> " "$1"; echo' _ "$myvar"
<./path to/my directory";date -u;"true>

Yukarıda da görebileceğiniz gibi, değer yorumlanmamış, sadece olarak kullanılmıştır "$1'.

Şimdi sudo'yu (güvenli bir şekilde) kullanabiliriz:

$ sudo bash -c 'cd -P -- "$1"; pwd' _ "$myvar"
_: line 0: cd: ./path to/my directory";date -u;"true: No such file or directory

Birkaç argüman varsa, şunları kullanabilirsiniz:

$ sudo bash -c 'printf "<%s>" "$@"' _ "$val1" "$val2" "$val3"

-2

Tek tırnak işaretleri burada çalışmaz, çünkü o zaman değişkeniniz genişletilmez. Yolunuzun değişkeninin dezenfekte edildiğinden eminseniz, en basit çözüm, yeni bash kabuğuna girdikten sonra ortaya çıkan genişleme etrafına tırnak eklemektir:

sudo bash -c "cd \"$myvar\""

2
Hala veya $myvarbenzeri değerler için bir komut enjeksiyon güvenlik açığı$(reboot)"; reboot; #
Stéphane Chazelas

1
kullanıldığında sudo bash -c "cd -P -- '$myvar'"sorun yalnızca $myvar sterilize edilmesi daha kolay olan tek tırnak karakteri içeren değerlerle sınırlanır .
Stéphane Chazelas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.