สิ่งสำคัญคือต้องตระหนักว่าจริงๆแล้วมันคือเชลล์ที่ขยายfoo*
ไปยังรายการของชื่อไฟล์ที่ตรงกันดังนั้นจึงมีความmv
สามารถเล็กน้อยที่จะทำเอง
ปัญหาตรงนี้คือเมื่อ glob ไม่ตรงกันเชลล์บางตัวbash
(และเชลล์คล้ายบอร์นอื่น ๆ ส่วนใหญ่พฤติกรรมการบั๊กกี้นั้นถูกนำเสนอโดยบอร์นเชลล์ในช่วงปลายยุค 70) ผ่านรูปแบบคำต่อคำไปยังคำสั่ง
ดังนั้นที่นี่เมื่อfoo*
ไม่ตรงกับไฟล์ใด ๆ แทนการยกเลิกคำสั่ง (เช่นก่อนบอร์นเปลือกหอยและเปลือกหอยทันสมัยหลายอย่างทำ) เปลือกผ่านคำต่อคำfoo*
ไฟล์mv
ดังนั้นโดยทั่วไปขอให้ย้ายไฟล์ที่เรียกว่าmv
foo*
ไฟล์นั้นไม่มีอยู่ ถ้าเป็นเช่นนั้นจริง ๆ แล้วมันจะตรงกับรูปแบบดังนั้นmv
รายงานข้อผิดพลาด หากรูปแบบได้รับการfoo[xy]
แทนที่mv
อาจมีการย้ายไฟล์ที่เรียกโดยไม่ได้ตั้งใจfoo[xy]
แทนfoox
และfooy
ไฟล์
ตอนนี้แม้ในเชลล์เหล่านั้นที่ไม่มีปัญหา (pre-Bourne, csh, tcsh, ปลา, zsh, bash -O failglob) คุณจะยังคงได้รับข้อผิดพลาดmv foo* ~/bar
แต่คราวนี้โดยเชลล์
หากคุณต้องการพิจารณาว่าไม่ใช่ข้อผิดพลาดหากไม่มีการจับคู่ไฟล์foo*
และในกรณีนั้นไม่ย้ายอะไรคุณจะต้องสร้างรายการไฟล์ก่อน (ในลักษณะที่ไม่ทำให้เกิดข้อผิดพลาดเช่นโดยใช้nullglob
ตัวเลือกของ เชลล์บางตัว) และจากนั้นเรียกใช้เท่านั้นmv
คือรายการไม่ว่างเปล่า
นั่นจะดีกว่าการซ่อนข้อผิดพลาดทั้งหมดของmv
(เช่นการเพิ่ม2> /dev/null
จะ) ราวกับว่าmv
ล้มเหลวด้วยเหตุผลอื่นใดคุณอาจยังคงต้องการที่จะรู้ว่าทำไม
ใน zsh
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
หรือใช้ฟังก์ชันที่ไม่ระบุชื่อเพื่อหลีกเลี่ยงการใช้ตัวแปรชั่วคราว:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh
เป็นหนึ่งในเชลล์เหล่านั้นที่ไม่มีบัก Bourne และรายงานข้อผิดพลาดโดยไม่ต้องดำเนินการคำสั่งเมื่อ glob ไม่ตรงกัน (และnullglob
ตัวเลือกไม่ได้เปิดใช้งาน) ดังนั้นที่นี่คุณสามารถซ่อนzsh
ข้อผิดพลาดและเรียกคืนได้ stderr ทำmv
เช่นนั้นคุณจะยังคงเห็นmv
ข้อผิดพลาดถ้ามี แต่ไม่ใช่ข้อผิดพลาดเกี่ยวกับ globs ที่ไม่ตรงกัน:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
หรือคุณสามารถใช้zargs
ซึ่งจะหลีกเลี่ยงปัญหาหากfoo*
glob จะขยายเป็นไฟล์คนเกินไป
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
ใน ksh93:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
ในทุบตี:
bash
ไม่มีไวยากรณ์ที่จะเปิดใช้งานnullglob
สำหรับหนึ่ง glob เท่านั้นและfailglob
ตัวเลือกยกเลิกnullglob
ดังนั้นคุณต้องมีสิ่งต่าง ๆ เช่น:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
หรือตั้งค่าตัวเลือกใน subshell เพื่อบันทึกต้องบันทึกก่อนและเรียกคืนหลังจากนั้น
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
ใน yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
ใน fish
ในเชลล์ปลาพฤติกรรม nullglob เป็นค่าเริ่มต้นสำหรับset
คำสั่งดังนั้นมันเป็นเพียง:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
ไม่มีnullglob
ตัวเลือกใน POSIX sh
และไม่มีอาร์เรย์อื่นนอกเหนือจากพารามิเตอร์ตำแหน่ง มีเคล็ดลับที่คุณสามารถใช้เพื่อตรวจสอบว่ากลมจับคู่หรือไม่:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
โดยการใช้ทั้ง a foo[*]
และfoo*
glob เราสามารถแยกความแตกต่างระหว่างเคสที่ไม่มีไฟล์ที่ตรงกันและอีกไฟล์หนึ่งที่มีไฟล์เดียวที่เรียกว่าfoo*
(ซึ่งset -- foo*
ไม่สามารถทำได้)
อ่านเพิ่มเติม:
mv foo* ~/bar/ 2> /dev/null
?