$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"
ในกรณีเช่นนี้ฉัน$myvar
จะอ้างอิงเพื่อหลีกเลี่ยงการแยกคำเนื่องจากช่องว่างสีขาวในค่าของได้myvar
อย่างไร
sudo bash -c
ฉันมีการขยายตัวแปร ชื่อพา ธ ซึ่งอาจมีช่องว่าง
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"
ในกรณีเช่นนี้ฉัน$myvar
จะอ้างอิงเพื่อหลีกเลี่ยงการแยกคำเนื่องจากช่องว่างสีขาวในค่าของได้myvar
อย่างไร
sudo bash -c
ฉันมีการขยายตัวแปร ชื่อพา ธ ซึ่งอาจมีช่องว่าง
คำตอบ:
ไม่มีการแยกคำ (ในคุณลักษณะที่แยกตัวแปรตามส่วนขยายที่$myvar
ไม่ได้กล่าวถึง) ในโค้ดนั้นเนื่องจากไม่ได้ถูกอ้างอิง
มี แต่ช่องโหว่ฉีดคำสั่งเป็นขยายตัวก่อนที่จะถูกส่งผ่านไปยัง$myvar
bash
ดังนั้นเนื้อหาจะถูกตีความเป็นรหัสทุบตี!
ช่องว่างในนั้นจะทำให้เกิดการขัดแย้งหลายประการที่จะถูกส่งผ่านไปcd
ไม่ใช่เพราะการแยกคำแต่เพราะพวกเขาจะได้รับการแยกเป็นโทเค็นหลาย ๆ ในไวยากรณ์เปลือก ด้วยค่าbye;reboot
ที่จะรีบูต! ¹
ที่นี่คุณต้องการ:
sudo bash -c 'cd -P -- "$1"' bash "$myvar"
(ที่คุณส่งเนื้อหาของ$myvar
เป็นอาร์กิวเมนต์แรกของสคริปต์แบบอินไลน์นั้นให้สังเกตว่าทั้งคู่$myvar
และ$1
ถูกอ้างถึงสำหรับเชลล์ที่เกี่ยวข้องเพื่อป้องกัน IFS-word-splitting (และ globbing) อย่างไร
หรือ:
sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
(ที่คุณส่งเนื้อหาของ$myvar
ในตัวแปรสภาพแวดล้อม)
แน่นอนคุณจะไม่บรรลุอะไรที่เป็นประโยชน์โดยการเรียกใช้เฉพาะ cd
ในสคริปต์แบบอินไลน์ที่ (นอกเหนือจากการตรวจสอบว่าroot
สามารถcd
เข้าไปที่นั่น) สมมุติว่าคุณต้องการให้สคริปต์cd
นั้นอยู่ที่นั่นแล้วทำอย่างอื่นที่นั่นเช่น:
sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"
หากความตั้งใจที่จะใช้sudo
เพื่อให้สามารถcd
เข้าไปในไดเรกทอรีที่คุณไม่สามารถเข้าถึงได้แสดงว่าไม่สามารถใช้งานได้จริง
sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
จะเริ่มต้นการโต้ตอบกับไดเรกทอรีปัจจุบันในbash
แต่เปลือกที่จะทำงานตามที่$myvar
root
คุณสามารถทำได้:
sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
ในการรับการโต้ตอบแบบไม่มีสิทธิพิเศษbash
กับไดเรกทอรีปัจจุบัน$myvar
แต่ถ้าคุณไม่มีสิทธิ์cd
ในไดเรกทอรีนั้นในตอนแรกคุณจะไม่สามารถทำสิ่งใดในไดเรกทอรีนั้นแม้ว่าจะเป็นไดเรกทอรีทำงานปัจจุบันของคุณ
$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied
ข้อยกเว้นจะเกิดขึ้นหากคุณมีสิทธิ์ในการค้นหาในไดเรกทอรี แต่จะไม่รวมอยู่ในองค์ประกอบไดเรกทอรีของเส้นทาง:
$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied
speaking การพูดอย่างเคร่งครัดสำหรับค่าของ$myvar
like $(seq 10)
(ตามตัวอักษร) จะมีการแยกคำแน่นอนเมื่อการขยายตัวของการทดแทนคำสั่งนั้นโดยbash
เชลล์เริ่มต้นเป็นroot
มีเวทย์มนต์ใหม่ (bash 4.4) ที่ให้คุณทำสิ่งที่คุณต้องการได้โดยตรง ขึ้นอยู่กับบริบทที่ใหญ่กว่าการใช้หนึ่งในเทคนิคอื่นอาจดีกว่า แต่มันจะทำงานในบริบทที่ จำกัด นี้
sudo bash -c "cd ${myvar@Q}; pwd"
หากคุณไม่สามารถเชื่อถือเนื้อหาของ$myvar
วิธีการที่เหมาะสมเพียงอย่างเดียวคือการใช้ GNU printf
เพื่อสร้างรุ่นที่หลบหนี:
#!/bin/bash
myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"
ในฐานะที่เป็นprintf
หน้าคนพูดว่า:
%q
ARGUMENT ถูกพิมพ์ในรูปแบบที่สามารถนำกลับมาใช้ใหม่เป็นอินพุตของเชลล์ได้โดยหลีกเลี่ยงอักขระที่ไม่สามารถพิมพ์ได้ด้วย
$''
ไวยากรณ์POSIX ที่เสนอ
printf
จะถูกเรียกใช้บนระบบ GNU เท่านั้นและหาก$(printf '%q' "$myvar")
เรียกใช้ในเชลล์ที่printf
ไม่ได้สร้างขึ้น หอยส่วนใหญ่มีอยู่printf
ในปัจจุบัน ไม่ได้ทั้งหมดสนับสนุนและแม้ว่าเมื่อพวกเขาทำพวกเขาจะพูดสิ่งที่อยู่ในรูปแบบการสนับสนุนจากเปลือกที่สอดคล้องกันซึ่งอาจไม่จำเป็นต้องเป็นเช่นเดียวกับที่เข้าใจ%q
bash
อันที่จริงbash
's printf
ล้มเหลวในการพูดสิ่งที่ถูกต้องสำหรับแม้แต่ตัวเองสำหรับค่าของบางส่วน$myvar
ในบางสถานที่
bash-4.3
อย่างน้อยที่ยังคงเป็นช่องโหว่ในการฉีดคำสั่งที่มีmyvar=$'\xa3``reboot`\xa3`'
อยู่ในสถานที่เกิดเหตุ zh_HK.big5hkscs เช่น
ก่อนอื่นอย่างที่คนอื่นสังเกตเห็นว่าcd
คำสั่งของคุณไม่มีประโยชน์เพราะมันเกิดขึ้นในบริบทของเชลล์ที่ออกไปทันที แต่โดยทั่วไปแล้วคำถามนี้สมเหตุสมผลดี สิ่งที่คุณต้องการคือวิธีที่เชลล์อ้างถึงสตริงโดยพลการเพื่อให้สามารถใช้ในบริบทที่มันจะถูกตีความว่าเป็นสตริงที่อ้างถึงเชลล์ ฉันมีตัวอย่างนี้ในหน้าshของฉัน:
quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }
ด้วยฟังก์ชั่นนี้คุณสามารถทำได้:
myvar="/path to/my directory"
sudo bash -c "cd $(quote "$myvar")"
สิ่งที่Stéphane Chazelas แนะนำนั้นเป็นวิธีที่ดีที่สุดในการทำสิ่งนี้ ฉันจะให้คำตอบเกี่ยวกับวิธีอ้างอิงอย่างปลอดภัยด้วยไวยากรณ์การขยายพารามิเตอร์เมื่อเทียบกับการใช้sed
กระบวนการย่อยเช่นในคำตอบของร ..
myvar='foo \'\''"bar'
quote() {
printf "'%s'" "${1//\'/\'\\\'\'}"
}
printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"
ผลลัพธ์ของสคริปต์นั้นคือ:
value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar
ใช่มีการแบ่งคำ
ในทางเทคนิคแล้วการแยกบรรทัดคำสั่งบน bash จะเป็นการแยกบรรทัด
ก่อนอื่นมาขอความถูกต้อง:
วิธีที่ง่ายที่สุด (และไม่ปลอดภัย) ของโซลูชันในการรับคำสั่งให้ทำงานตามที่ฉันเชื่อว่าคุณจะต้องการคือ:
bash -c "cd \"$myvar\""
เรามายืนยันสิ่งที่เกิดขึ้น (สมมติว่าmyvar="/path to/my directory"
):
$ bash -c "printf '<%s>\n' $myvar"
<./path>
<to/my>
<directory>
อย่างที่คุณเห็นสตริงพา ธ ถูกแบ่งบนช่องว่าง และ:
$ bash -c "printf '<%s>\n' \"$myvar\""
<./path to/my directory>
ไม่แยก
อย่างไรก็ตามคำสั่ง (ตามที่เขียน) ไม่ปลอดภัย ใช้ดีกว่า:
$ bash -c "cd -P -- \"$myvar\"; pwd"
/home/user/temp/path to/my directory
ที่จะป้องกันซีดีจากไฟล์ที่ขึ้นต้นด้วยเส้นประ (-) หรือตามลิงค์ที่อาจนำไปสู่ปัญหา
สิ่งนี้จะใช้ได้หากคุณสามารถเชื่อถือได้ว่าสตริงใน$myvar
นั้นเป็นเส้นทาง
อย่างไรก็ตาม "การฉีดรหัส" ยังคงเป็นไปได้เนื่องจากคุณ "กำลังเรียกใช้งานสตริง":
$ myvar='./path to/my directory";date;"true'
$ bash -c "printf '<%s> ' \"$myvar\"; echo"
<./path to/my directory> Tue May 8 12:25:51 UTC 2018
คุณต้องการข้อความที่แข็งแกร่งกว่าเช่น:
$ bash -c 'printf "<%s> " "$1"; echo' _ "$myvar"
<./path to/my directory";date -u;"true>
ขณะที่คุณสามารถดูด้านบนค่าไม่ได้ตีความ "$1'
แต่เพียงใช้เป็น
ตอนนี้เราสามารถใช้ sudo ได้อย่างปลอดภัย
$ sudo bash -c 'cd -P -- "$1"; pwd' _ "$myvar"
_: line 0: cd: ./path to/my directory";date -u;"true: No such file or directory
หากมีหลายข้อโต้แย้งคุณสามารถใช้:
$ sudo bash -c 'printf "<%s>" "$@"' _ "$val1" "$val2" "$val3"
เครื่องหมายคำพูดเดี่ยวจะไม่ทำงานที่นี่เนื่องจากตัวแปรของคุณจะไม่ถูกขยาย หากคุณมั่นใจว่าตัวแปรสำหรับเส้นทางของคุณถูกทำให้สะอาดโซลูชั่นที่ง่ายที่สุดคือการเพิ่มเครื่องหมายคำพูดรอบ ๆ การขยายตัวที่เกิดขึ้นทันทีที่มันอยู่ใน bash shell ใหม่:
sudo bash -c "cd \"$myvar\""
$myvar
ชอบ$(reboot)
หรือ"; reboot; #
sudo bash -c "cd -P -- '$myvar'"
จะ จำกัด ปัญหาเฉพาะค่า$myvar
ที่มีอักขระเครื่องหมายคำพูดเดี่ยวซึ่งจะทำให้การฆ่าเชื้อง่ายขึ้น
cd
มีเพียงผลภายในbash -c
เปลือก