Cara mencetak hanya variabel yang ditentukan (variabel shell dan / atau lingkungan) di bash


57

Perintah bash builtin set, jika dijalankan tanpa argumen, akan mencetak semua variabel shell dan environment, tetapi juga semua fungsi yang didefinisikan. Ini membuat output tidak dapat digunakan oleh manusia dan sulit untuk dilakukan grep.

Bagaimana saya bisa membuat perintah bash builtin sethanya mencetak variabel dan bukan fungsinya?

Apakah ada perintah lain yang hanya mencetak variabel shell, tanpa fungsi?

Catatan: bash membedakan antara variabel shell dan variabel lingkungan. lihat di sini Perbedaan antara variabel lingkungan dan variabel lingkungan yang diekspor di bash

Jawaban:


51

"Apakah ada perintah lain yang hanya mencetak variabel shell, tanpa fungsi?"

Dalam man bash, di bagian SHELL BUILTIN COMMANDS (di bagian set) dikatakan: "Dalam mode posix, hanya variabel shell yang terdaftar."

(set -o posix; set)

Catatan: ()Sintaks memunculkan subkulit, jika Anda tidak suka forking hanya menggunakan versi yang lebih verbose

set -o posix; set; set +o posix

1
Sejauh ini solusi terbaik
Ruslan

1
Ada banyak variabel yang biasanya tidak menarik yang dimulai dengan _, yang mudah untuk dihilangkan:(set -o posix ; set | grep -v ^_)
hyde

Ini telah mengganggu saya terlalu lama! Mengambil garis yang dimulai dengan _ tidak cukup jika Anda memiliki nilai multi-baris, itu masih akan meninggalkan isi nilai dalam kebijaksanaan. Karena set mencetak garis-garis secara berurutan, semua _ garis akan berada di akhir, sehingga Anda dapat memotong hasilnya setelah garis _ pertama, menggunakan:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott

1
Hanya sebuah catatan: tanda kurung penting (set -o posix; set). Tanpa tanda kurung perilaku shell Bash saat ini akan diubah agar sesuai dengan standar Posix. (Entah apa artinya tapi terlihat penting.) Dengan tanda kurung, Bash tetap tidak berubah.
John Red

1
Efek samping : Set POSIXLY_CORRECT=ydan menambahkan posixke$SHELLOPTS
Tom Hale

31

Berikut ini beberapa solusinya:

$ comm -3 <(declare | sort) <(declare -f | sort)

kerusakan:

  1. declare mencetak setiap variabel yang ditentukan (diekspor atau tidak) dan berfungsi.
  2. declare -f hanya mencetak fungsi.
  3. comm -3akan menghapus semua baris yang umum untuk keduanya. Akibatnya ini akan menghapus fungsi, hanya menyisakan variabel.

Untuk hanya mencetak variabel yang tidak diekspor:

$ comm -3 <(comm -3 <(declare | sort) <(declare -f | sort)) <(env | sort)

Solusi lain:

$ declare -p

Ini hanya akan mencetak variabel, tetapi dengan beberapa atribut jelek.

declare -- BASH="/bin/bash"
declare -ir BASHPID=""
declare -A BASH_ALIASES='()'
declare -a BASH_ARGC='()'
...

Anda dapat memotong atribut menggunakan ... potong:

$ declare -p | cut -d " " -f 3

Satu kelemahannya adalah bahwa nilai IFS ditafsirkan alih-alih ditampilkan.

membandingkan:

$ comm -3 <(declare | sort) <(declare -f | sort)
...
IFS=$' \t\n'
...
$ declare -p | cut -d " " -f 3
...
IFS="
"
...

Ini membuatnya sangat sulit untuk menggunakan output itu untuk diproses lebih lanjut, karena itu satu-satunya "dalam satu baris. Mungkin beberapa IFS-fu dapat dilakukan untuk mencegah hal ini.


Solusi lain, menggunakan compgen:

$ compgen -v

Bash builtin compgendimaksudkan untuk digunakan dalam skrip penyelesaian. Untuk tujuan ini, compgen -vdaftarkan semua variabel yang ditentukan. The downside: itu hanya mencantumkan nama variabel, bukan nilai.

Berikut ini adalah retasan untuk juga mencantumkan nilai.

$ compgen -v | while read var; do printf "%s=%q\n" "$var" "${!var}"; done

Keuntungan: itu adalah solusi bash murni. Kerugiannya: beberapa nilai kacau karena interpretasi yang mendalam printf. Subkulit dari pipa dan / atau loop menambahkan beberapa variabel tambahan.


apakah declare ...| cutpipa Anda pecah jika tidak ada atribut untuk variabel? Saya menduga itu --digunakan dalam kasus itu, tapi saya masih gelisah. sedmungkin lebih aman, yaitudeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd

+1 untuk compgen:)
RSFalcon7

13

The typesetbuiltin anehnya memiliki pilihan untuk menampilkan fungsi saja ( -f) tapi tidak untuk menunjukkan parameter saja. Untungnya, typeset(atau set) menampilkan semua parameter sebelum semua fungsi, fungsi dan nama parameter tidak dapat berisi baris baru atau tanda sama dengan, dan baris baru dalam nilai parameter dikutip (mereka muncul sebagai \n). Jadi Anda bisa berhenti di baris pertama yang tidak mengandung tanda sama dengan:

set | awk -F '=' '! /^[0-9A-Z_a-z]+=/ {exit} {print $1}'

Ini hanya mencetak nama parameter; jika Anda menginginkan nilainya, ubah print $1ke print $0.

Perhatikan bahwa ini mencetak semua nama parameter (parameter dan variabel digunakan secara sinonim di sini), bukan hanya variabel lingkungan ("variabel lingkungan" identik dengan "parameter yang diekspor").

Perhatikan juga bahwa ini mengasumsikan tidak ada variabel lingkungan dengan nama yang tidak cocok dengan batasan bash pada nama parameter. Variabel seperti itu tidak dapat dibuat di dalam bash tetapi bisa diwarisi dari lingkungan saat bash dimulai:

env 'foo ()
{
  =oops' bash

Solusi hebat! Saya bahkan tidak berpikir untuk berhenti di baris pertama yang tidak memiliki '='. +1
Steven D

Setara, dengan sed: set | sed '/=/!Q'
Toby Speight

7

Saya tidak yakin bagaimana orang dapat membuat setvariabel hanya mencetak. Namun dengan melihat output dari set, saya bisa datang dengan yang berikut ini yang tampaknya mengambil hanya variabel:

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" 

Pada dasarnya, saya mencari garis yang dimulai dengan huruf, angka, atau tanda baca diikuti oleh "=". Dari output yang saya lihat, ini mengambil semua variabel; Namun, saya ragu bahwa ini sangat portabel.

Jika, seperti judul Anda menyarankan, Anda ingin mengurangi dari daftar ini variabel yang diekspor dan dengan demikian mendapatkan daftar variabel yang tidak diekspor, maka Anda bisa melakukan sesuatu seperti ini

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars && env | sort | comm -23 ./setvars - 

Untuk sedikit menguraikan hal ini, inilah fungsinya:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Mudah-mudahan ini mendapatkan semua variabel (seperti yang dibahas sebelumnya), mengurutkannya, dan menempelkan hasilnya dalam file.
  2. && env | sort: Setelah perintah sebelumnya selesai, kita akan memanggil envdan mengurutkan hasilnya.
  3. | comm -23 ./setvars -: Akhirnya, kami menyalurkan output yang diurutkan envmenjadi commdan menggunakan -23opsi untuk mencetak garis yang unik untuk argumen pertama, dalam hal ini garis yang unik untuk output kami dari set.

Ketika Anda selesai, Anda mungkin ingin membersihkan file temp yang dibuat dengan perintah rm ./setvars


Masalah dengan perintah Anda bukanlah portabilitas (ini khusus untuk bash, tentu saja, tetapi tidak ada masalah di sisi grep). Masalahnya adalah Anda mengambil beberapa baris dalam definisi fungsi. Bash indentasi sebagian besar kode fungsi, tetapi tidak semuanya, khususnya bukan teks di dalam dokumen ini (di f () {|cat <<EOF|foo=bar|EOF|}mana |merupakan pemisah baris).
Gilles 'SANGAT berhenti menjadi jahat'


2

Coba perintah printenv:

$printenv

6
printenv dan env hanya mencetak variabel yang diekspor (lingkungan) dan bukan variabel yang tidak diekspor (shell).
Lri

@Lri Terima kasih! Saya bertanya-tanya bagaimana cara mencetak hanya variabel yang diekspor.
Mark Stewart

1

Dalam bash, ini hanya akan mencetak nama variabel:

compgen -v

Atau, jika nilai juga diperlukan, gunakan:

declare -p

Terima kasih atas usaha Anda. harap dicatat bahwa saya telah menyebutkan kemungkinan itu dalam jawaban saya unix.stackexchange.com/a/5691/1170 tetap memiliki upvote.
lesmana

0

berbeda dengan shell yang bersih untuk memastikan vars dari skrip rc bisa ditinggalkan

## get mostly local vars
diff_env(){
    diff <(bash -cl 'set -o posix && set') \
        <(set -o posix && set && set +o posix) | \
        grep -E "^>|^\+" | \
        grep -Ev "^(>|\+|\+\+) ?(BASH|COLUMNS|LINES|HIST|PPID|SHLVL|PS(1|2)|SHELL|FUNC)" | \
        sed -r 's/^> ?|^\+ ?//'
}

versi dengan commakan terlalu berbelit-belit


0

Jawaban yang diterima bagus. Saya menawarkan alternatif yang tidak melibatkan pengaturan mode POSIX:

Inilah teknik yang telah saya gunakan untuk menyimpan status fungsi ke file sehingga saya bisa melanjutkannya nanti. Yang pertama menghasilkan dump negara dan yang kedua hanya menghasilkan daftar nama-nama variabel.

set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo $a; done 

set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo ${a/=*}; done 

Keduanya bekerja dengan membuang baris hingga menemukan satu tanpa char '=', yang terjadi ketika fungsi pertama terdaftar. Yang terakhir memotong tugas.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.