Yeni soru için bu komut dosyası çalışır:
#!/bin/bash
f() { for i in $(seq "$((RANDOM % 3 ))"); do
echo;
done; return $((RANDOM % 256));
}
exact_output(){ out=$( $1; ret=$?; echo x; exit "$ret" );
unset OldLC_ALL ; [ "${LC_ALL+set}" ] && OldLC_ALL=$LC_ALL
LC_ALL=C ; out=${out%x};
unset LC_ALL ; [ "${OldLC_ALL+set}" ] && LC_ALL=$OldLC_ALL
printf 'Output:%10q\nExit :%2s\n' "${out}" "$?"
}
exact_output f
echo Done
Yürütme sırasında:
Output:$'\n\n\n'
Exit :25
Done
Daha uzun açıklama
POSIX mermilerinin çıkarılmasıyla ilgili olağan bilgelik \n:
ekle x
s=$(printf "%s" "${1}x"); s=${s%?}
Son yeni hattı (çünkü gerekli olan S ) başına komut genişlemesi ile çıkarılır POSIX tarifname :
yer değiştirmenin sonunda bir veya daha fazla karakterin dizilerinin kaldırılması.
Bir iz hakkında x.
Bu soruda, bir xkodlamanın bazı karakterlerin sondaki baytı ile karıştırılabileceği söylenmiştir . Ancak, bazı dillerde kodlamanın hangi dilde veya hangi karakterin daha iyi olduğunu nasıl tahmin edeceğiz, en azından söylemek gerekirse, bu zor bir öneri.
Ancak; Bu sadece yanlış .
İzlememiz gereken tek kural, tam olarak kaldırdığımızı eklemektir .
Mevcut bir dizeye (veya bayt dizisine) bir şey eklersek ve daha sonra tamamen aynı şeyi kaldırırsak , orijinal dizenin (veya bayt dizisinin) aynı olması gerektiğini anlamak kolay olmalıdır .
Nerede yanlış gidiyoruz? Biz ne zaman karıştırmak karakterleri ve bayt .
Bir bayt eklersek, bir baytı kaldırmalıyız, bir karakter eklersek aynı karakteri kaldırmamız gerekir .
İkinci seçenek, bir karakter eklemek (ve daha sonra aynı karakteri kaldırmak) kıvrık ve karmaşık hale gelebilir ve evet, kod sayfaları ve kodlamalar engel olabilir.
Bununla birlikte, ilk seçenek oldukça mümkündür ve açıkladıktan sonra basit hale gelecektir.
Bir bayt, bir ASCII baytı (<127) ekleyelim ve işleri mümkün olduğunca daha az kıvrımlı tutmak için az aralığında ASCII karakteri olduğunu varsayalım. Ya da söylememiz gerektiği gibi, onaltılık aralıktaki bir bayt 0x61- 0x7a. Bunlardan herhangi birini seçelim, belki bir x (gerçekten bir değer bayt 0x78). Böyle bir baytı bir dizeye x ile birleştirerek ekleyebiliriz (varsayalım é):
$ a=é
$ b=${a}x
Dizeye bayt dizisi olarak bakarsak, şunu görürüz:
$ printf '%s' "$b" | od -vAn -tx1c
c3 a9 78
303 251 x
X ile biten dize sırası.
Bu x'i (bayt değeri 0x78) kaldırırsak :
$ printf '%s' "${b%x}" | od -vAn -tx1c
c3 a9
303 251
Sorunsuz çalışır.
Biraz daha zor bir örnek.
İlgilendiğimiz dize bayt ile biter 0xc3:
$ a=$'\x61\x20\x74\x65\x73\x74\x20\x73\x74\x72\x69\x6e\x67\x20\xc3'
Ve bir bayt değer ekleyelim 0xa9
$ b=$a$'\xa9'
Dize şimdi bu oldu:
$ echo "$b"
a test string é
Tam olarak istediğim gibi, son iki bayt utf8'de bir karakterdir (böylece herkes bu sonuçları utf8 konsolunda çoğaltabilir).
Bir karakteri kaldırırsak, orijinal dize değiştirilir. Ama eklediğimiz bu değil, x olarak yazılan bir bayt değeri ekledik, ama yine de bir bayt.
Baytları karakter olarak yanlış yorumlamaktan kaçınmamız gerekenler. İhtiyacımız olan, kullandığımız baytı kaldıran bir eylem 0xa9. Aslında, kül, bash, lksh ve mksh tam olarak bunu yapıyor gibi görünüyor:
$ c=$'\xa9'
$ echo ${b%$c} | od -vAn -tx1c
61 20 74 65 73 74 20 73 74 72 69 6e 67 20 c3 0a
a t e s t s t r i n g 303 \n
Ama ksh veya zsh değil.
Bununla birlikte, bunu çözmek çok kolaydır, tüm bu kabuklara bayt kaldırma işlemini söyleyelim :
$ LC_ALL=C; echo ${b%$c} | od -vAn -tx1c
işte bu, test edilen tüm mermiler (yash hariç) (ipin son kısmı için):
ash : s t r i n g 303 \n
dash : s t r i n g 303 \n
zsh/sh : s t r i n g 303 \n
b203sh : s t r i n g 303 \n
b204sh : s t r i n g 303 \n
b205sh : s t r i n g 303 \n
b30sh : s t r i n g 303 \n
b32sh : s t r i n g 303 \n
b41sh : s t r i n g 303 \n
b42sh : s t r i n g 303 \n
b43sh : s t r i n g 303 \n
b44sh : s t r i n g 303 \n
lksh : s t r i n g 303 \n
mksh : s t r i n g 303 \n
ksh93 : s t r i n g 303 \n
attsh : s t r i n g 303 \n
zsh/ksh : s t r i n g 303 \n
zsh : s t r i n g 303 \n
Sadece bu kadar basit, tüm bayt değerleri için tam olarak bir bayt olan bir LC_ALL = C karakterini kaldırmak için kabuk anlatmak 0x00için 0xff.
Yorumlar için çözüm:
Yorumlarda tartışılan örnek için, (zsh'de başarısız olan) olası bir çözüm:
#!/bin/bash
LC_ALL=zh_HK.big5hkscs
a=$(printf '\210\170');
b=$(printf '\170');
unset OldLC_ALL ; [ "${LC_ALL+set}" ] && OldLC_ALL=$LC_ALL
LC_ALL=C ; a=${a%"$b"};
unset LC_ALL ; [ "${OldLC_ALL+set}" ] && LC_ALL=$OldLC_ALL
printf '%s' "$a" | od -vAn -c
Bu kodlama sorununu ortadan kaldıracaktır.
$IFS, bu yüzden argüman olarak ele alınmayacaktır.