Về cơ bản, đó là một vấn đề về tính di động (và độ tin cậy).
Ban đầu, echo
không chấp nhận bất kỳ tùy chọn nào và không mở rộng bất cứ điều gì. Tất cả những gì nó đang làm là xuất ra các đối số được phân tách bằng một ký tự khoảng trắng và được chấm dứt bởi một ký tự dòng mới.
Bây giờ, ai đó nghĩ rằng sẽ rất tuyệt nếu chúng ta có thể làm những việc như echo "\n\t"
xuất ký tự dòng hoặc tab mới hoặc có tùy chọn không xuất ký tự dòng mới.
Sau đó, họ suy nghĩ kỹ hơn nhưng thay vì thêm chức năng đó vào hệ vỏ (như perl
nơi bên trong dấu ngoặc kép, \t
thực sự có nghĩa là một ký tự tab), họ đã thêm nó vào echo
.
David Korn đã nhận ra sai lầm và giới thiệu một hình thức trích dẫn vỏ mới: $'...'
sau đó đã được sao chép bash
và zsh
nhưng nó đã quá muộn vào thời điểm đó.
Bây giờ khi một UNIX tiêu chuẩn echo
nhận được một đối số có chứa hai ký tự \
và t
thay vì xuất ra chúng, nó sẽ xuất ra một ký tự tab. Và ngay khi nó nhìn thấy \c
trong một đối số, nó dừng xuất ra (vì vậy dòng mới theo dõi cũng không được xuất ra).
Các shell / nhà cung cấp / phiên bản Unix khác đã chọn làm điều đó một cách khác biệt: họ đã thêm một -e
tùy chọn để mở rộng các chuỗi thoát và một -n
tùy chọn không xuất ra dòng mới. Một số có -E
để vô hiệu hóa các chuỗi thoát, một số có -n
nhưng không -e
, danh sách các chuỗi thoát được hỗ trợ bởi một echo
triển khai không nhất thiết giống như được hỗ trợ bởi một trình tự khác.
Sven Mascheck có một trang đẹp cho thấy mức độ của vấn đề .
Trên những echo
triển khai hỗ trợ tùy chọn, có nói chung không có sự ủng hộ của một --
để đánh dấu sự kết thúc lựa chọn (các echo
BUILTIN của một số vỏ phi Bourne giống như làm, và zsh hỗ trợ -
cho dù đó), vì vậy ví dụ, rất khó để đầu ra "-n"
với echo
trong nhiều vỏ.
Trên một số shell như bash
¹ hoặc ksh93
² hoặc yash
( $ECHO_STYLE
biến), hành vi thậm chí phụ thuộc vào cách shell được biên dịch hoặc môi trường ( echo
hành vi của GNU cũng sẽ thay đổi nếu $POSIXLY_CORRECT
ở trong môi trường và với phiên bản 4 , zsh
với bsd_echo
tùy chọn của phiên bản 4 , với tùy chọn của nó , một số pdksh dựa trên posix
tùy chọn của họ hoặc liệu họ có được gọi là sh
hay không). Vì vậy, hai bash
echo
s, thậm chí từ cùng một phiên bản bash
không được đảm bảo hành xử giống nhau.
POSIX nói: nếu đối số đầu tiên là -n
hoặc bất kỳ đối số nào chứa dấu gạch chéo ngược thì hành vi đó không được chỉ định . bash
tiếng vang trong vấn đề đó không phải là POSIX, ví dụ như echo -e
không xuất ra -e<newline>
như POSIX yêu cầu. Đặc tả UNIX chặt chẽ hơn, nó cấm -n
và yêu cầu mở rộng một số chuỗi thoát bao gồm cả chuỗi \c
để dừng xuất.
Những thông số kỹ thuật đó không thực sự được giải cứu ở đây do nhiều triển khai không tuân thủ. Ngay cả một số hệ thống được chứng nhận như macOS 5 cũng không tuân thủ.
Để thực sự đại diện cho thực tế hiện tại, POSIX thực sự nên nói : nếu đối số đầu tiên khớp với biểu ^-([eEn]*|-help|-version)$
thức chính mở rộng hoặc bất kỳ đối số nào có dấu gạch chéo ngược (hoặc các ký tự có mã hóa chứa mã hóa ký tự dấu gạch chéo ngược như α
trong các địa điểm sử dụng bảng mã BIG5), thì hành vi đó là không xác định.
Nói chung, bạn không biết cái gì echo "$var"
sẽ xuất ra trừ khi bạn có thể chắc chắn rằng $var
nó không chứa các ký tự dấu gạch chéo ngược và không bắt đầu bằng -
. Đặc tả POSIX thực sự bảo chúng ta sử dụng printf
thay thế trong trường hợp đó.
Vì vậy, điều đó có nghĩa là bạn không thể sử dụng echo
để hiển thị dữ liệu không được kiểm soát. Nói cách khác, nếu bạn đang viết một tập lệnh và nó đang lấy đầu vào bên ngoài (từ người dùng làm đối số hoặc tên tệp từ hệ thống tệp ...), bạn không thể sử dụng echo
để hiển thị tập lệnh.
Không sao đâu
echo >&2 Invalid file.
Đây không phải là:
echo >&2 "Invalid file: $file"
(Mặc dù nó sẽ hoạt động tốt với một số echo
triển khai (không tuân thủ UNIX) như bash
khi xpg_echo
tùy chọn chưa được bật theo cách này hay cách khác như tại thời gian biên dịch hoặc qua môi trường).
file=$(echo "$var" | tr ' ' _)
không phải là OK trong hầu hết các trường (trường hợp ngoại lệ là yash
với ECHO_STYLE=raw
(với sự báo trước đó yash
của các biến không thể giữ chuỗi tùy ý các byte do đó không tên tập tin tùy ý) và zsh
's echo -E - "$var"
6 ).
printf
, mặt khác là đáng tin cậy hơn, ít nhất là khi nó bị giới hạn trong việc sử dụng cơ bản echo
.
printf '%s\n' "$var"
Sẽ xuất nội dung $var
theo sau bởi một ký tự dòng mới bất kể nó có thể chứa ký tự nào.
printf '%s' "$var"
Sẽ xuất nó mà không có ký tự dòng mới.
Bây giờ, cũng có sự khác biệt giữa các printf
triển khai. Có một lõi các tính năng được chỉ định bởi POSIX, nhưng sau đó có rất nhiều tiện ích mở rộng. Ví dụ, một số hỗ trợ a %q
để trích dẫn các đối số nhưng cách thực hiện thay đổi từ shell sang shell, một số hỗ trợ \uxxxx
cho các ký tự unicode. Hành vi khác nhau đối với printf '%10s\n' "$var"
các địa phương nhiều byte, có ít nhất ba kết quả khác nhau choprintf %b '\123'
Nhưng cuối cùng, nếu bạn sử dụng bộ tính năng POSIX printf
và không thử làm bất cứ điều gì quá lạ mắt với nó, bạn sẽ không gặp rắc rối.
Nhưng hãy nhớ đối số đầu tiên là định dạng, vì vậy không nên chứa dữ liệu biến / không kiểm soát.
Một cách đáng tin cậy hơn có echo
thể được thực hiện bằng cách sử dụng printf
, như:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
Subshell (ngụ ý sinh ra một quá trình bổ sung trong hầu hết các triển khai shell) có thể tránh được bằng cách sử dụng local IFS
với nhiều shell hoặc bằng cách viết nó như sau:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Ghi chú
1. hành vi bash
của họ echo
có thể được thay đổi như thế nào .
Với bash
, trong thời gian chạy, có hai điều khiển hành vi của echo
(bên cạnh enable -n echo
hoặc xác định lại echo
dưới dạng hàm hoặc bí danh): xpg_echo
bash
tùy chọn và liệu bash
có ở chế độ posix hay không. posix
chế độ có thể được bật nếu bash
được gọi là sh
hoặc nếu POSIXLY_CORRECT
trong môi trường hoặc với posix
tùy chọn:
Hành vi mặc định trên hầu hết các hệ thống:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
mở rộng các chuỗi như UNIX yêu cầu:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Nó vẫn tôn vinh -n
và -e
(và -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Với xpg_echo
chế độ 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
Lần này, bash
là cả POSIX và UNIX phù hợp. Lưu ý rằng trong chế độ POSIX, bash
vẫn không tuân thủ POSIX vì nó không xuất ra -e
trong:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
Các giá trị mặc định cho xpg_echo và posix có thể được xác định tại thời điểm biên dịch với --enable-xpg-echo-default
và --enable-strict-posix-default
các tùy chọn cho configure
tập lệnh. Đó thường là những phiên bản gần đây của OS / X làm gì để xây dựng chúng /bin/sh
. Mặc dù vậy, không có triển khai / phân phối Unix / Linux nào trong tâm trí của họ thường sẽ làm điều đó/bin/bash
. Trên thực tế, điều đó không đúng, việc /bin/bash
Oracle vận chuyển với Solaris 11 (trong gói tùy chọn) dường như được xây dựng cùng --enable-xpg-echo-default
(đó không phải là trường hợp trong Solaris 10).
2. Làm thế nào ksh93
's echo
hành vi có thể được thay đổi.
Trong ksh93
, việc echo
mở rộng các chuỗi thoát hay không và nhận ra các tùy chọn tùy thuộc vào nội dung của các biến $PATH
và / hoặc $_AST_FEATURES
môi trường.
Nếu $PATH
chứa một thành phần có chứa /5bin
hoặc /xpg
trước /bin
hoặc /usr/bin
thành phần thì nó hoạt động theo cách SysV / UNIX (mở rộng trình tự, không chấp nhận các tùy chọn). Nếu nó tìm thấy /ucb
hoặc /bsd
đầu tiên hoặc nếu $_AST_FEATURES
7 chứa UNIVERSE = ucb
, thì nó hoạt động theo cách BSD 3 ( -e
để cho phép mở rộng, nhận dạng -n
).
Mặc định là phụ thuộc hệ thống, BSD trên Debian (xem đầu ra của builtin getconf; getconf UNIVERSE
các phiên bản gần đây của 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 cho tiếng vang -e?
Tham chiếu đến BSD để xử lý -e
tùy chọn là một chút sai lệch ở đây. Hầu hết các echo
hành vi khác nhau và không tương thích đều được giới thiệu tại AT & T:
\n
, \0ooo
, \c
Trong Bench Programmer của làm việc UNIX (dựa trên Unix V6), và phần còn lại ( \b
, \r
...) trong Unix Hệ thống III Ref .
-n
trong Unix V7 (bởi Dennis Ritchie Ref )
-e
trong Unix V8 (bởi Dennis Ritchie Ref )
-E
bản thân nó có thể ban đầu đến từ bash
(CWRU / CWRU.chlog trong phiên bản 1.13.5 đề cập Brian Fox thêm vào ngày 1992-10-18, GNU echo
sao chép nó ngay sau đó trong sh-utils-1.8 được phát hành 10 ngày sau đó)
Mặc dù phần echo
tích hợp của sh
hoặc BSD đã hỗ trợ -e
kể từ ngày họ bắt đầu sử dụng trình bao Almquist cho nó vào đầu những năm 90, echo
tiện ích độc lập cho đến ngày nay không hỗ trợ nó ở đó ( FreeBSDecho
vẫn không hỗ trợ -e
, mặc dù nó hỗ trợ -n
như Unix V7 (và cũng có \c
nhưng chỉ ở phần cuối của đối số cuối cùng)).
Việc xử lý -e
được bổ sung vào ksh93
's echo
khi trong BSD vũ trụ trong phiên bản ksh93r phát hành vào năm 2006 và có thể được vô hiệu hóa tại thời gian biên dịch.
4. GNU echo thay đổi hành vi trong 8.31
Kể từ coreutils 8,31 (và cam kết này ), GNU echo
bây giờ mở rộng chuỗi escape theo mặc định khi POSIXLY_CORRECT là trong môi trường, để phù hợp với hành vi của bash -o posix -O xpg_echo
's echo
BUILTIN (xem báo cáo lỗi ).
5. macOS echo
Hầu hết các phiên bản macOS đã nhận được chứng nhận UNIX từ Opengroup .
Của họ sh
được xây dựng trong echo
tương thích như nó bash
(một phiên bản rất cũ) xây dựng với xpg_echo
kích hoạt theo mặc định, nhưng độc lập của họ echo
tiện ích không phải là. env echo -n
đầu ra không có gì thay vì -n<newline>
, env echo '\n'
đầu ra \n<newline>
thay vì <newline><newline>
.
Đó /bin/echo
là một từ FreeBSD ngăn chặn đầu ra dòng mới nếu đối số đầu tiên là -n
hoặc (kể từ năm 1995) nếu đối số cuối cùng kết thúc \c
, nhưng không hỗ trợ bất kỳ chuỗi dấu gạch chéo ngược nào khác được UNIX yêu cầu, thậm chí không \\
.
6. echo
triển khai có thể xuất nguyên văn dữ liệu tùy ý
Nói đúng ra, bạn cũng có thể đếm mà FreeBSD / MacOS /bin/echo
trên (không vỏ của họ echo
được xây dựng trong), nơi zsh
's echo -E - "$var"
hoặc yash
' s ECHO_STYLE=raw echo "$var"
( printf '%s\n' "$var"
) có thể được viết như sau:
/bin/echo "$var
\c"
Việc triển khai hỗ trợ -E
và -n
(hoặc có thể được cấu hình) cũng có thể thực hiện:
echo -nE "$var
"
Và zsh
's echo -nE - "$var"
( printf %s "$var"
) có thể được viết
/bin/echo "$var\c"
7. _AST_FEATURES
và ASTUNIVERSE
Điều _AST_FEATURES
này không có nghĩa là được thao tác trực tiếp, nó được sử dụng để tuyên truyền các cài đặt cấu hình AST trong suốt quá trình thực thi lệnh. Cấu hình được thực hiện thông qua astgetconf()
API (không có giấy tờ) . Bên trong ksh93
, getconf
nội dung (được kích hoạt bằng builtin getconf
hoặc bằng cách gọi command /opt/ast/bin/getconf
) là giao diện đểastgetconf()
Chẳng hạn, bạn sẽ làm gì builtin getconf; getconf UNIVERSE = att
để thay đổi UNIVERSE
cài đặt thành att
(gây ra echo
hành vi theo cách SysV trong số những thứ khác). Sau khi làm điều đó, bạn sẽ nhận thấy $_AST_FEATURES
biến môi trường chứa UNIVERSE = att
.
echo -e
?