ไปที่ไดเรกทอรีโดยใช้ตัวแปร bash ไม่ทำงานเมื่อชื่อไดเรกทอรีมีช่องว่าง


11

สมมติว่าฉันต้องการเก็บคำสั่งต่อไปนี้ในตัวแปร

cd "/cygdrive/c/Program Files/"

ดังนั้นฉันทำสิ่งนี้

dir="cd \"/cygdrive/c/Program Files/\""

ที่ควรเก็บคำสั่งเพื่อนำทางไปยังไดเรกทอรี Program Files ดังนั้นเมื่อฉันพิมพ์ $ dir มันจะพาฉันไปยังไดเรกทอรีนั้น ในการตรวจสอบว่าใบเสนอราคาได้รับการหลีกเลี่ยงอย่างถูกต้องฉันพิมพ์

echo $dir

ซึ่งให้ฉัน

cd "/cygdrive/c/Program Files/"

ดังนั้นทุกอย่างควรจะทำงานได้ดี อย่างไรก็ตามเมื่อฉันพิมพ์

$dir

ฉันเข้าใจ

bash: cd: "/cygdrive/c/Program: No such file or directory

ผมทำอะไรผิดหรือเปล่า? ฉันใช้ Cygwin แต่ฉันคิดว่าปัญหานี้ใช้ได้กับการทุบตีทั่วไป


ลองลบเครื่องหมายคำพูดล้อมรอบชื่อไดเรกทอรีและหลีกเลี่ยงช่องว่างด้วยแบ็กสแลชแทน
Mike Fitzpatrick

คำตอบ:


8

คำตอบสั้น ๆ : ดูBashFAQ # 050 ("ฉันพยายามใส่คำสั่งในตัวแปร แต่กรณีที่ซับซ้อนล้มเหลวเสมอ!")

คำตอบยาว: เมื่อ bash แยกวิเคราะห์คำสั่งจะวิเคราะห์คำพูดก่อนที่จะแทนที่ตัวแปร; มันจะไม่ย้อนกลับและวิเคราะห์คำพูดอีกครั้งในค่าของตัวแปรดังนั้นพวกเขาจึงปิดท้ายไม่ได้ทำอะไรที่มีประโยชน์ การใช้ echo เพื่อตรวจสอบคำสั่งนั้นทำให้เข้าใจผิดอย่างสมบูรณ์เพราะมันแสดงคำสั่งหลังจากที่ได้รับการแยกวิเคราะห์; ถ้าคุณต้องการที่จะเห็นสิ่งที่กำลังดำเนินการจริง ๆ ก็จะใช้set -xคำสั่งเชลล์พิมพ์ในขณะที่พวกเขากำลังดำเนินการหรือใช้printf "%q " $dir; echoแทน echo ธรรมดา

หากคุณต้องการเก็บคำสั่งที่ซับซ้อน (เช่นคำสั่งที่มีช่องว่างหรืออักขระพิเศษอื่น ๆ ใน "Words") คุณต้องใส่คำสั่งนั้นลงในอาร์เรย์แทนตัวแปรข้อความธรรมดาแล้วขยายมันด้วยสำนวน `` $ { อาร์เรย์ [@]} "เช่นนี้:

dir=(cd "/cygdrive/c/Program Files/")
"${dir[@]}"

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

alias dir='cd "/cygdrive/c/Program Files/"'
dir

หรือ

dir() { cd "/cygdrive/c/Program Files/"; }
dir

7

เมื่อทุบตีขยาย$dirมันจะทำการแยกคำและกลมบนมัน แยก Word การผลิตสามคำ: cd, และ"/cygdrive/c/Program Files/"จากนั้นคำสั่งเพื่อดำเนินการจะcdมีสองอาร์กิวเมนต์; cdดูเฉพาะอาร์กิวเมนต์แรก"/cygdrive/c/Programซึ่งไม่ใช่ไดเรกทอรีที่มีอยู่

หากคุณต้องการประเมินเชลล์แบบเต็มบนเนื้อหาของตัวแปรให้ใช้eval:

eval "$dir"

โปรดทราบว่าคุณต้องใส่เครื่องหมายอัญประกาศล้อมรอบ$dirมิฉะนั้นการแยกคำจะต้องดำเนินการก่อนจากนั้นevalจึงทำการเชื่อมอาร์กิวเมนต์เข้ากับช่องว่าง สิ่งนี้จะเกิดขึ้นกับการทำงานที่นี่ แต่โดยทั่วไปจะไม่ดี (เช่นหากมีช่องว่างติดต่อกันสองรายการในชื่อไฟล์)

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

dir () {
  cd "/cygdrive/c/Program Files/"
}

หากคุณสนใจที่$dirจะพิมพ์เพื่อสลับไปยังไดเรกทอรีหนึ่ง ๆ และนี่ไม่ใช่แค่ตัวอย่างง่ายๆตั้งแต่ bash 4 ให้ใส่และตั้งค่าshopt -s autocdของคุณ.bashrc

dir="/cygdrive/c/Program Files/"

หลังจากนั้นคุณสามารถพิมพ์เพียง"/cygdrive/c/Program Files/"หรือ"$dir"ที่เชลล์พรอมต์เพื่อสลับไปยังไดเรกทอรีนั้น คุณยังคงต้องใช้คำพูดสองรอบ$dir; หากคุณไม่ชอบให้ใช้ zsh แทน bash


6

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

ค่อนข้างมากกว่า

dir="cd \"/cygdrive/c/Program Files/\""

ลองหนึ่งในสิ่งเหล่านี้:

dir="/cygdrive/c/Program Files"
...
cd "$dir"

หรือ

dir() { cd "/cygdrive/c/Program Files"; }
dir

หรือ

alias dir='cd "/tmp/Program Files"'
...
dir

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

แก้ไข:

และdirอาจเป็นชื่อที่ไม่ดีสำหรับนามแฝงหรือฟังก์ชั่นเนื่องจากมีคำสั่งอยู่ในชื่อนั้น (โดยพื้นฐานแล้วมันเป็นรุ่นlsอย่างน้อยถ้าคุณมี coreutils ของ GNU)


upvote forStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob

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