Bash'de betikte bildirilen değişkenler nasıl listelenir?


98

Bash'deki betiğimde birçok değişken var ve bunları dosyaya kaydetmek için bir şeyler yapmam gerekiyor. Sorum, komut dosyamda bildirilen tüm değişkenleri nasıl listeleyeceğim ve şöyle bir liste alacağım:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

Yanıtlar:


151

set değişkenlerin çıktısını alacak, ne yazık ki aynı zamanda işlevlerin tanımladığı çıktıları da verecektir.

Neyse ki POSIX modu yalnızca değişkenleri çıkarır:

( set -o posix ; set ) | less

İçin Boru lessSeçenekleri istediğiniz yere veya yönlendirme.

Dolayısıyla, yalnızca komut dosyasında açıklanan değişkenleri almak için:

( set -o posix ; set ) >/tmp/variables.before
source script
( set -o posix ; set ) >/tmp/variables.after
diff /tmp/variables.before /tmp/variables.after
rm /tmp/variables.before /tmp/variables.after

(Ya da en azından buna dayalı bir şey :-))


Ben gönderirken düzenleme yaptın. -o posixŞimdi bir fark ile güzel çağrı yalnızca değişkenleri içerecektir.
ezpz

8
Geçici dosyaları kullanarak olmadan: VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Bu ayrıca değişkenleri kaydetmeye hazır bir biçimde çıkarır. Liste, komut dosyasının değiştirdiği değişkenleri içerecektir (bunun istenip istenmediğine bağlıdır)
ivan_pozdeev

1
@ErikAronesty ile bir ortamı yeniden yaratabilmek sourceistiyorsanız, bunu çıktısı ile yapabilmelisiniz declare -p.
Altı

2
Geçici dosyaların kullanımına başvurmadan önce ve sonra bir ortamı diff (veya benzer herhangi bir komut) ile karşılaştırmak istiyorsanız, bunu aşağıdaki şekilde yapabilirsiniz:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Altı

1
@Caesar Hayır, sadece boş olmayan dizileri denedim. Boş diziler durumunda haklısınız - basılmazlar.
Socowi


11
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

Bu, tüm kabuk değişken adlarını yazdırmalıdır. Hangi değişkenlerin yeni olduğunu ayırt etmek için (diğer cevaplarda açıklandığı gibi) "set" ile olduğu gibi dosyanızı kaynaklamadan önce ve sonra bir liste alabilirsiniz. Ancak, diff ile bu tür filtrelemenin, ihtiyacınız olan ancak dosyanızı tedarik etmeden önce mevcut olan bazı değişkenleri filtreleyebileceğini unutmayın.

Sizin durumunuzda, değişkenlerinizin adlarının "DEĞİŞKEN" ile başladığını biliyorsanız, o zaman komut dosyanızı kaynaklayabilir ve şunları yapabilirsiniz:

for var in ${!VARIABLE@}; do
   printf "%s%q\n" "$var=" "${!var}"
done

GÜNCELLEME: Saf BASH çözümü için (harici komut kullanılmaz):

for i in _ {a..z} {A..Z}; do
   for var in `eval echo "\\${!$i@}"`; do
      echo $var
      # you can test if $var matches some criteria and put it in the file or ignore
   done 
done

1
+1 ve bir oneliner sürümü (tek bir değerlendirmeyle):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj

4

Yukarıdaki cevaplardan bazılarına dayanarak, bu benim için çalıştı:

before=$(set -o posix; set | sort);

kaynak dosya :

comm -13 <(printf %s "$before") <(set -o posix; set | sort | uniq) 

3

Sonradan işlem yapabiliyorsanız, (daha önce bahsedildiği gibi) setbetiğinizin başına ve sonuna (her biri farklı bir dosyaya) bir çağrı yapabilir ve iki dosyada bir fark yapabilirsiniz. Bunun hala biraz gürültü içereceğini anlayın.

Bunu programlı olarak da yapabilirsiniz. Çıktıyı yalnızca mevcut kapsamınızla sınırlamak için, değişken oluşturmaya bir sarmalayıcı uygulamanız gerekir. Örneğin

store() {
    export ${1}="${*:2}"
    [[ ${STORED} =~ "(^| )${1}($| )" ]] || STORED="${STORED} ${1}"
}

store VAR1 abc
store VAR2 bcd
store VAR3 cde

for i in ${STORED}; do
    echo "${i}=${!i}"
done

Hangi verim

VAR1=abc
VAR2=bcd
VAR3=cde

2

İşte @ GinkgoFr yanıtına benzer, ancak @Tino veya @DejayClayton tarafından tanımlanan sorunlar olmadan ve @ DouglasLeeder'ın akıllı bitinden daha sağlam set -o posix:

+ function SOLUTION() { (set +o posix; set) | sed -ne '/^\w\+=/!q; p;'; }

Aradaki fark, bu çözümün ilk değişken olmayan rapordan sonra DURMASI, örn. set

BTW: "Tino" sorunu çözüldü. POSIX kapatılır ve işlevleri tarafından bildirilen rağmen set, sed ...çözümün kısmı sadece (örneğin aracılığıyla değişken raporlar verir VAR=VALUEçizgiler). Özellikle, A2yok değil spuriously çıktı dönüştürebilmek.

+ function a() { echo $'\nA2=B'; }; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A9=999

VE: "DejayClayton" sorunu (değişken değerler gömülü yeni satırlar do çözüldü değil çıktıyı bozmak - her VAR=VALUEtek çıkış hattı almak):

+ A1=$'111\nA2=222'; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A1=$'111\nA2=222'
A9=999

NOT: @DouglasLeeder tarafından sağlanan çözüm, "DejayClayton" sorunundan muzdariptir (gömülü satırsonu değerleri). Aşağıda A1yanlış ve A2hiç gösterilmemelidir.

$ A1=$'111\nA2=222'; A0=000; A9=999; (set -o posix; set) | grep '^A[0-9]='
A0=000
A1='111
A2=222'
A9=999

SONUNDA: Konuların versiyonunu düşünmüyorum bash, ama olabilir. Test / geliştirmemi bunun üzerinde yaptım:

$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)

POST-SCRIPT: OP'ye verilen diğer bazı yanıtlar göz önüne alındığında , değer içindeki satırları set her zaman dönüştürdüğünden <% 100 eminim \n, bu çözüm "DejayClayton" sorununu önlemek için buna dayanıyor. Belki de bu modern bir davranış? Veya bir derleme zamanı varyasyonu? Veya bir set -oveya shoptseçenek ayarı? Eğer siz böyle varyasyonların biliyoruz Yorum eklemek ... Lütfen


0

Bir komut dosyası kullanmayı deneyin (buna "ls_vars" diyelim):

  #!/bin/bash
  set -a
  env > /tmp/a
  source $1
  env > /tmp/b
  diff /tmp/{a,b} | sed -ne 's/^> //p'

chmod + x it ve:

  ls_vars your-script.sh > vars.files.save

0

Güvenlik açısından, ya @ akostadinov'un yanıtı ya da @ JuvenXu'nun yanıtı , setaşağıdaki olası güvenlik açığı nedeniyle komutun yapılandırılmamış çıktısına güvenmeye tercih edilir :

#!/bin/bash

function doLogic()
{
    local COMMAND="${1}"
    if ( set -o posix; set | grep -q '^PS1=' )
    then
        echo 'Script is interactive'
    else
        echo 'Script is NOT interactive'
    fi
}

doLogic 'hello'   # Script is NOT interactive
doLogic $'\nPS1=' # Script is interactive

Yukarıdaki işlev , komut dosyasının etkileşimli olup olmadığını belirlemek için değişkenin varlığını kontrol etmek için doLogickullanır (bu hedefi gerçekleştirmenin en iyi yolu olup olmadığını boşverin; bu sadece bir örnektir.)setPS1

Bununla birlikte, çıktısı setyapılandırılmamıştır, bu da bir satırsonu içeren herhangi bir değişkenin sonuçları tamamen kirletebileceği anlamına gelir.

Bu, elbette, potansiyel bir güvenlik riskidir. Bunun yerine, ya Bash'in dolaylı değişken adı genişletme desteğini ya da compgen -v.


0

Şunu deneyin: set | egrep "^\w+="(ile veya olmadan| less borulu borusuz)

Önerilen ilk çözüm ( set -o posix ; set ) | lessişe yarıyor ancak bir dezavantajı var: kontrol kodlarını terminale iletiyor, böylece düzgün görüntülenmiyorlar. Örneğin, bir değişken varsa (muhtemelen)IFS=$' \t\n' , şunu görebiliriz:

IFS='
'

…yerine.

Benim egrepçözümüm bunu (ve sonunda diğer benzerlerini) düzgün bir şekilde gösteriyor.


8 yıl oldu, biliyorsun
lauriys

Olumsuz oy verildi çünkü bu açıkça yanlış bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A görüntüler A=B"-> Başarısız!
Tino

Sadece "_" (önceki komutun son argümanı) sözde değişkenini ima ediyor gibi görünen ancak diğerlerinde, özellikle IFS'de başarısız olmayan garip test. "Bash -c" altında çalıştırmak da önemli olmayabilir. Olumsuz oy kullanmak yerine en azından tarafsız kalmalıydın.
GingkoFr

0

Muhtemelen cevabı bir süre önce çaldım ... Zaten biraz farklı bir işlev olarak:

    ##
    # usage source bin/nps-bash-util-funcs
    # doEchoVars
    doEchoVars(){

        # if the tmp dir does not exist
        test -z ${tmp_dir} && \
        export tmp_dir="$(cd "$(dirname $0)/../../.."; pwd)""/dat/log/.tmp.$$" && \
        mkdir -p "$tmp_dir" && \
        ( set -o posix ; set )| sort >"$tmp_dir/.vars.before"


        ( set -o posix ; set ) | sort >"$tmp_dir/.vars.after"
        cmd="$(comm -3 $tmp_dir/.vars.before $tmp_dir/.vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
        echo -e "$cmd"
    } 

0

printenvkomut:

printenvenvironment variablesdeğerleriyle birlikte yazdırır .

İyi şanslar...


0

Bunu yapmanın basit yolu , komut dosyanızı çalıştırmadan önce sistem ortamı değişkenlerini ayarlayarak sıkı sıkı modunu kullanmak ve yalnızca komut dosyanızı sıralamak için diff kullanmaktır:

# Add this line at the top of your script :
set > /tmp/old_vars.log

# Add this line at the end of your script :
set > /tmp/new_vars.log

# Alternatively you can remove unwanted variables with grep (e.g., passwords) :
set | grep -v "PASSWORD1=\|PASSWORD2=\|PASSWORD3=" > /tmp/new_vars.log

# Now you can compare to sort variables of your script :
diff /tmp/old_vars.log /tmp/new_vars.log | grep "^>" > /tmp/script_vars.log

Betiğinizin değişkenlerini artık /tmp/script_vars.log'da alabilirsiniz. Ya da en azından buna dayalı bir şey!


0

Partiye biraz geç ama işte başka bir öneri:

#!/bin/bash

set_before=$( set -o posix; set | sed -e '/^_=*/d' )

# create/set some variables
VARIABLE1=a
VARIABLE2=b
VARIABLE3=c

set_after=$( set -o posix; unset set_before; set | sed -e '/^_=/d' )
diff  <(echo "$set_before") <(echo "$set_after") | sed -e 's/^> //' -e '/^[[:digit:]].*/d'

Fark + sed boru hattı komut satırı istenen biçiminde tüm komut tanımlanmış değişkenleri (OP yayınının belirtildiği gibi) verir:

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
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.