ฉันจะยกเลิกการส่งออกตัวแปรโดยไม่สูญเสียมูลค่าได้อย่างไร


10

สมมติว่าฉันส่งออกตัวแปร:

foo=bar
export foo

ตอนนี้ฉันต้องการยกเลิกการส่งออก นั่นคือจะบอกว่าถ้าผมทำผมก็ไม่ควรจะได้รับsh -c 'echo "$foo"' ไม่ควรแสดงในสภาพแวดล้อมของเลย เป็นเพียงตัวอย่างวิธีง่ายๆในการแสดงการมีอยู่ของตัวแปร คำสั่งอาจเป็นอะไรก็ได้ - มันอาจเป็นสิ่งที่พฤติกรรมของมันได้รับผลกระทบเพียงแค่การปรากฏตัวของตัวแปรในสภาพแวดล้อมของมันbarfoosh -csh -c

ฉันสามารถ:

  1. unset ตัวแปรและสูญเสียมัน
  2. ลบออกโดยใช้envสำหรับแต่ละคำสั่ง:env -u foo sh -c 'echo "$foo"'
    • ทำไม่ได้ถ้าคุณต้องการใช้เชลล์ปัจจุบันต่อไปอีกซักพัก

เป็นการดีที่ฉันต้องการเก็บค่าของตัวแปร แต่ไม่ให้ปรากฏขึ้นในกระบวนการลูกไม่ได้เป็นตัวแปรว่างเปล่า

ฉันเดาว่าฉันสามารถทำได้:

otherfoo="$foo"; unset foo; foo="$otherfoo"; unset otherfoo

ความเสี่ยงนี้เกิดขึ้นotherfooหากมีอยู่แล้ว

นั่นเป็นวิธีเดียวหรือไม่ มีวิธีมาตรฐานหรือไม่?


1
คุณสามารถสะท้อนค่าลงในแฟ้มชั่วคราวโดยใช้mktempถ้าว่าเป็นแบบพกพาพอและล้างค่าค่าและแหล่งที่มาของไฟล์ชั่วคราวเพื่อกำหนดตัวแปร อย่างน้อยไฟล์ชั่วคราวสามารถสร้างขึ้นโดยใช้ชื่อที่กำหนดเองได้มากกว่าหรือน้อยกว่าตรงกันข้ามกับตัวแปรเชลล์
Thomas Dickey

@Sukminder sh -cคำสั่งเป็นเพียงตัวอย่าง ใช้คำสั่งใด ๆ ภายในซึ่งคุณไม่สามารถยกเลิกการตั้งค่าตัวแปรแทนได้ถ้าคุณต้องการ
muru

คำตอบ:


6

ไม่มีวิธีมาตรฐาน

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

unexport () {
  while [ "$#" -ne 0 ]; do
    eval "set -- \"\${$1}\" \"\${$1+set}\" \"\$@\""
    if [ -n "$2" ]; then
      unset "$3"
      eval "$3=\$1"
    fi
    shift; shift; shift
  done
}
unexport foo bar

ใน ksh, bash และ zsh คุณสามารถยกเลิกการส่งออกตัวแปรtypeset +x fooได้ สิ่งนี้จะรักษาคุณสมบัติพิเศษเช่นประเภทดังนั้นจึงเป็นที่ต้องการใช้ ผมคิดว่าเปลือกหอยทั้งหมดที่มีในตัวมีtypesettypeset +x

case $(LC_ALL=C type typeset 2>&1) in
  typeset\ *\ builtin) unexport () { typeset +x -- "$@"; };;
  *) unexport () {  };; # code above
esac

1
สำหรับผู้ที่ไม่คุ้นเคย${var+foo}จะประเมินfooว่าvarมีการตั้งค่าหรือไม่แม้ว่าจะว่างเปล่าและไม่มีอะไรเป็นอย่างอื่น
muru

บอกเด็ก ๆ ว่าคุณมีความคิดเห็นเกี่ยวtypeset +xกับ vs export -nสำหรับกระสุนที่สนับสนุนอดีตหรือไม่ เป็นexport -nrarer หรือมันไม่รักษาคุณสมบัติบางอย่าง?
Muru

@muru หากคุณกำลังเขียนสคริปต์ทุบตีคุณสามารถใช้export -nหรือtypeset +xไม่แยแส ใน ksh หรือ zsh typeset +xมีเพียง
Gilles 'หยุดความชั่วร้าย'

7

แก้ไข:สำหรับbashเท่านั้นตามที่ระบุไว้ในความคิดเห็น:

-nตัวเลือกที่จะexportเอาexportทรัพย์สินจากชื่อได้รับในแต่ละ (ดูhelp export.)

ดังนั้นสำหรับbash,คำสั่งที่คุณต้องการคือ:export -n foo


1
นั่นคือเชลล์เฉพาะ (ดูPOSIX ), OP ไม่ได้ระบุเชลล์ แต่ขอวิธีมาตรฐานในการแก้ปัญหา
Thomas Dickey

1
@ThomasDickey ไม่รู้เรื่องนั้น ขอขอบคุณอัปเดต
สัญลักษณ์แทน

3

ฉันเขียนฟังก์ชัน POSIX ที่คล้ายกัน แต่ไม่เสี่ยงกับการใช้รหัสโดยอำเภอใจ:

unexport()
    while case ${1##[0-9]*} in                   ### rule out leading numerics
          (*[!_[:alnum:]]*|"")                   ### filter out bad|empty names
          set "" ${1+"bad name: '$1'"}           ### prep bad name error
          return ${2+${1:?"$2"}}                 ### fail w/ above err or return 
          esac
    do    eval  set '"$'"{$1+$1}"'" "$'"$1"'" "$'@\" ###  $1 = (  $1+ ? $1 : "" )
          eval  "${1:+unset $1;$1=\$2;} shift 3"     ### $$1 = ( $1:+ ? $2 : -- )
    done

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

ฉันคิดถึงวิธีอื่น ฉันชอบมันมากขึ้น

unexport()
        while   unset OPTARG; OPTIND=1           ### always work w/ $1
                case  ${1##[0-9]*}    in         ### same old same old
                (*[!_[:alnum:]]*|"")             ### goodname && $# > 0 || break
                    ${1+"getopts"} : "$1"        ### $# ? getopts : ":"
                    return                       ### getopts errored or ":" didnt
                esac
        do      eval   getopts :s: '"$1" -"${'"$1+s}-\$$1\""
                eval   unset  "$1;  ${OPTARG+$1=\${OPTARG}#-}"
                shift
        done

ทั้งสองวิธีนี้ใช้เทคนิคเดียวกันมากมาย โดยทั่วไปถ้า shell var ไม่ได้มีการอ้างอิงไว้จะไม่ขยายด้วยการ+ขยายพารามิเตอร์ แต่ถ้ามันถูกตั้งค่า - ไม่ว่าจะเป็นค่าใด - การขยายพารามิเตอร์เช่น: ${parameter+word}จะขยายไปที่word- และไม่ใช่กับค่าของตัวแปร ดังนั้นตัวแปรเชลล์ทดสอบตัวเองและใช้แทนตัวเองในความสำเร็จ

พวกเขายังสามารถล้มเหลวได้ด้วยตนเอง ในฟังก์ชั่นด้านบนถ้าพบชื่อที่ไม่ดีฉันย้าย$1เข้า$2และออกจากศูนย์$1เพราะสิ่งต่อไปที่ฉันทำคือreturnความสำเร็จถ้า args ทั้งหมดได้รับการประมวลผลและวนรอบที่สิ้นสุดหรือถ้า ARG ไม่ถูกต้องเปลือกจะ ขยาย$2เข้าไปใน$1:?ซึ่งจะฆ่าเปลือกสคริปต์และกลับไปขัดจังหวะหนึ่งในการโต้ตอบในขณะที่เขียนwordไปยัง stderr

ในคนที่สองgetoptsทำการมอบหมาย และมันจะไม่กำหนดชื่อที่ไม่ดี - แทนที่จะเขียนมันจะเขียนข้อความแสดงข้อผิดพลาดมาตรฐานให้ stderr ยิ่งไปกว่านั้นจะบันทึกค่าของ arg ใน$OPTARG กรณีที่อาร์กิวเมนต์เป็นชื่อของตัวแปร set ในตำแหน่งแรก ดังนั้นหลังจากทำgetoptsทุกอย่างที่จำเป็นคือevalการOPTARGขยายตัวของชุดลงในการมอบหมายที่เหมาะสม


2
สักวันหนึ่งฉันจะอยู่ในแผนกจิตเวชที่ไหนสักแห่งหลังจากพยายามคลุมหัวคำตอบของคุณ : D คำตอบอื่น ๆ ประสบกับการใช้รหัสโดยอำเภอใจอย่างไร คุณสามารถยกตัวอย่างได้หรือไม่?
muru

3
@muru - ไม่ใช่ถ้าอาร์กิวเมนต์เป็นชื่อที่ไม่ถูกต้อง แต่นั่นไม่ใช่ปัญหา - ปัญหาคืออินพุตไม่ได้รับการตรวจสอบ ใช่มันเป็นสิ่งจำเป็นที่คุณจะต้องผ่านการโต้แย้งด้วยชื่อแปลก ๆ เพื่อให้มันรันโค้ดโดยพลการ - แต่นั่นก็เป็นพื้นฐานสำหรับ CVE ในประวัติศาสตร์ หากคุณพยายามexportใช้ชื่อแปลก ๆ มันจะไม่ฆ่าคอมพิวเตอร์ของคุณ
mikeserv

1
@muru - โอ้และข้อโต้แย้งสามารถกำหนดเองได้: var=something; varname=var; export "$varname"ถูกต้องสมบูรณ์ สิ่งเดียวกันก็เกิดunsetขึ้นและด้วยสิ่งนี้และสิ่งอื่น ๆ แต่นาทีที่เนื้อหาของ"$varname"ตัวแปรนั้นบ้าไปแล้วมันน่าเสียดาย และนั่นก็เป็นวิธีที่bashการพังทลายของการส่งออกฟังก์ชั่นนั้นเกิดขึ้นแล้ว
mikeserv

1
@mikeserv ฉันคิดว่าคุณจะได้รับ upvotes มากขึ้น (อย่างน้อยจากฉัน) ถ้าคุณแทนที่รหัส obfuscated ด้วยรหัสที่อธิบายตัวเอง (หรือแสดงความคิดเห็นอย่างน้อยบรรทัด)
PSkocik

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