$ BASH_COMMAND değerini değerlendirmek güvenli mi?


11

Değişkenlerden, örneğin Bash SSS'den öğrendiğim bir teknikle) karmaşık bir komut oluşturan bir kabuk komut dosyası üzerinde çalışıyorum :

#!/bin/bash

SOME_ARG="abc"
ANOTHER_ARG="def"

some_complex_command \
  ${SOME_ARG:+--do-something "$SOME_ARG"} \
  ${ANOTHER_ARG:+--with "$ANOTHER_ARG"}

Bu komut dosyası dinamik parametreler ekler --do-something "$SOME_ARG"ve --with "$ANOTHER_ARG"için some_complex_commandbu değişkenler tanımlanırsa. Şimdiye kadar bu iyi çalışıyor.

Ama şimdi aynı zamanda, örneğin betiğim bir hata ayıklama modunda çalıştırıldığında, komutu yazdırabilir veya günlüğe kaydedebilirim. Bu yüzden benim komut dosyası çalıştığında some_complex_command --do-something abc --with def, ben de bu syslog günlüğe kaydedebilirsiniz böylece bir değişken içinde bu komutu olmasını istiyorum.

Bash SSS , bu amaçla tuzağı ve değişkeni (örneğin hata ayıklama amacıyla) kullanmaDEBUG$BASH_COMMAND tekniğini gösterir . Ben aşağıdaki kod ile denedim:

#!/bin/bash

ARG="test string"

trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG"

echo "Command was: ${COMMAND}"

Bu çalışır, ancak komuttaki değişkenleri genişletmez:

host ~ # ./test.sh
test string
Command was: echo "$ARG"

Ben genişlemek echo "$ARG"için eval kullanmak zorunda sanırım echo test string(en azından evalhenüz olmadan bir yol bulamadı ). Aşağıdakiler işe yarar:

eval echo "Command was: ${COMMAND}"

Aşağıdaki çıktıyı üretir:

host ~ # ./test.sh
test string
Command was: echo "$ARG"
Command was: echo test string

Ancak evalbu şekilde güvenli bir şekilde kullanabileceğimden emin değilim . Başarısız bir şekilde bazı şeylerden yararlanmaya çalıştım:

#!/bin/bash

ARG="test string; touch /x"
DANGER='$(touch /y; cat /etc/shadow)'

trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG" $DANGER

echo "Command was: ${COMMAND}"
eval echo "Command was: ${COMMAND}"

Bunu iyi idare ediyor gibi görünüyor, ama başka birinin kaçırdığım bir sorun görüp görmediğini merak ediyorum.


Sudo ambalajında ​​yıllardır kullandığım şey bu, hiç bir sorun fark etmedim.
w00t

Yanıtlar:


7

Bir olasılık, aynı zamanda komutu yazdıracak ve aşağıdaki gibi çalıştıracak bir sarmalayıcı işlevi yapmaktır:

debug() {
    # This function prints (to stdout) its arguments and executes them
    local args=() idx=0 IFS=' ' c
    for c; do printf -v args[idx++] '%q' "$c"; done
    printf "%s\n" "${args[*]}"
    # Execute!
    "$@"
}

Böylece betiğinizde şunları yapabilirsiniz:

debug echo "$ARG"

Tuzağa düşmeye gerek yok. Dezavantajı, debugkodunuzun her yerine bazı anahtar kelimeler ekler (ancak bu iyi olmalı, ekler vb. Bu tür şeylere sahip olmak yaygındır).

Genel bir değişken bile ekleyebilir DEBUGve debugişlevi şu şekilde değiştirebilirsiniz :

debug() {
    # This function prints (to stdout) its arguments if DEBUG is non-null
    # and executes them
    if [[ $DEBUG ]]; then
        local args=() idx=0 IFS=' ' c
        for c; do printf -v args[idx++] '%q' "$c"; done
        printf "%s\n" "${args[*]}"
    fi
    # Execute!
    "$@"
}

Ardından komut dosyanızı şu şekilde çağırabilirsiniz:

$ DEBUG=yes ./myscript

veya

$ DEBUG= ./myscript

ya da sadece

$ ./myscript

hata ayıklama bilgisine sahip olmak isteyip istemediğinize bağlı olarak.

DEBUGDeğişkeni büyük harfle yazdım çünkü bir ortam değişkeni olarak ele alınması gerekiyor. DEBUGönemsiz ve yaygın bir addır, bu nedenle bu, diğer komutlarla çakışabilir. Belki onu arayın GNIOURF_DEBUGya MARTIN_VON_WITTICH_DEBUGda UNICORN_DEBUGtek boynuzlu atları seviyorsanız (ve muhtemelen midillileri de seviyorsunuz).

Not. In debugfonksiyonu dikkatle her argüman biçimlendirilmiş printf '%q'çıktı düzgün kaçtı ve doğrudan kopya ile yeniden harfi harfine olmak ve yapıştırmak şekilde alıntı edilecektir böylece. Ayrıca, her bir argümanı (boşluklar veya diğer komik semboller durumunda) anlayabileceğiniz için kabuğun ne gördüğünü tam olarak gösterecektir. Bu işlev ayrıca gereksiz alt kabukları önlemek için -vanahtarı ile doğrudan atama kullanır printf.


1
İşlev harika çalışıyor, ancak maalesef komutum yeniden yönlendirmeler ve "arka planda excute" kontrol operatörü içeriyor &. Bunları işe almak için işleve taşımak zorunda kaldım - çok hoş değil, ama daha iyi bir yol olduğunu düşünmüyorum. Ama daha fazla değil eval, bu yüzden benim için gidiş var, bu güzel :)
Martin von Wittich

5

eval "$BASH_COMMAND" komutu çalıştırır.

printf '%s\n' "$BASH_COMMAND" tam olarak belirtilen komutu ve ayrıca bir yeni satırı yazdırır.

Komut değişken içeriyorsa (örn. Böyle bir şeyse cat "$foo"), komutun yazdırılması değişken metni yazdırır. Komutu çalıştırmadan değişkenin değerini yazdırmak imkansızdır - komutları düşünün variable=$(some_function) other_variable=$variable.

Kabuk betiğinin yürütülmesinden bir iz almanın en basit yolu xtrace, betiği çalıştırarak bash -x /path/to/scriptveya set -xkabuğun içinde çağırarak kabuk seçeneğini belirlemektir . İz, standart hataya yazdırılır.


1
Biliyorum xtrace, ama bu bana çok fazla kontrol vermiyor. Ben "set -x; ...; set + x" denedim, ama: 1) xtrace devre dışı bırakmak için "set + x" komutu da basılır 2) Çıktıya önek ekleyemem örneğin bir zaman damgası 3) sistem günlüğüne kaydetme.
Martin von Wittich

2
"Komutu çalıştırmadan değişkenin değerini yazdırmak imkansız" - iyi bir örnek, böyle bir durumu düşünmemiştim. Eğer bash BASH_COMMANDgenişletilmiş komutun yanında ek bir değişken olsaydı iyi olurdu , çünkü bir noktada komutu çalıştırırken yine de komutta değişken genişletme yapmak zorunda :)
Martin von Wittich
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.