วิธีพิมพ์ตัวแปรที่กำหนดไว้เฉพาะ (เชลล์และ / หรือตัวแปรสภาพแวดล้อม) ใน bash


57

คำสั่ง bash builtin setหากเรียกใช้โดยไม่มีอาร์กิวเมนต์จะพิมพ์ตัวแปรเชลล์และสภาวะแวดล้อมทั้งหมด แต่รวมถึงฟังก์ชันที่กำหนดไว้ทั้งหมด grepนี้จะทำให้การส่งออกใช้ไม่ได้สำหรับมนุษย์และยากที่จะ

ฉันจะทำให้คำสั่ง bash builtin setพิมพ์เฉพาะตัวแปรและไม่ใช่ฟังก์ชันได้อย่างไร

มีคำสั่งอื่นที่พิมพ์เฉพาะตัวแปรเชลล์โดยไม่มีฟังก์ชั่นหรือไม่?

หมายเหตุ: ทุบตีแตกต่างระหว่างตัวแปรเชลล์และตัวแปรสภาพแวดล้อม ดูที่นี่ความแตกต่างระหว่างตัวแปรสภาพแวดล้อมและตัวแปรสภาพแวดล้อมที่ส่งออกใน bash

คำตอบ:


51

"มีคำสั่งอื่นที่พิมพ์เฉพาะตัวแปรเชลล์โดยไม่มีฟังก์ชั่นหรือไม่"

ใน man bash, ในส่วนของคำสั่งของเชลล์สร้าง (ในส่วน set) มันบอกว่า: "ในโหมด posix, จะแสดงเฉพาะตัวแปรเชลล์เท่านั้น"

(set -o posix; set)

หมายเหตุ: ()ไวยากรณ์สร้างเชลล์ย่อยหากคุณไม่ต้องการการฟอร์กเพียงแค่ใช้เวอร์ชั่นที่ละเอียดมากขึ้น

set -o posix; set; set +o posix

1
โดยวิธีแก้ปัญหาที่ดีที่สุด
Ruslan

1
มีพวงของตัวแปรมักจะน่าทึ่งเริ่มต้นด้วยการเป็น_: ซึ่งจะง่ายต่อการกำจัด(set -o posix ; set | grep -v ^_)
hyde

นี่ทำให้ฉันรำคาญมานานแล้ว! การทิ้งบรรทัดที่เริ่มต้นด้วย _ นั้นไม่เพียงพอหากคุณมีค่าหลายบรรทัด แต่จะยังคงทิ้งเนื้อหาของค่าไว้ในแทคติก เนื่องจากชุดจะพิมพ์บรรทัดตามลำดับ _ _ ทั้งหมดจะอยู่ที่ท้ายดังนั้นคุณสามารถตัดผลลัพธ์หลังบรรทัดแรก _ โดยใช้:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott

1
เพียงบันทึก: (set -o posix; set)วงเล็บมีความสำคัญใน ถ้าไม่มีวงเล็บพฤติกรรมของเชลล์ Bash ปัจจุบันจะถูกเปลี่ยนเพื่อให้ตรงกับมาตรฐาน Posix (Dunno หมายถึงอะไร แต่มีความสำคัญ) ด้วยเครื่องหมายวงเล็บ Bash จะไม่เปลี่ยนแปลง
John Red

1
ผลข้างเคียง : ตั้งค่าPOSIXLY_CORRECT=yและเพิ่มposixใน$SHELLOPTS
Tom Hale

31

นี่คือวิธีแก้ปัญหาบางอย่าง:

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

ชำรุด:

  1. declare พิมพ์ตัวแปรที่กำหนดไว้ทั้งหมด (ส่งออกหรือไม่) และฟังก์ชั่น
  2. declare -f พิมพ์ฟังก์ชั่นเท่านั้น
  3. comm -3จะลบบรรทัดทั้งหมดที่เหมือนกันทั้งสอง ในผลนี้จะลบฟังก์ชั่นเหลือเพียงตัวแปร

หากต้องการพิมพ์เฉพาะตัวแปรที่ไม่ได้ถูกส่งออก:

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

วิธีแก้ปัญหาอื่น:

$ declare -p

จะพิมพ์ตัวแปรเท่านั้น แต่มีแอตทริบิวต์ที่น่าเกลียดบางอย่าง

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

คุณสามารถตัดแอททริบิวออกได้โดยใช้ ... cut:

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

ข้อเสียอย่างหนึ่งคือการที่ค่าของ IFS ถูกตีความแทนการแสดง

เปรียบเทียบ:

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

สิ่งนี้ทำให้ยากที่จะใช้เอาต์พุตนั้นสำหรับการประมวลผลเพิ่มเติมเนื่องจากโลน"ในหนึ่งบรรทัด บางที IFS-fu บางตัวสามารถทำได้เพื่อป้องกันสิ่งนี้


วิธีแก้ปัญหาอื่นโดยใช้compgen:

$ compgen -v

bash builtin compgenนั้นมีวัตถุประสงค์เพื่อใช้ในสคริปต์ที่เสร็จสมบูรณ์ ด้วยเหตุนี้compgen -vรายการตัวแปรที่กำหนดไว้ทั้งหมด ข้อเสีย: มันแสดงเฉพาะชื่อตัวแปรไม่ใช่ค่า

นี่คือแฮ็คที่จะแสดงรายการค่าต่างๆ

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

ข้อดี: มันเป็นโซลูชั่นทุบตีบริสุทธิ์ ข้อเสีย: ค่าบางอย่างจะ messed printfขึ้นเนื่องจากการตีความผ่าน นอกจากนี้เชลล์ย่อยจากไพพ์และ / หรือลูปจะเพิ่มตัวแปรพิเศษบางอย่าง


declare ...| cutไพพ์ของคุณแตกหรือไม่หากไม่มีแอตทริบิวต์สำหรับตัวแปร? ฉันเดาว่า--จะใช้ในกรณีนั้น แต่ฉันก็ยังไม่สบายใจ sedอาจปลอดภัยกว่าเช่นdeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd

+1 สำหรับcompgen:)
RSFalcon7

13

typesetbuiltin แปลกมีตัวเลือกที่จะแสดงฟังก์ชั่นเท่านั้น (เป็น-f) แต่จะไม่แสดงพารามิเตอร์เท่านั้น โชคดีที่typeset(หรือset) แสดงพารามิเตอร์ทั้งหมดก่อนฟังก์ชั่นฟังก์ชั่นและชื่อพารามิเตอร์ทั้งหมดไม่สามารถมีการขึ้นบรรทัดใหม่หรือเครื่องหมายเท่ากับและขึ้นบรรทัดใหม่ในค่าพารามิเตอร์ (จะปรากฏเป็น\n) ดังนั้นคุณสามารถหยุดที่บรรทัดแรกที่ไม่มีเครื่องหมายเท่ากับ:

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

จะพิมพ์ชื่อพารามิเตอร์เท่านั้น ถ้าคุณต้องการค่าที่เปลี่ยนไปprint $1print $0

โปรดทราบว่าสิ่งนี้จะพิมพ์ชื่อพารามิเตอร์ทั้งหมด (พารามิเตอร์และตัวแปรที่ใช้เหมือนกันที่นี่) ไม่ใช่แค่ตัวแปรสภาพแวดล้อม (“ ตัวแปรสภาพแวดล้อม” นั้นมีความหมายเหมือนกันกับ“ พารามิเตอร์ที่เอ็กซ์พอร์ต”)

โปรดทราบว่าสิ่งนี้ถือว่าไม่มีตัวแปรสภาพแวดล้อมที่มีชื่อที่ไม่ตรงกับข้อ จำกัด ของ bash กับชื่อพารามิเตอร์ ไม่สามารถสร้างตัวแปรดังกล่าวได้ใน bash แต่สามารถสืบทอดมาจากสภาพแวดล้อมได้เมื่อ bash เริ่มทำงาน:

env 'foo ()
{
  =oops' bash

สุดยอดทางออก! ฉันไม่คิดแม้แต่จะหยุดที่บรรทัดแรกที่ไม่มี '=' +1
Steven D

เท่ากันกับ sed: set | sed '/=/!Q'
Toby Speight

7

ฉันไม่แน่ใจว่าจะสร้างsetตัวแปรได้อย่างไร อย่างไรก็ตามเมื่อดูที่ผลลัพธ์ของsetฉันสามารถสร้างสิ่งต่อไปนี้ที่ดูเหมือนว่าจะคว้าตัวแปรเพียงอย่างเดียว:

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

โดยทั่วไปฉันกำลังมองหาบรรทัดที่ขึ้นต้นด้วยตัวอักษรตัวเลขหรือเครื่องหมายวรรคตอนตามด้วย "=" จากผลลัพธ์ที่ฉันเห็นนี่จะคว้าตัวแปรทั้งหมด อย่างไรก็ตามฉันสงสัยว่านี่เป็นแบบพกพามาก

หากตามที่ชื่อของคุณแนะนำคุณต้องการลบตัวแปรที่ส่งออกแล้วจากรายการนี้และรับรายการตัวแปรที่ไม่ได้ถูกส่งออกจากนั้นคุณสามารถทำสิ่งนี้ได้

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

หากต้องการแยกสิ่งนี้ออกเล็กน้อยนี่คือสิ่งที่ทำ:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : นี่หวังว่าจะได้รับตัวแปรทั้งหมด (ตามที่กล่าวไว้ก่อนหน้า), เรียงลำดับและติดผลลัพธ์ในไฟล์
  2. && env | sort: หลังจากคำสั่งก่อนหน้านี้เสร็จสมบูรณ์แล้วเราจะเรียกenvและเรียงลำดับผลลัพธ์
  3. | comm -23 ./setvars -: ในที่สุดเราท่อส่งออกเรียงenvลงไปcommและใช้-23ตัวเลือกในการพิมพ์เส้นที่เป็นเอกลักษณ์ของอาร์กิวเมนต์แรกในกรณีนี้เส้นที่ไม่ซ้ำกันเพื่อการส่งออกของเราจากชุด

เมื่อเสร็จแล้วคุณอาจต้องการล้างไฟล์ temp ที่สร้างขึ้นด้วยคำสั่ง rm ./setvars


ปัญหาเกี่ยวกับคำสั่งของคุณไม่ใช่การพกพา (แน่นอนสำหรับการทุบตีแน่นอน แต่ไม่มีปัญหาในด้าน grep) ปัญหาคือคุณกำลังจับบางบรรทัดในนิยามฟังก์ชัน Bash เยื้องส่วนใหญ่ของรหัสฟังก์ชั่น แต่ไม่ได้ทั้งหมดโดยเฉพาะอย่างยิ่งไม่ใช่ข้อความภายในเอกสารที่นี่ ( f () {|cat <<EOF|foo=bar|EOF|}ซึ่ง|หมายถึงการแบ่งบรรทัด)
Gilles 'หยุดชั่วร้าย'

6

envเพียงแค่ใช้คำสั่ง มันไม่ได้พิมพ์ฟังก์ชั่น


1
มันจะทำอย่างไรถ้าใช้ภายในสคริปต์
DocSalvager

2

ลองใช้คำสั่ง printenv:

$printenv

6
printenv และ env จะพิมพ์เฉพาะตัวแปรที่ส่งออก (สภาพแวดล้อม) และไม่ใช่ตัวแปรที่ไม่ได้ส่งออก (เชลล์)
Lri

@Lri ขอบคุณ! ฉันสงสัยว่าจะพิมพ์เฉพาะตัวแปรที่ส่งออกได้อย่างไร
Mark Stewart

1

ใน bash สิ่งนี้จะพิมพ์เฉพาะชื่อตัวแปร:

compgen -v

หรือหากจำเป็นต้องใช้ค่าให้ใช้:

declare -p

ขอบคุณสำหรับความพยายามของคุณ. โปรดทราบว่าฉันได้กล่าวไปแล้วว่าความเป็นไปได้ในคำตอบของฉันunix.stackexchange.com/a/5691/1170มี upvote อยู่แล้ว
lesmana

0

แตกต่างกับเชลล์สะอาดเพื่อให้แน่ใจว่า vars จากสคริปต์ rc จะถูกปล่อยออกไป

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

รุ่นที่มีcommจะซับซ้อนเกินไป


0

คำตอบที่ยอมรับได้ดีมาก ฉันขอเสนอทางเลือกที่ไม่เกี่ยวข้องกับการตั้งค่าโหมด POSIX:

นี่คือเทคนิคที่ฉันใช้ในการจัดเก็บสถานะการทำงานของไฟล์เพื่อให้ฉันสามารถดำเนินการต่อในภายหลัง ครั้งแรกที่สร้างการถ่ายโอนข้อมูลของรัฐและที่สองสร้างเพียงรายการของชื่อตัวแปร

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 

ทั้งสองทำงานโดยการทิ้งบรรทัดจนกว่าจะพบรายการที่ไม่มีอักขระ '=' ซึ่งเกิดขึ้นเมื่อมีการแสดงรายการฟังก์ชันแรก หลังครอบตัดการมอบหมายออก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.