วิธีหนึ่งที่แข็งแกร่งในการทุบตีคือการขยายเข้าไปในอาร์เรย์และส่งออกองค์ประกอบแรกเท่านั้น:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(คุณสามารถทำได้เพียงแค่echo $files
ดัชนีที่ขาดหายไปนั้นถือเป็น [0])
สิ่งนี้จะจัดการกับพื้นที่ / แท็บ / บรรทัดใหม่และ metacharacters อื่น ๆ อย่างปลอดภัยเมื่อขยายชื่อไฟล์ โปรดทราบว่าการตั้งค่าภาษาจะมีผลต่อการเปลี่ยนแปลง "ครั้งแรก"
นอกจากนี้คุณยังสามารถทำเช่นนี้โต้ตอบกับทุบตีฟังก์ชั่นเสร็จสิ้น :
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]} # string to expand
if compgen -G "$cur*" > /dev/null; then
local files=( ${cur:+$cur*} ) # don't expand empty input as *
[ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
fi
}
complete -o bashdefault -F _echo echo
_echo
ฟังก์ชันนี้จะผูกฟังก์ชันเพื่อทำให้อาร์กิวเมนต์สมบูรณ์กับecho
คำสั่ง (แทนที่การเสร็จสิ้นตามปกติ) มีการเพิ่ม "*" เพิ่มเติมในรหัสด้านบนคุณสามารถกดแท็บชื่อไฟล์บางส่วนและหวังว่าสิ่งที่ถูกต้องจะเกิดขึ้น
รหัสมีความซับซ้อนเล็กน้อยแทนที่จะตั้งค่าหรือสมมติว่าnullglob
( shopt -s nullglob
) เราตรวจสอบว่าcompgen -G
สามารถขยาย glob ไปสู่การแข่งขันบางส่วนจากนั้นเราขยายอย่างปลอดภัยไปยังอาร์เรย์และสุดท้ายตั้งค่า COMPREPLY เพื่อให้การอ้างอิงมีความแข็งแกร่ง
คุณสามารถทำสิ่งนี้ได้บางส่วน (โดยการขยายโปรแกรมแบบกลม) ด้วย bash's compgen -G
แต่ก็ไม่ได้ทนทานเพราะมันส่งผลโดยไม่ต้องพูดถึง stdout
ตามปกติแล้วการทำให้สำเร็จค่อนข้างเต็มไปด้วยสิ่งนี้จะทำให้สิ่งอื่น ๆ เสร็จสมบูรณ์รวมถึงตัวแปรสภาพแวดล้อม (ดู_bash_def_completion()
ฟังก์ชันที่นี่เพื่อดูรายละเอียดของการจำลองพฤติกรรมเริ่มต้น)
คุณสามารถใช้compgen
นอกฟังก์ชั่นที่เสร็จสมบูรณ์:
files=( $(compgen -W "$pattern") )
สิ่งหนึ่งที่ควรทราบคือ "~" ไม่ใช่รูปโค้ง แต่ถูกจัดการโดยการทุบตีในขั้นตอนการขยายแยกต่างหากเช่นเดียวกับตัวแปร $ และการขยายอื่น ๆ compgen -G
เพียงแค่ชื่อไฟล์กลมกลืน แต่compgen -W
ให้การขยายเริ่มต้นทั้งหมดของ bash แม้ว่าอาจมีการขยายมากเกินไป (รวมถึง``
และ $()
) ซึ่งแตกต่างจาก-G
ที่-W
ถูกยกมาได้อย่างปลอดภัย (ฉันไม่สามารถอธิบายความแตกต่าง) เนื่องจากวัตถุประสงค์ของ-W
มันคือมันขยายโทเค็นซึ่งหมายความว่ามันจะขยาย "a" ถึง "a" แม้ว่าจะไม่มีไฟล์ดังกล่าวอยู่ดังนั้นจึงอาจไม่เหมาะ
สิ่งนี้ง่ายต่อการเข้าใจ แต่อาจมีผลข้างเคียงที่ไม่พึงประสงค์:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
แล้ว:
touch $'curious \n filename'
echo curious*
tab
หมายเหตุการใช้printf %q
เพื่ออ้างอิงค่าอย่างปลอดภัย
ตัวเลือกสุดท้ายคือการใช้เอาต์พุตที่คั่นด้วย 0 กับยูทิลิตี้ GNU (ดูคำถามที่พบบ่อยทุบตี ):
pattern="*.txt"
while IFS= read -r -d $'\0' filename; do
printf '%q' "$filename";
break;
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )
ตัวเลือกนี้ช่วยให้คุณสามารถควบคุมคำสั่งการเรียงลำดับได้มากขึ้น (คำสั่งเมื่อขยาย glob จะขึ้นอยู่กับสถานที่ของคุณ / LC_COLLATE
และอาจจะใช่หรือไม่ก็ได้) แต่เป็นค้อนที่ค่อนข้างใหญ่สำหรับปัญหาเล็ก ๆ ;-)