ในสคริปต์ของฉันใน bash มีตัวแปรมากมายและฉันต้องทำอะไรบางอย่างเพื่อบันทึกลงในไฟล์ คำถามของฉันคือวิธีแสดงรายการตัวแปรทั้งหมดที่ประกาศในสคริปต์ของฉันและรับรายการดังนี้:
VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi
ในสคริปต์ของฉันใน bash มีตัวแปรมากมายและฉันต้องทำอะไรบางอย่างเพื่อบันทึกลงในไฟล์ คำถามของฉันคือวิธีแสดงรายการตัวแปรทั้งหมดที่ประกาศในสคริปต์ของฉันและรับรายการดังนี้:
VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi
คำตอบ:
set
จะส่งออกตัวแปร แต่น่าเสียดายที่มันจะส่งออกฟังก์ชันที่กำหนดเช่นกัน
โชคดีที่โหมด POSIX แสดงเฉพาะตัวแปร:
( set -o posix ; set ) | less
ไปป์less
หรือเปลี่ยนเส้นทางไปยังตำแหน่งที่คุณต้องการตัวเลือก
ดังนั้นเพื่อให้ได้ตัวแปรที่ประกาศในสคริปต์:
( 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
(หรืออย่างน้อยก็ขึ้นอยู่กับสิ่งนั้น :-))
VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;
. นอกจากนี้ยังจะแสดงผล vars ในรูปแบบพร้อมที่จะบันทึก รายการจะรวมตัวแปรที่สคริปต์เปลี่ยนแปลง (ขึ้นอยู่กับว่าสิ่งนี้เป็นที่ต้องการหรือไม่)
source
คุณควรจะทำได้ด้วยผลลัพธ์ของdeclare -p
ไฟล์.
before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
compgen -v
มันแสดงรายการตัวแปรทั้งหมดรวมถึงตัวแปรท้องถิ่น ผมได้เรียนรู้ได้จากรายชื่อรับของตัวแปรที่มีชื่อตรงกับรูปแบบบางอย่างและใช้มันในสคริปต์ของฉัน
compgen -v
รายการตัวแปรส่วนกลางที่ไม่ได้ตั้งค่าในเครื่อง ไม่แน่ใจว่าเป็นจุดบกพร่องที่มีมานานหรือเป็นพฤติกรรมที่ต้องการ
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"
ต้องพิมพ์ชื่อตัวแปรเชลล์ทั้งหมด คุณสามารถรับรายการก่อนและหลังการจัดหาไฟล์ของคุณได้เช่นเดียวกับการ "set" เพื่อให้ตัวแปรใดใหม่ (ตามที่อธิบายไว้ในคำตอบอื่น ๆ ) แต่โปรดทราบว่าการกรองด้วย diff ดังกล่าวสามารถกรองตัวแปรบางตัวที่คุณต้องการ แต่มีอยู่ก่อนที่จะจัดหาไฟล์ของคุณ
ในกรณีของคุณหากคุณทราบว่าชื่อตัวแปรของคุณขึ้นต้นด้วย "VARIABLE" คุณสามารถซอร์สสคริปต์ของคุณและทำ:
for var in ${!VARIABLE@}; do
printf "%s%q\n" "$var=" "${!var}"
done
อัปเดต:สำหรับโซลูชัน BASH บริสุทธิ์ (ไม่ใช้คำสั่งภายนอก):
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
eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
จากคำตอบข้างต้นสิ่งนี้ใช้ได้ผลสำหรับฉัน:
before=$(set -o posix; set | sort);
ไฟล์ต้นฉบับ :
comm -13 <(printf %s "$before") <(set -o posix; set | sort | uniq)
หากคุณสามารถโพสต์โพรเซสได้ (ดังที่ได้กล่าวไปแล้ว) คุณอาจจะset
โทรที่จุดเริ่มต้นและจุดสิ้นสุดของสคริปต์ของคุณได้ โปรดทราบว่าสิ่งนี้จะยังคงมีเสียงรบกวนอยู่
คุณยังสามารถดำเนินการตามโปรแกรมได้อีกด้วย หากต้องการ จำกัด เอาต์พุตให้เหลือเพียงขอบเขตปัจจุบันของคุณคุณจะต้องใช้ wrapper ในการสร้างตัวแปร ตัวอย่างเช่น
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
ซึ่งให้ผลตอบแทน
VAR1=abc
VAR2=bcd
VAR3=cde
นี่คือสิ่งที่คล้ายกับคำตอบของ @GinkgoFr แต่ไม่มีปัญหาที่ระบุโดย @Tino หรือ @DejayClayton และมีประสิทธิภาพมากกว่าset -o posix
บิตที่ชาญฉลาดของ @ DouglasLeeder :
+ function SOLUTION() { (set +o posix; set) | sed -ne '/^\w\+=/!q; p;'; }
ความแตกต่างคือโซลูชันนี้จะหยุดหลังจากรายงานที่ไม่ใช่ตัวแปรแรกเช่นฟังก์ชันแรกที่รายงานโดย set
BTW: ปัญหา "Tino" ได้รับการแก้ไขแล้ว แม้ว่า POSIX จะปิดอยู่และมีการรายงานฟังก์ชันset
แต่sed ...
ส่วนของโซลูชันจะอนุญาตให้รายงานตัวแปรผ่าน (เช่นVAR=VALUE
บรรทัด) เท่านั้น โดยเฉพาะอย่างยิ่งA2
ไม่ได้ spuriously ทำให้มันกลายเป็นเอาท์พุท
+ function a() { echo $'\nA2=B'; }; A0=000; A9=999;
+ SOLUTION | grep '^A[0-9]='
A0=000
A9=999
และ: ปัญหา "DejayClayton" ได้รับการแก้ไขแล้ว (บรรทัดใหม่ที่ฝังอยู่ในค่าตัวแปรจะไม่รบกวนผลลัพธ์ - แต่ละรายการVAR=VALUE
จะได้รับบรรทัดผลลัพธ์เดียว)
+ A1=$'111\nA2=222'; A0=000; A9=999;
+ SOLUTION | grep '^A[0-9]='
A0=000
A1=$'111\nA2=222'
A9=999
หมายเหตุ: วิธีแก้ปัญหาโดย @DouglasLeeder ประสบปัญหา "DejayClayton" (ค่าที่มีเส้นขึ้นบรรทัดใหม่ฝังอยู่) ด้านล่างนี้A1
ผิดและA2
ไม่ควรแสดงเลย
$ A1=$'111\nA2=222'; A0=000; A9=999; (set -o posix; set) | grep '^A[0-9]='
A0=000
A1='111
A2=222'
A9=999
ในที่สุด: ฉันไม่คิดว่าเวอร์ชั่นมีความbash
สำคัญ แต่มันอาจจะ ฉันทำการทดสอบ / พัฒนาเกี่ยวกับสิ่งนี้:
$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)
POST-SCRIPT: จากคำตอบอื่น ๆ ที่มีต่อ OP ฉันมั่นใจว่า <100% set
จะแปลงบรรทัดใหม่ภายในค่า\n
เสมอซึ่งโซลูชันนี้อาศัยเพื่อหลีกเลี่ยงปัญหา "DejayClayton" บางทีนั่นอาจเป็นพฤติกรรมสมัยใหม่? หรือรูปแบบเวลาคอมไพล์? หรือการตั้งค่าset -o
หรือshopt
ตัวเลือก? หากคุณรู้จักรูปแบบดังกล่าวโปรดเพิ่มความคิดเห็น ...
ลองใช้สคริปต์ (เรียกว่า "ls_vars"):
#!/bin/bash
set -a
env > /tmp/a
source $1
env > /tmp/b
diff /tmp/{a,b} | sed -ne 's/^> //p'
chmod + x มันและ:
ls_vars your-script.sh > vars.files.save
จากมุมมองของการรักษาความปลอดภัยทั้ง @ akostadinov ของคำตอบหรือ @ JuvenXu ของคำตอบที่ดีกว่าที่จะอาศัยการส่งออกที่ไม่มีโครงสร้างของset
คำสั่งเนื่องจากข้อบกพร่องที่อาจเกิดขึ้นต่อไปนี้การรักษาความปลอดภัย:
#!/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
ฟังก์ชันข้างต้นdoLogic
ใช้set
ตรวจสอบการมีอยู่ของตัวแปรPS1
เพื่อตรวจสอบว่าสคริปต์เป็นแบบโต้ตอบหรือไม่ (ไม่ต้องกังวลว่านี่เป็นวิธีที่ดีที่สุดในการบรรลุเป้าหมายหรือไม่นี่เป็นเพียงตัวอย่างเท่านั้น)
อย่างไรก็ตามผลลัพธ์ของset
ไม่มีโครงสร้างซึ่งหมายความว่าตัวแปรใด ๆ ที่มีการขึ้นบรรทัดใหม่สามารถปนเปื้อนผลลัพธ์ได้ทั้งหมด
แน่นอนว่านี่เป็นความเสี่ยงด้านความปลอดภัยที่อาจเกิดขึ้น ให้ใช้การสนับสนุนของ Bash สำหรับการขยายชื่อตัวแปรทางอ้อมแทนหรือcompgen -v
.
ลองสิ่งนี้: set | egrep "^\w+="
(มีหรือไม่มีไฟล์| less
ท่อ)
โซลูชันแรกที่เสนอใช้( set -o posix ; set ) | less
งานได้ แต่มีข้อเสียคือส่งรหัสควบคุมไปยังเทอร์มินัลดังนั้นจึงแสดงไม่ถูกต้อง ตัวอย่างเช่นหากมี(น่าจะเป็น)IFS=$' \t\n'
ตัวแปรเราจะเห็น:
IFS='
'
…แทน.
egrep
โซลูชันของฉันแสดงสิ่งนี้(และในที่สุดก็คล้าย ๆ กัน)อย่างถูกต้อง
bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A
แสดงที่ไม่ถูกต้องA=B"
-> ล้มเหลว!
ฉันอาจจะขโมยคำตอบไปเมื่อก่อน ... แตกต่างกันเล็กน้อยในฐานะ func:
##
# 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"
}
printenv
สั่ง:printenv
พิมพ์environment variables
พร้อมกับค่าของพวกเขาทั้งหมด
โชคดี...
วิธีง่ายๆในการทำเช่นนี้คือใช้โหมด bash ที่เข้มงวดโดยตั้งค่าตัวแปรสภาพแวดล้อมระบบก่อนรันสคริปต์ของคุณและใช้ diff เพื่อเรียงลำดับสคริปต์ของคุณเท่านั้น:
# 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
ตอนนี้คุณสามารถดึงตัวแปรของสคริปต์ของคุณใน /tmp/script_vars.log หรืออย่างน้อยก็เป็นไปตามนั้น!
ไปงานปาร์ตี้สายเล็กน้อย แต่นี่คือคำแนะนำอื่น:
#!/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'
diff + sedบรรทัดคำสั่งท่อเอาท์พุทตัวแปรสคริปต์ที่กำหนดไว้ทั้งหมดในรูปแบบที่ต้องการ (ตามที่ระบุไว้ในโพสต์ของ OP):
VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
-o posix
ตอนนี้ความแตกต่างจะมีเฉพาะตัวแปรเท่านั้น