โดยทั่วไปมันเป็นปัญหาการพกพา (และความน่าเชื่อถือ)
เริ่มแรกechoไม่ยอมรับตัวเลือกใด ๆ และไม่ขยายอะไรเลย สิ่งที่มันทำทั้งหมดคือการแสดงผลอาร์กิวเมนต์ของมันคั่นด้วยอักขระเว้นวรรคและสิ้นสุดด้วยอักขระขึ้นบรรทัดใหม่
ตอนนี้มีคนคิดว่ามันจะดีถ้าเราสามารถทำสิ่งต่าง ๆ เช่นecho "\n\t"การส่งออกบรรทัดใหม่หรืออักขระแท็บหรือมีตัวเลือกที่จะไม่ส่งออกอักขระบรรทัดใหม่ต่อท้าย
จากนั้นพวกเขาคิดว่ายาก แต่แทนที่จะเพิ่มฟังก์ชันการทำงานที่เปลือก (เช่นperlที่อยู่ภายในราคาคู่\tจริงหมายถึงอักขระแท็บ) echoพวกเขาเพิ่มไปยัง
David Korn ตระหนักถึงความผิดพลาดและนำเสนอรูปแบบคำพูดใหม่ของเชลล์$'...'ซึ่งต่อมาถูกคัดลอกมาbashและในเวลาzshนั้นมันก็สายเกินไป
ตอนนี้เมื่อ UNIX มาตรฐานechoได้รับการโต้แย้งซึ่งมีอักขระสองตัว\และtแทนที่จะส่งออกมันจะส่งออกอักขระแท็บ และทันทีที่เห็น\cในอาร์กิวเมนต์มันจะหยุดการส่งออก (ดังนั้นการขึ้นบรรทัดใหม่จะไม่ได้รับผลลัพธ์เช่นกัน)
ผู้จำหน่าย / เวอร์ชั่นอื่น ๆ ของ shells / Unix เลือกที่จะทำมันแตกต่างกัน: พวกเขาเพิ่ม-eตัวเลือกในการขยายลำดับ escape และ-nตัวเลือกที่จะไม่แสดงบรรทัดขึ้นบรรทัดใหม่ บางคนมีการ-Eปิดการใช้งานลำดับ escape บางส่วนมี-nแต่ไม่-eรายการลำดับการยกเว้นได้รับการสนับสนุนโดยechoการใช้งานอย่างใดอย่างหนึ่งไม่จำเป็นต้องเหมือนกับรายการอื่นที่สนับสนุน
สเวน Mascheck มีหน้าที่ดีที่แสดงให้เห็นถึงขอบเขตของปัญหา
ในบรรดาผู้echoใช้งานที่สนับสนุนตัวเลือกมีโดยทั่วไปการสนับสนุนของไม่มี--การทำเครื่องหมายจุดสิ้นสุดของตัวเลือก (เช่นechoในตัวของเปลือกหอยที่ไม่ใช่บอร์นเหมือนทำและ zsh สนับสนุน-สำหรับว่าที่) ดังนั้นสำหรับตัวอย่างเช่นมันเป็นเรื่องยากที่จะส่งออก"-n"ที่มีechoใน เปลือกหอยจำนวนมาก
บนเปลือกหอยบางอย่างเช่นbash¹หรือksh93²หรือyash( $ECHO_STYLEตัวแปร) พฤติกรรมแม้ขึ้นอยู่กับว่าเปลือกถูกรวบรวมหรือสิ่งแวดล้อม (GNU echoพฤติกรรมนอกจากนี้ยังจะมีการเปลี่ยนแปลงหาก$POSIXLY_CORRECTอยู่ในสภาพแวดล้อมและกับรุ่น4 , zsh's ด้วยbsd_echoตัวเลือก pdksh บางposixตัวมีตัวเลือกหรือไม่ว่าพวกเขาจะเรียกว่าเป็นshหรือไม่) ดังนั้นสองbash echos แม้จะมาจากเวอร์ชั่นเดียวกันbashก็ไม่รับประกันว่าจะทำงานเหมือนกัน
POSIX says: ถ้าอาร์กิวเมนต์แรกคือ-nหรือข้อโต้แย้งใด ๆ ที่มีเครื่องหมายแล้วพฤติกรรมที่ไม่ระบุรายละเอียด bashเสียงสะท้อนในเรื่องนั้นไม่ใช่ POSIX ในตัวอย่างecho -eนั้นไม่ได้เอาต์พุต-e<newline>ตามที่ POSIX ต้องการ ข้อมูลจำเพาะของ UNIX นั้นเข้มงวด-nและห้ามไม่ให้มีการขยายตัวของลำดับหนีบางอย่างรวมถึงลำดับที่\cจะหยุดการส่งออก
ข้อมูลจำเพาะเหล่านั้นไม่ได้มาเพื่อช่วยชีวิตที่นี่เนื่องจากการใช้งานหลายอย่างไม่สอดคล้อง แม้แต่ระบบที่ได้รับการรับรองบางอย่างเช่น macOS 5ก็ยังไม่เข้ากัน
ในการเป็นตัวแทนของความเป็นจริงในปัจจุบันPOSIX ควรพูดจริง ๆ ว่า : ถ้าอาร์กิวเมนต์แรกตรงกับ^-([eEn]*|-help|-version)$regexp ที่ขยายเพิ่มหรืออาร์กิวเมนต์ใด ๆ มีแบ็กสแลช (หรืออักขระที่มีการเข้ารหัสประกอบด้วยการเข้ารหัสอักขระแบ็กสแลชเหมือนαในโลแคล ยังไม่ระบุ
ทั้งหมดในที่ทุกท่านไม่ทราบว่าecho "$var"จะส่งออกจนกว่าคุณจะสามารถตรวจสอบว่าไม่ประกอบด้วยอักขระทับขวาและไม่ได้เริ่มต้นด้วย$var -ข้อมูลจำเพาะ POSIX จริง ๆ บอกให้เราใช้printfแทนในกรณีนั้น
ดังนั้นหมายความว่าคุณไม่สามารถใช้echoแสดงข้อมูลที่ไม่มีการควบคุมได้ กล่าวอีกนัยหนึ่งถ้าคุณกำลังเขียนสคริปต์และกำลังรับอินพุตภายนอก (จากผู้ใช้เป็นอาร์กิวเมนต์หรือชื่อไฟล์จากระบบไฟล์ ... ) คุณไม่สามารถใช้echoเพื่อแสดง
นี่โอเค:
echo >&2 Invalid file.
มันไม่ใช่:
echo >&2 "Invalid file: $file"
(แม้ว่ามันจะทำงานได้ดีกับการใช้งานบางอย่าง (ไม่สอดคล้องกับ UNIX) echoเช่นbashเมื่อxpg_echoตัวเลือกไม่ได้เปิดใช้งานไม่ทางใดก็ทางหนึ่งเช่นเวลาการรวบรวมหรือผ่านสภาพแวดล้อม)
file=$(echo "$var" | tr ' ' _)ไม่ได้ตกลงในการใช้งานมากที่สุด (ยกเว้นเป็นyashด้วยECHO_STYLE=raw(มีข้อแม้ที่ว่าyash's ตัวแปรไม่สามารถถือลำดับโดยพลการของไบต์ชื่อไฟล์จึงไม่พล) และzsh' s echo -E - "$var"6 )
printfบนมืออื่น ๆ ที่มีความน่าเชื่อถือมากขึ้นอย่างน้อยเมื่อมัน จำกัด echoอยู่ที่การใช้งานพื้นฐานของ
printf '%s\n' "$var"
จะส่งออกเนื้อหา$varตามด้วยอักขระขึ้นบรรทัดใหม่โดยไม่คำนึงถึงอักขระที่อาจมี
printf '%s' "$var"
จะเอาท์พุทมันโดยไม่ต้องขึ้นบรรทัดใหม่ของตัวละคร
ตอนนี้ก็มีความแตกต่างระหว่างprintfการใช้งาน มีคุณสมบัติหลักที่ระบุโดย POSIX แต่มีส่วนขยายจำนวนมาก ตัวอย่างเช่นบางสนับสนุนการ%qอ้างถึงข้อโต้แย้ง แต่วิธีการทำแตกต่างกันไปจากเปลือกหอยเพื่อเปลือกบางการสนับสนุน\uxxxxสำหรับอักขระ Unicode พฤติกรรมแตกต่างกันไปprintf '%10s\n' "$var"ในโลแคลหลายไบต์มีผลลัพธ์ที่แตกต่างกันอย่างน้อยสามรายการprintf %b '\123'
แต่ในที่สุดถ้าคุณยึดติดกับชุดคุณลักษณะ POSIX printfและอย่าพยายามทำอะไรที่แฟนซีเกินไปคุณก็จะไม่มีปัญหา
แต่โปรดจำไว้ว่าอาร์กิวเมนต์แรกคือรูปแบบดังนั้นจึงไม่ควรมีข้อมูลตัวแปร / ไม่มีการควบคุม
ความน่าเชื่อถือมากขึ้นechoสามารถดำเนินการโดยใช้printfเช่น:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
เชลล์ย่อย (ซึ่งหมายถึงการวางไข่กระบวนการพิเศษในการใช้งานเชลล์ส่วนใหญ่) สามารถหลีกเลี่ยงการใช้local IFSกับเชลล์จำนวนมากหรือโดยการเขียนเช่น:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
หมายเหตุ
1. พฤติกรรมbashของวิธีechoการสามารถเปลี่ยนแปลงได้
ด้วยbashเวลาทำงานมีสองสิ่งที่ควบคุมพฤติกรรมของecho(ด้านข้างenable -n echoหรือนิยามใหม่echoเป็นฟังก์ชั่นหรือนามแฝง): xpg_echo bashตัวเลือกและไม่ว่าจะbashอยู่ในโหมด posix posixสามารถเปิดใช้งานโหมดได้หากbashถูกเรียกว่าเป็นshหรือPOSIXLY_CORRECTอยู่ในสภาพแวดล้อมหรือด้วยposixตัวเลือก:
พฤติกรรมเริ่มต้นในระบบส่วนใหญ่:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo ขยายลำดับเมื่อ UNIX ต้องการ:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
มันยังคงให้เกียรติ-nและ-e(และ-E):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
ด้วยxpg_echoและโหมด POSIX:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A
เวลานี้bashเป็นไปตาม POSIX และ UNIX โปรดทราบว่าในโหมด POSIX bashยังคงไม่สอดคล้องกับ POSIX เนื่องจากไม่ได้แสดงผล-eใน:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
ค่าเริ่มต้นสำหรับ xpg_echo และ posix สามารถกำหนดได้ในเวลารวบรวมด้วยตัวเลือก--enable-xpg-echo-defaultและสคริปต์ นั่นเป็นสิ่งที่มักจะรุ่นล่าสุดของระบบปฏิบัติการ / X ทำเพื่อสร้างของพวกเขา ไม่มี Unix / Linux การดำเนินงาน / การจัดจำหน่ายในใจขวาของพวกเขาโดยทั่วไปจะทำว่าแม้ว่า ที่จริงแล้วไม่เป็นความจริงว่า Oracle มาพร้อมกับ Solaris 11 (ในแพ็คเกจเพิ่มเติม) ดูเหมือนว่าจะถูกสร้างขึ้นด้วย(ไม่ใช่ในกรณี Solaris 10)--enable-strict-posix-defaultconfigure/bin/sh/bin/bash/bin/bash--enable-xpg-echo-default
2. วิธีksh93's echoพฤติกรรมสามารถเปลี่ยนแปลงได้
ในksh93ไม่ว่าจะechoขยายลำดับการหลบหนีหรือไม่และตระหนักถึงตัวเลือกขึ้นอยู่กับเนื้อหาของ$PATHและ / หรือ$_AST_FEATURESตัวแปรสภาพแวดล้อม
หาก$PATHมีส่วนประกอบที่มี/5binหรือ/xpgก่อนหน้า/binหรือ/usr/binส่วนประกอบก็จะทำงานในลักษณะ SysV / UNIX (ขยายลำดับไม่ยอมรับตัวเลือก) หากพบ/ucbหรือ/bsdก่อนหรือถ้ามี$_AST_FEATURES7UNIVERSE = ucbก็จะทำงานในลักษณะ BSD 3 ( -eเพื่อเปิดใช้งานการขยายตัวรับรู้-n)
ค่าเริ่มต้นขึ้นอยู่กับระบบ BSD บน Debian (ดูผลลัพธ์builtin getconf; getconf UNIVERSEใน ksh93 รุ่นล่าสุด):
$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD
3. BSD สำหรับ echo -e?
การอ้างอิงถึง BSD สำหรับการจัดการ-eตัวเลือกนั้นทำให้เข้าใจผิดเล็กน้อยที่นี่ echoพฤติกรรมส่วนใหญ่ที่แตกต่างและเข้ากันไม่ได้ทั้งหมดถูกนำมาใช้ที่ AT&T:
\n, \0ooo, \cในการทำงานของโปรแกรมเมอร์บัลลังก์ยูนิกซ์ (Unix บนพื้นฐานของ V6) และส่วนที่เหลือ ( \b, \r... ) ในระบบยูนิกซ์ III Ref
-nใน Unix V7 (โดย Dennis Ritchie Ref )
-eใน Unix V8 (โดย Dennis Ritchie Ref )
-Eในตอนแรกอาจมาจากตัวเองbash(CWRU / CWRU.chlog ในเวอร์ชั่น 1.13.5กล่าวถึง Brian Fox เพิ่มใน 1992-10-18, GNU echoคัดลอกไม่นานหลังจากใน sh-utils-1.8 ออก 10 วันต่อมา)
ในขณะที่echobuiltin ของshหรือ BSDs ได้รับการสนับสนุน-eตั้งแต่วันที่พวกเขาเริ่มใช้ Almquist shell สำหรับมันในช่วงต้น 90s echoยูทิลิตี้แบบสแตนด์อโลนจนถึงทุกวันนี้ไม่สนับสนุนมัน ( FreeBSDechoยังไม่สนับสนุน-eแม้ว่ามันจะสนับสนุน-nเช่น Unix V7 (และ\cเฉพาะที่ท้ายอาร์กิวเมนต์สุดท้าย))
การจัดการของ-eถูกบันทึกอยู่ในksh93's echoเมื่ออยู่ใน BSD จักรวาลในรุ่น ksh93r ที่ปล่อยออกมาในปี 2006 และสามารถใช้งานได้ตลอดเวลารวบรวม
4. GNU echo เปลี่ยนแปลงพฤติกรรมใน 8.31
ตั้งแต่ coreutils 8.31 (และนี้กระทำ ) GNU echoตอนนี้ขยายลำดับหนีโดยค่าเริ่มต้นเมื่อ POSIXLY_CORRECT อยู่ในสภาพแวดล้อมเพื่อให้ตรงกับลักษณะการทำงานของbash -o posix -O xpg_echo's echobuiltin (ดูรายงานข้อผิดพลาด )
5. macOS echo
รุ่นส่วนใหญ่ของ MacOS ได้รับการรับรองจากยูนิกซ์ OpenGroup
shbuiltin ของพวกเขาechoเป็นไปตามที่มันเป็นbash(รุ่นเก่ามาก) สร้างด้วยการxpg_echoเปิดใช้งานโดยค่าเริ่มต้น แต่echoยูทิลิตี้แบบสแตนด์อโลนของพวกเขาไม่ได้ env echo -nเอาท์พุทอะไรแทน-n<newline>, env echo '\n'เอาท์พุทแทน\n<newline><newline><newline>
นั่น/bin/echoเป็นหนึ่งจาก FreeBSD ซึ่งยับยั้งการส่งออกขึ้นบรรทัดใหม่ถ้าอาร์กิวเมนต์แรกคือ-nหรือ (ตั้งแต่ปี 1995) ถ้าอาร์กิวเมนต์สุดท้ายจะสิ้นสุดลงใน\cแต่ไม่สนับสนุนลำดับทับขวาอื่น ๆ ที่จำเป็นโดย UNIX \\ไม่ได้
6. echoการใช้งานที่สามารถส่งออกข้อมูลทุกคำต่อคำ
พูดอย่างเคร่งครัดนอกจากนี้คุณยังสามารถนับว่า FreeBSD / MacOS /bin/echoเหนือ (ไม่ใช่เปลือกของพวกเขาechoในตัว) ที่zsh's echo -E - "$var"หรือyash' s ECHO_STYLE=raw echo "$var"( printf '%s\n' "$var") สามารถเขียน:
/bin/echo "$var
\c"
การใช้งานที่สนับสนุน-Eและ-n(หรือสามารถกำหนดค่าให้) สามารถทำได้เช่นกัน:
echo -nE "$var
"
และzsh's echo -nE - "$var"( printf %s "$var") สามารถเขียน
/bin/echo "$var\c"
7. _AST_FEATURESและ ASTUNIVERSE
_AST_FEATURESไม่ได้หมายถึงจะจัดการโดยตรงนั้นจะถูกใช้ในการเผยแพร่การตั้งค่า AST ข้ามเรียกคำสั่ง การกำหนดค่าหมายถึงการทำผ่านastgetconf()API (ไม่มีเอกสาร) ภายในksh93ที่getconfbuiltin (เปิดใช้งานด้วยbuiltin getconfหรือโดยการกล่าวอ้างcommand /opt/ast/bin/getconf) เป็นส่วนติดต่อกับastgetconf()
ตัวอย่างเช่นคุณต้องbuiltin getconf; getconf UNIVERSE = attเปลี่ยนการUNIVERSEตั้งค่าเป็นatt(ทำให้มีechoพฤติกรรมแบบ SysV เหนือสิ่งอื่นใด) หลังจากทำที่คุณจะสังเกตเห็นตัวแปรสภาพแวดล้อมมี$_AST_FEATURESUNIVERSE = att
echo -eอะไร