โดยทั่วไปมันเป็นปัญหาการพกพา (และความน่าเชื่อถือ)
เริ่มแรก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
echo
s แม้จะมาจากเวอร์ชั่นเดียวกัน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-default
configure
/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_FEATURES
7UNIVERSE = 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 วันต่อมา)
ในขณะที่echo
builtin ของ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 echo
builtin (ดูรายงานข้อผิดพลาด )
5. macOS echo
รุ่นส่วนใหญ่ของ MacOS ได้รับการรับรองจากยูนิกซ์ OpenGroup
sh
builtin ของพวกเขา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
ที่getconf
builtin (เปิดใช้งานด้วยbuiltin getconf
หรือโดยการกล่าวอ้างcommand /opt/ast/bin/getconf
) เป็นส่วนติดต่อกับastgetconf()
ตัวอย่างเช่นคุณต้องbuiltin getconf; getconf UNIVERSE = att
เปลี่ยนการUNIVERSE
ตั้งค่าเป็นatt
(ทำให้มีecho
พฤติกรรมแบบ SysV เหนือสิ่งอื่นใด) หลังจากทำที่คุณจะสังเกตเห็นตัวแปรสภาพแวดล้อมมี$_AST_FEATURES
UNIVERSE = att
echo -e
อะไร