Cách in chỉ các biến được xác định (shell và / hoặc biến môi trường) trong bash


57

Lệnh bash dựng sẵn set, nếu được gọi mà không có đối số, sẽ in tất cả các biến shell và môi trường, nhưng cũng có tất cả các hàm được xác định. Điều này làm cho đầu ra không thể sử dụng cho con người và khó khăn grep.

Làm thế nào tôi có thể làm cho lệnh bash dựng sẵn chỉ setin các biến chứ không phải các hàm?

Có các lệnh khác chỉ in các biến shell, không có các hàm không?

Lưu ý: bash phân biệt giữa các biến shell và biến môi trường. xem ở đây Sự khác biệt giữa các biến môi trường và biến môi trường xuất trong bash

Câu trả lời:


51

"Có các lệnh khác chỉ in các biến shell, không có các hàm không?"

Trong man bash, trong phần SHELL BUILTIN THÔNG TIN (trong phần thiết lập) có ghi: "Trong chế độ posix, chỉ có các biến shell được liệt kê."

(set -o posix; set)

lưu ý: ()cú pháp sinh ra một subshell, nếu bạn không muốn sử dụng phiên bản dài dòng hơn

set -o posix; set; set +o posix

1
Cho đến nay, giải pháp tốt nhất
Ruslan

1
Có một loạt các biến thường không thú vị bắt đầu bằng _, rất dễ bị loại bỏ:(set -o posix ; set | grep -v ^_)
hyde

Điều này đã làm phiền tôi quá lâu! Cắt bớt các dòng bắt đầu bằng _ là không đủ nếu bạn có các giá trị nhiều dòng, nó vẫn sẽ để lại nội dung của giá trị trong chiến thuật. Vì thiết lập in các dòng theo thứ tự, tất cả các dòng _ sẽ ở cuối, vì vậy bạn chỉ cần cắt kết quả sau dòng _ đầu tiên, sử dụng:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott

1
Chỉ cần một lưu ý: dấu ngoặc là quan trọng trong (set -o posix; set). Nếu không có dấu ngoặc, hành vi của vỏ Bash hiện tại sẽ được thay đổi để phù hợp với tiêu chuẩn Posix. (Không có nghĩa là gì nhưng có vẻ quan trọng.) Với dấu ngoặc, Bash vẫn không thay đổi.
John Red

1
Tác dụng phụ : Đặt POSIXLY_CORRECT=yvà thêm posixvào$SHELLOPTS
Tom Hale

31

Dưới đây là một số cách giải quyết:

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

phá vỡ:

  1. declare in mọi biến được xác định (xuất hoặc không) và hàm.
  2. declare -f chỉ in các chức năng.
  3. comm -3sẽ loại bỏ tất cả các dòng chung cho cả hai. Trong thực tế, điều này sẽ loại bỏ các chức năng, chỉ để lại các biến.

Để chỉ in các biến không được xuất:

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

Một cách giải quyết khác:

$ declare -p

Điều này sẽ chỉ in các biến, nhưng với một số thuộc tính xấu.

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

Bạn có thể cắt các thuộc tính bằng cách sử dụng ... cắt:

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

Một nhược điểm là giá trị của IFS được diễn giải thay vì hiển thị.

so sánh:

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

Điều này làm cho nó khá khó để sử dụng đầu ra đó để xử lý thêm, vì nó đơn độc "trong một dòng. Có lẽ một số IFS-fu có thể được thực hiện để ngăn chặn điều này.


Một cách giải quyết khác, sử dụng compgen:

$ compgen -v

Nội dung bash compgencó nghĩa là được sử dụng trong các kịch bản hoàn thành. Để kết thúc này, compgen -vliệt kê tất cả các biến được xác định. Nhược điểm: nó chỉ liệt kê các tên biến, không phải các giá trị.

Đây là một hack để liệt kê các giá trị.

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

Ưu điểm: nó là một giải pháp bash tinh khiết. Nhược điểm: một số giá trị bị rối vì giải thích thông qua printf. Ngoài ra, lớp con từ đường ống và / hoặc vòng lặp thêm một số biến phụ.


declare ...| cutống của bạn có bị vỡ nếu không có thuộc tính cho một biến không? Tôi đoán nó --được sử dụng trong trường hợp đó, nhưng tôi vẫn không yên tâm. sedcó thể an toàn hơn, tức làdeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd

+1 cho compgen:)
RSFalcon7

13

Nội dung typesetlạ có một tùy chọn chỉ hiển thị các hàm ( -f) nhưng không chỉ hiển thị các tham số. May mắn thay, typeset(hoặc set) hiển thị tất cả các tham số trước khi tất cả các hàm, tên hàm và tham số không thể chứa dòng mới hoặc dấu bằng và dòng mới trong giá trị tham số được trích dẫn (chúng xuất hiện dưới dạng \n). Vì vậy, bạn có thể dừng ở dòng đầu tiên không chứa dấu bằng:

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

Điều này chỉ in tên tham số; nếu bạn muốn các giá trị, thay đổi print $1thành print $0.

Lưu ý rằng điều này in tất cả các tên tham số (tham số và biến được sử dụng đồng nghĩa ở đây), không chỉ các biến môi trường (biến môi trường của nhà mạng là đồng nghĩa với các tham số được xuất khẩu.

Cũng lưu ý rằng điều này giả định rằng không có biến môi trường với tên không khớp với các ràng buộc của bash đối với tên tham số. Các biến như vậy không thể được tạo bên trong bash nhưng có thể được kế thừa từ môi trường khi bash bắt đầu:

env 'foo ()
{
  =oops' bash

Giải pháp tuyệt vời! Tôi thậm chí đã không nghĩ đến việc chỉ dừng lại ở dòng đầu tiên không có '='. +1
Steven D

Tương tự, với sed: set | sed '/=/!Q'
Toby Speight

7

Tôi không chắc chắn làm thế nào người ta có thể tạo setra các biến chỉ in. Tuy nhiên, bằng cách nhìn vào đầu ra của set, tôi đã có thể đưa ra những điều sau đây dường như chỉ lấy các biến:

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

Về cơ bản, tôi đang tìm kiếm các dòng bắt đầu bằng chữ cái, số hoặc dấu chấm câu theo sau là "=". Từ đầu ra mà tôi thấy, cái này lấy tất cả các biến; tuy nhiên, tôi nghi ngờ rằng điều này là rất di động.

Nếu, như tiêu đề của bạn gợi ý, bạn muốn trừ khỏi danh sách này các biến được xuất và do đó có được danh sách các biến không xuất, thì bạn có thể làm một cái gì đó như thế này

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

Để phá vỡ điều này một chút, đây là những gì nó làm:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Điều này hy vọng có được tất cả các biến (như đã thảo luận trước đó), sắp xếp chúng và đưa kết quả vào một tệp.
  2. && env | sort: Sau khi lệnh trước hoàn thành, chúng ta sẽ gọi envvà sắp xếp đầu ra của nó.
  3. | comm -23 ./setvars -: Cuối cùng, chúng tôi dẫn đầu ra được sắp xếp envvào commvà sử dụng -23tùy chọn để in các dòng duy nhất cho đối số đầu tiên, trong trường hợp này là các dòng duy nhất cho đầu ra của chúng tôi từ bộ.

Khi bạn hoàn tất, bạn có thể muốn dọn sạch tệp tạm thời mà nó đã tạo bằng lệnh rm ./setvars


Vấn đề với lệnh của bạn không phải là tính di động (tất nhiên là cụ thể đối với bash, nhưng không có vấn đề gì về phía grep). Vấn đề là bạn đang lấy một số dòng trong định nghĩa hàm. Bash thụt lề hầu hết các mã chức năng, nhưng không phải tất cả, đặc biệt không phải là văn bản bên trong các tài liệu ở đây ( f () {|cat <<EOF|foo=bar|EOF|}nơi |thể hiện một ngắt dòng).
Gilles 'SO- ngừng trở nên xấu xa'

6

Chỉ cần sử dụng lệnh env. Nó không in các chức năng.


1
Nó không nếu được sử dụng trong một tập lệnh.
DocSalvager

2

Hãy thử lệnh printenv:

$printenv

6
printenv và env chỉ in các biến xuất (môi trường) và không xuất biến (shell).
Lri

@Lri Cảm ơn! Tôi tự hỏi làm thế nào để in ra chỉ các biến xuất khẩu.
Mark Stewart

1

Trong bash, điều này sẽ chỉ in tên biến:

compgen -v

Hoặc, nếu cũng cần các giá trị, sử dụng:

declare -p

Cảm ơn bạn đã nỗ lực của bạn. xin lưu ý rằng tôi đã đề cập đến khả năng đó trong câu trả lời của tôi unix.stackexchange.com/a/5691/1170 có upvote nào.
lesmana

0

khác với vỏ sạch để đảm bảo các vars từ các tập lệnh RC bị bỏ lại

## 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/^> ?|^\+ ?//'
}

phiên bản với commsẽ quá phức tạp


0

Câu trả lời được chấp nhận là rất tốt. Tôi đang cung cấp một giải pháp thay thế không liên quan đến việc cài đặt chế độ POSIX:

Đây là kỹ thuật tôi đã sử dụng để lưu trữ trạng thái chức năng vào một tệp để tôi có thể tiếp tục sau. Cái đầu tiên tạo ra một bãi chứa trạng thái và cái thứ hai chỉ tạo ra một danh sách các tên biến.

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 

Cả hai hoạt động bằng cách kết xuất các dòng cho đến khi nó tìm thấy một dòng không có '=' char, xảy ra khi chức năng đầu tiên được liệt kê. Cây trồng sau được giao nhiệm vụ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.