Tylko dodatkowa uwaga na podstawie dobrej odpowiedzi @ Kusalananda .
echo run after_bundle
jest w porządku, ponieważ żaden z tych 3 argumentów¹ nie przekazał echo
znaków specjalnych dla powłoki.
I (dodatkowy punkt, o którym chcę tu wspomnieć) nie ma ustawień regionalnych systemu, w których bajty te mogłyby zostać przetłumaczone na znaki specjalne dla powłoki.
Wszystkie te znaki są w tym, co POSIX nazywa przenośnym zestawem znaków . Znaki te powinny być obecne i zakodowane tak samo we wszystkich zestawach znaków w systemie POSIX².
Tak więc wiersz poleceń będzie interpretowany tak samo niezależnie od ustawień regionalnych.
Teraz, jeśli zaczniemy używać znaków spoza tego przenośnego zestawu znaków, dobrze jest zacytować je, nawet jeśli nie są one specjalne dla powłoki, ponieważ w innym ustawieniu narodowym bajty, które je tworzą, mogą być interpretowane jako różne znaki, które mogłyby specjalne dla powłoki. Zauważ, że chodzi o to, czy używasz echo
czy jakiejkolwiek innej komendy, problem nie dotyczy, echo
ale sposobu, w jaki powłoka analizuje swój kod.
Na przykład w UTF-8:
echo voilà | iconv -f UTF-8 -t //TRANSLIT
To à
jest zakodowane jako 0xc3 0xa0. Teraz, jeśli masz ten wiersz kodu w skrypcie powłoki, a skrypt powłoki jest wywoływany przez użytkownika, który używa ustawień regionalnych, których zestaw znaków nie jest UTF-8, te dwa bajty mogą tworzyć bardzo różne znaki.
Na przykład w fr_FR.ISO8859-15
ustawieniach narodowych typowe ustawienia francuskie używające standardowego jednobajtowego zestawu znaków, który obejmuje język francuski (taki sam, jaki stosuje się w większości języków Europy Zachodniej, w tym angielskiego), bajt 0xc3 jest interpretowany jako Ã
znak, a 0xa0 jako inny niż łamanie spacji.
W niektórych systemach, takich jak NetBSD³, to nieprzerwane miejsce jest traktowane jako pusty znak ( isblank()
po zwróceniu wartości true, jest dopasowywany [[:blank:]]
) i powłoki w ten bash
sposób traktują go jako ogranicznik tokenów w swojej składni.
Oznacza to, że zamiast uruchamiać echo
z $'voil\xc3\xa0'
argumentem, uruchamiają go z $'voil\xc3'
argumentem, co oznacza, że nie zostanie wydrukowany voilà
poprawnie.
To staje się dużo gorzej z chińskich zestawów znaków, takich jak BIG5, BIG5-HKSCS, GB18030, GBK, które mają wiele postaci, których kodowanie zawiera tego samego kodowania jak |
, `
, \
(aby wymienić najgorszy) (również ten śmieszny SJIS, aka Microsoft Kanji, z wyjątkiem że jest ¥
zamiast \
, ale nadal jest traktowany jak \
większość narzędzi, ponieważ jest tam zakodowany jako 0x5c).
Na przykład, jeśli w zh_CN.gb18030
chińskim języku, piszesz skrypt taki jak:
echo 詜 reboot
Skrypt ten wyświetli się 詜 reboot
w lokalizacji używającej GB18030 lub GBK, 唰 reboot
w lokalizacji używającej BIG5 lub BIG5-HKSCS, ale w lokalizacji C używającej ASCII lub lokalizacji używającej ISO8859-15 lub UTF-8, spowoduje reboot
uruchomienie, ponieważ kodowanie GB18030 of 詜
to 0xd4 0x7c, a 0x7c to kodowanie |
w ASCII, więc kończymy:
echo �| reboot
(ten reprezentujący jednak bajt 0xd4 jest renderowany w ustawieniach regionalnych). Przykład użycia mniej szkodliwego uname
zamiast reboot
:
$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$
( uname
był prowadzony).
Tak więc radzę zacytować wszystkie ciągi zawierające znaki spoza przenośnego zestawu znaków.
Należy jednak pamiętać, że ponieważ kodowanie \
i `
znajdują się w kodowaniu niektórych z tych znaków, lepiej nie używać \
lub "..."
lub $'...'
(wewnątrz których `
i / lub \
nadal są specjalne), a '...'
zamiast tego cytować znaki spoza przenośnego zestawu znaków.
Nie znam żadnego systemu, który ma ustawienia narodowe, w których zestaw znaków ma dowolny znak ( '
oczywiście inny niż sam), którego kodowanie zawiera kodowanie '
, więc te '...'
powinny być zdecydowanie najbezpieczniejsze.
Zauważ, że kilka powłok obsługuje również $'\uXXXX'
zapis do wyrażania znaków na podstawie ich punktu kodowego Unicode. W powłokach takich jak zsh
i bash
znak jest wstawiany zakodowany w zestawie znaków regionu (chociaż może powodować nieoczekiwane zachowania, jeśli ten zestaw znaków nie ma tego znaku). Pozwala to uniknąć wstawiania znaków innych niż ASCII do kodu powłoki.
Tak więc powyżej:
echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'
Lub:
echo $'voil\u00e0'
echo $'\u8a5c reboot'
(z zastrzeżeniem może on uszkodzić skrypt, gdy zostanie uruchomiony w lokalizacjach, które nie mają tych znaków).
Lub lepiej, ponieważ \
jest również specjalny dla echo
(lub przynajmniej niektórych echo
implementacji, przynajmniej tych zgodnych z Uniksem):
printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'
(zauważ, że \
jest to również specjalne w pierwszym argumencie do printf
, więc lepiej jest unikać znaków spoza ASCII, na wypadek, gdyby mogły zawierać kodowanie \
).
Pamiętaj, że możesz także:
'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'
(byłoby to przesadą, ale mogłoby ci dać spokój, jeśli nie jesteś pewien, które postacie znajdują się w przenośnym zestawie znaków)
Upewnij się także, aby nigdy nie używać starożytnej `...`
formy zastępowania poleceń (która wprowadza kolejny poziom przetwarzania odwrotnego ukośnika), ale używaj $(...)
zamiast tego.
¹ technicznie, echo
jest również przekazywana jako argument do echo
narzędzia (aby poinformować go, jak został wywołany), to jest to argv[0]
i argc
to 3, chociaż w większości powłok obecnie echo
jest wbudowane, tak że exec()
z /bin/echo
pliku z listy 3 argumentów jest symulowane przez muszla. Często uważa się, że lista argumentów zaczyna się od drugiej ( argv[1]
do argv[argc - 1]
), ponieważ to na nią głównie działają polecenia.
² znaczący wyjątek od tej niedorzecznej ja_JP.SJIS
lokalizacji systemów FreeBSD, których zestaw znaków nie ma \
ani ~
charakteru!
³ zauważ, że podczas gdy wiele systemów (FreeBSD, Solaris, a nie GNU) uważa U + 00A0 za lokalizację [[:blank:]]
w ustawieniach UTF-8, nieliczne robią to w innych ustawieniach, takich jak ISO8859-15, być może w celu uniknięcia tego rodzaju problemów.