วิธีอ้างอิงไฟล์สำหรับตัวแปรที่ใช้ Bash


152

ฉันต้องการเรียกไฟล์การตั้งค่าสำหรับตัวแปรฉันจะทำสิ่งนี้ใน bash ได้อย่างไร?

ดังนั้นไฟล์การตั้งค่าจะกำหนดตัวแปร (เช่น: CONFIG.FILE):

production="liveschool_joe"
playschool="playschool_joe"

และสคริปต์จะใช้ตัวแปรเหล่านั้นในนั้น

#!/bin/bash
production="/REFERENCE/TO/CONFIG.FILE"
playschool="/REFERENCE/TO/CONFIG.FILE"
sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool

ฉันจะทุบตีเพื่อทำสิ่งนั้นได้อย่างไร ฉันจะต้องใช้ awk / sed ฯลฯ หรือไม่?

คำตอบ:


242

คำตอบสั้น ๆ

ใช้sourceคำสั่ง


ตัวอย่างการใช้ source

ตัวอย่างเช่น:

config.sh

#!/usr/bin/env bash
production="liveschool_joe"
playschool="playschool_joe"
echo $playschool

script.sh

#!/usr/bin/env bash
source config.sh
echo $production

โปรดทราบว่าผลลัพธ์จากsh ./script.shในตัวอย่างนี้คือ:

~$ sh ./script.sh 
playschool_joe
liveschool_joe

นี่เป็นเพราะsourceคำสั่งรันโปรแกรมจริง ทุกอย่างในconfig.shจะถูกดำเนินการ


อีกวิธีหนึ่ง

คุณสามารถใช้exportคำสั่งในตัวและรับและตั้งค่า "ตัวแปรสภาพแวดล้อม" ได้เช่นกัน

วิ่งexportและecho $ENVควรเป็นสิ่งที่คุณต้องรู้เกี่ยวกับการเข้าถึงตัวแปร การเข้าถึงตัวแปรสภาพแวดล้อมทำได้เช่นเดียวกับตัวแปรโลคอล

หากต้องการตั้งค่าให้พูด:

export variable=value

ที่บรรทัดคำสั่ง สคริปต์ทั้งหมดจะสามารถเข้าถึงค่านี้


ต้อง config.sh มีสิทธิ์ดำเนินการเพื่อให้ทำงานได้หรือไม่
Ramiro

2
มีวิธีใช้sourceโดยการไพพ์ในเนื้อหาแทนที่จะให้ไฟล์หรือไม่? เช่นเดียวกับsome command | sourceไม่ทำงาน ...
เอลเลียตมีโอกาส

1
ไม่เป็นไรฉันพบวิธีแก้ปัญหาและโพสต์ให้คนอื่น
Elliot Chance

และในการกู้คืนอาร์เรย์คุณต้องเก็บค่าแต่ละรายการของอาร์เรย์ที่แยกจากกัน (ไม่ใช่แถวเดี่ยวเต็มแถวจากdeclare -p) จะเป็นเช่นsomeArray[3]="abc"นั้น ...
Aquarius Power

1
@Ramiro ไม่มันไม่ได้ ฉันตรวจสอบแล้ว :)
แมตต์ Komarnicki

22

ยิ่งสั้นลงโดยใช้จุด:

#!/bin/bash
. CONFIG_FILE

sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool

17
เมื่อใช้สิ่งนี้ในสคริปต์การจดชวเลขไม่จำเป็นและอาจทำให้สับสน ทำไมไม่ใช้sourceคำสั่งเต็มรูปแบบเพื่อทำให้ชัดเจน?
Lyle

2
@Lyle เพราะคุณต้องการให้สคริปต์ของคุณไม่เบี่ยงเบนไปจากไวยากรณ์ POSIX แบบพกพาเมื่อคุณไม่จำเป็นต้อง?
tripleee

14

ใช้sourceคำสั่งเพื่อนำเข้าสคริปต์อื่น ๆ :

#!/bin/bash
source /REFERENCE/TO/CONFIG.FILE
sudo -u wwwrun svn up /srv/www/htdocs/$production
sudo -u wwwrun svn up /srv/www/htdocs/$playschool

12

ฉันมีปัญหาเดียวกันโดยเฉพาะในเรื่องความปลอดภัยและฉันพบวิธีแก้ปัญหาที่นี่ที่นี่

ปัญหาของฉันคือว่าฉันต้องการที่จะเขียนสคริปต์การใช้งานในทุบตีกับไฟล์ config ที่เนื้อหาบางเส้นทางเช่นนี้

################### Config File Variable for deployment script ##############################

VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0"
VAR_CONFIG_FILE_DIR="/home/erman/config-files"
VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh"

โซลูชันที่มีอยู่ประกอบด้วยการใช้คำสั่ง "SOURCE" และนำเข้าไฟล์ config ด้วยตัวแปรเหล่านี้ 'SOURCE path / to / file' แต่วิธีนี้มีปัญหาด้านความปลอดภัยเนื่องจากไฟล์ที่มาสามารถมีสิ่งที่สคริปต์ Bash สามารถทำได้ ที่สร้างปัญหาด้านความปลอดภัย คน malicicios สามารถ "รัน" โค้ดตามอำเภอใจเมื่อสคริปต์ของคุณกำลังหาไฟล์ปรับแต่ง

ลองนึกภาพบางสิ่งเช่นนี้:

 ################### Config File Variable for deployment script ##############################

    VAR_GLASSFISH_DIR="/home/erman/glassfish-4.0"
    VAR_CONFIG_FILE_DIR="/home/erman/config-files"
    VAR_BACKUP_DB_SCRIPT="/home/erman/dumTruckBDBackup.sh"; rm -fr ~/*

    # hey look, weird code follows...
    echo "I am the skull virus..."
    echo rm -fr ~/*

เพื่อแก้ปัญหานี้เราอาจต้องการอนุญาตให้สร้างเฉพาะในรูปแบบNAME=VALUEในไฟล์นั้น (ไวยากรณ์การกำหนดตัวแปร) และอาจแสดงความคิดเห็น (แม้ว่าในทางเทคนิคแล้วความคิดเห็นจะไม่สำคัญ) ดังนั้นเราสามารถตรวจสอบไฟล์ config โดยใช้คำสั่งเทียบเท่าของegrepgrep -E

นี่คือวิธีที่ฉันได้แก้ปัญหา

configfile='deployment.cfg'
if [ -f ${configfile} ]; then
    echo "Reading user config...." >&2

    # check if the file contains something we don't want
    CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(\`]*$)"
    if egrep -q -iv "$CONFIG_SYNTAX" "$configfile"; then
      echo "Config file is unclean, Please  cleaning it..." >&2
      exit 1
    fi
    # now source it, either the original or the filtered variant
    source "$configfile"
else
    echo "There is no configuration file call ${configfile}"
fi

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

6
ที่ไม่ปลอดภัยเพียงพอ, CMD="$(rm -fr ~/*)"คุณยังสามารถทำ
svlasov

ขอบคุณ @svlasov ผมคิดว่ามันเป็นปัญหาที่ร้ายแรงฉันจะแก้ไขคำตอบของฉันที่จะหลีกเลี่ยงประเภทของปัญหาว่าด้วยการปฏิเสธตัวอักษรที่ใช้สำหรับ subtitution คำสั่งที่ชอบ(และ `` `สุดท้าย CONFIG_SYNTAX="(^\s*#|^\s*$|^\s*[a-z_][^[:space:]]*=[^;&\(`]*$)"
Erman

ยังไม่เพียงพอ foo=bar unleash_virusสามารถดำเนินการได้ หมายเหตุfoo=bar\ unleash_virus, foo="bar unleash_virus"และfoo=bar #unleash_virusมีความปลอดภัย ไม่ใช่เรื่องง่ายที่จะทำความสะอาดอย่างถูกต้องและไม่ปิดกั้นไวยากรณ์ที่ไม่เป็นอันตรายโดยเฉพาะเมื่อคุณนึกถึงการอ้างถึงและการหลบหนีที่เป็นไปได้ทั้งหมด
Kamil Maciorowski


3

การแปลงไฟล์พารามิเตอร์เป็นตัวแปรสภาพแวดล้อม

ฉันมักจะแยกวิเคราะห์แทนที่จะหาแหล่งที่มาเพื่อหลีกเลี่ยงความซับซ้อนของสิ่งประดิษฐ์บางอย่างในไฟล์ของฉัน มันยังเสนอวิธีให้ฉันจัดการกับคำพูดและสิ่งอื่น ๆ เป็นพิเศษ เป้าหมายหลักของฉันคือการรักษาทุกสิ่งที่เกิดขึ้นหลังจาก '=' ตามตัวอักษรแม้กระทั่งเครื่องหมายคำพูดและช่องว่าง

#!/bin/bash

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "value content" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

function readpars() {
  while read -r line ; do
    key=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\1/')
    val=$(echo "${line}" | sed -e 's/^\([^=]*\)=\(.*\)$/\2/' -e 's/"/\\"/g')
    eval "${key}=\"${val}\""
  done << EOF
var1="value content"
var2=value content
EOF
}

# Option 1: Will Pass
echo "eval \"cntpars \$var1\""
eval "cntpars $var1"

# Option 2: Will Fail
echo "cntpars \$var1"
cntpars $var1

# Option 3: Will Fail
echo "cntpars \"\$var1\""
cntpars "$var1"

# Option 4: Will Pass
echo "cntpars \"\$var2\""
cntpars "$var2"

สังเกตเคล็ดลับเล็กน้อยที่ฉันต้องทำเพื่อพิจารณาข้อความที่ยกมาของฉันเป็นพารามิเตอร์เดียวที่มีพื้นที่ในการcntparsทำงานของฉัน จำเป็นต้องมีการประเมินในระดับพิเศษหนึ่งระดับ หากฉันไม่ทำเช่นนี้ในตัวเลือกที่ 2 ฉันจะผ่านพารามิเตอร์ 2 ดังนี้:

  • "value
  • content"

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

ตัวเลือกอื่น ๆ อาจเป็นเพียงการไม่ใส่ตัวแปรในเครื่องหมายคำพูดคู่เช่นเดียวกับในตัวเลือก 4 แล้วเพียงเพื่อให้แน่ใจว่าคุณพูดพวกเขาเมื่อจำเป็น

สิ่งที่ต้องจำไว้

ค้นหาตามเวลาจริง

อีกสิ่งที่ฉันชอบทำคือทำการค้นหาตามเวลาจริงหลีกเลี่ยงการใช้ตัวแปรสภาพแวดล้อม:

lookup() {
if [[ -z "$1" ]] ; then
  echo ""
else
  ${AWK} -v "id=$1" 'BEGIN { FS = "=" } $1 == id { print $2 ; exit }' $2
fi
}

MY_LOCAL_VAR=$(lookup CONFIG_VAR filename.cfg)
echo "${MY_LOCAL_VAR}"

ไม่ใช่ไฟล์ที่มีประสิทธิภาพที่สุด แต่ไฟล์ขนาดเล็กนั้นทำงานได้อย่างหมดจด


2

sourceหากตัวแปรที่จะถูกสร้างขึ้นและไม่ได้บันทึกเป็นไฟล์คุณไม่สามารถท่อพวกเขาในการเข้าสู่ วิธีง่ายๆในการหลอกลวงคือ:

some command | xargs

-1

สคริปต์ที่มีตัวแปรสามารถดำเนินการนำเข้าได้โดยใช้ bash พิจารณา script-variable.sh

#!/bin/sh
scr-var=value

พิจารณาสคริปต์จริงที่จะใช้ตัวแปร:

 #!/bin/sh
 bash path/to/script-variable.sh
 echo "$scr-var"

สิ่งนี้ไม่ทำงานมันจะรันสคริปต์ Bash ในกระบวนการย่อยจากนั้นสูญเสียสภาพแวดล้อมใด ๆ (รวมถึงตัวแปร) ที่สร้างขึ้นในช่วงอายุการใช้งาน ยังsrc-varไม่ใช่ตัวระบุที่ถูกต้อง
tripleee

-1

เพื่อป้องกันความขัดแย้งของการตั้งชื่อให้นำเข้าตัวแปรที่คุณต้องการเท่านั้น:

variableInFile () {
    variable="${1}"
    file="${2}"

    echo $(
        source "${file}";
        eval echo \$\{${variable}\}
    )
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.