Bagaimana saya bisa memeriksa karakter kata demi kata dari string perintah bash?


15

Saya memiliki perilaku aneh pagi ini di terminal bash:

user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
bash: [: missing «]»
user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
true
  • Perintah pertama disisipkan dari skrip yang diedit dengan gedit.
  • Yang kedua diketik langsung di terminal.

Setelah beberapa penggalian, saya menemukan bahwa menghapus karakter ke-30 (ruang antara client.conf dan "]") dan menggantinya dengan spasi membuat perintah berfungsi lagi.

Asumsi saya benar: karakter kosong yang tidak dikenal telah menyelinap ke dalam perintah , tetapi pertanyaannya adalah:

  1. Bagaimana saya bisa mengungkapkan karakter-karakter itu di terminal sehingga saya bisa men-debug perintah? Dan yang lebih penting:
  2. Bagaimana saya bisa mencegah hal ini terjadi lagi?

BTW, saya menjalankan Ubuntu 18.04 / Bahasa Perancis, skrip yang saya tempelkan perintah dari dalam drive USB dan mungkin telah diedit pada Windows juga.


Terima kasih atas jawaban Anda yang sangat bagus. Karakter buruknya adalah karakter UTF-8 ruang bebas c2 a0 . Pertanyaan Bagaimana menghapus karakter 'M-BM-' khusus dengan sed memiliki fakta menarik tentang karakter itu.

Yang aneh adalah naskahnya bebas dari karakter ini. Jadi saya tidak tahu dari mana asalnya.


3
Gunakan editor yang menyoroti karakter tersebut. Penyorotan sintaks juga banyak membantu. Jangan pernah menempel langsung dari web ke terminal, selalu melalui editor yang disebutkan di atas.
choroba

2
Anda mungkin ingin menemukan perintah masalah di daftar riwayat Anda, lalu menyalurkan output melalui program tampilan hex. Agar Anda tidak perlu mengarungi daftar panjang, jalankan kembali perintah untuk meletakkannya di bagian bawah daftar riwayat dan jalankan history 2|xxd(karena historyperintah itu sendiri selalu yang terakhir dalam daftar), atau ketik history|grep "CommandWithProblem"|xxd. Anda dapat menggunakan program tampilan hex lain, bukan xxd, tetapi ini default ke format yang saya suka.
AFH

@Gabriel Glenn, tandai jawaban terbaik / paling membantu / apa pun sebagai " diterima " menggunakan centang - daripada mengomentari setiap jawaban yang membantu. info
Attie

1
@ Attie, Ya saya akan, saya biasanya menunggu 24 jam sebelum menerima jawaban terbaik, seperti yang disarankan dalam: meta.stackexchange.com/questions/5234/…
Gabriel Glenn

1
Secara pribadi saya akan menggunakan set -x. Ini akan menunjukkan kepada Anda perintah & bagaimana itu dibagi. Itu tidak selalu mengatakan "karakter buruk di sini", tetapi itu akan menunjukkan kepada Anda bahwa bash tidak terpecah pada karakter itu.
Patrick

Jawaban:


11

Satu opsi adalah melihat karakter yang Anda coba gunakan dengan hex viewer atau editor. hexdumpadalah pilihan yang baik jika Anda terbatas pada terminal.

$ hexdump -Cv <<"EOF"
> [ -f /etc/openvpn/client.conf ] && echo true
> EOF
00000000  5b 20 2d 66 20 2f 65 74  63 2f 6f 70 65 6e 76 70  |[ -f /etc/openvp|
00000010  6e 2f 63 6c 69 65 6e 74  2e 63 6f 6e 66 20 5d 20  |n/client.conf ] |
00000020  26 26 20 65 63 68 6f 20  74 72 75 65 0a           |&& echo true.|
0000002d

Anda bisa lihat di sini bahwa space, close-square-brace, spacebenar - 0x20, 0x5D, 0x20.

Nilai-nilai ini adalah kode ASCII, ditampilkan dalam heksadesimal . Nilai apa pun di luar rentang 0x20- 0x7Ebukan " karakter yang dapat dicetak " sejauh yang menyangkut ASCII, dan kemungkinan besar tidak akan cocok dengan antarmuka baris perintah.

Catatan: Saya menyalin baris " rusak " pertama Anda untuk digunakan dalam hexdumpcontoh di atas, jadi sesuatu telah menggantikan ruang bukan-ASCII dengan ruang ASCII antara sumber asli Anda dan pertanyaan yang Anda buat.


Untuk mengulangi ini, lakukan langkah-langkah berikut:

  1. Ketik hexdump -Cv <<"EOF"dan tekanEnter
  2. Rekatkan teks yang ingin Anda gunakan
  3. Ketik EOFbarisnya sendiri, dan tekanEnter

Terminal dan Antarmuka Baris Perintah tidak menangani karakter khusus dengan baik - seperti yang telah Anda temukan. Jika Anda tidak terlalu berhati-hati dalam memformat dokumen, Anda juga akan memiliki masalah dengan Microsoft Word (dan lain-lain) menggunakan " kutipan pintar ", em-strip, daftarnya berlanjut ...

Temukan perbedaan: (bagian atas adalah " kutipan pintar ", bagian bawah adalah " kutipan langsung ")

contoh kutipan pintar vs kutipan lurus

$ hexdump -Cv <<"EOF"
> quoted string
> EOF
00000000  e2 80 9c 71 75 6f 74 65  64 20 73 74 72 69 6e 67  |...quoted string|
00000010  e2 80 9d 0a                                       |....|
00000014

Di sini, tanda kutip terbuka bukan ASCII kutipan sederhana ( "), tetapi adalah Unicode / UTF-8 series - 0xE2, 0x80, 0x9C, atau U+201C- yang terminal tidak akan menangani seperti yang Anda harapkan.

Saran Kiwy cat -Ajuga melakukan pekerjaan:

$ cat -A <<"EOF"
> quoted string
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$

Catatan: saat menggunakanecho "..." | hd, Anda memiliki peluang bahwa bash akan mengganti bagian-bagian dari string yang Anda coba periksa. Ini khususnya menjadi perhatian ketika mencoba memeriksa komponen-komponen naskah.

Misalnya, coba:

$ echo "${USER}"
attie

$ echo "`whoami`"
attie

$ echo "$(whoami)"
attie

$ cat <<EOF
> ${USER}
> EOF
attie

Metode ini mengganti komponen dengan teks yang relevan. Untuk menghindari ini, gunakan salah satu pendekatan berikut. Perhatikan penggunaan tanda kutip tunggal ( '), dan " heredoc yang dikutip " ( "EOF").

$ echo '${USER}'
${USER}

$ echo '`whoami`'
`whoami`

$ echo '$(whoami)'
$(whoami)

$ cat <<"EOF"
> ${USER}
> EOF
${USER}

Solusi ini berfungsi: echo "[ -f /etc/openvpn.ovpn ]" | hd mengembalikan [...] c2 a0 [...]. Kita dapat melihat ruang non-breaking karakter c2 a0 UT-8
Gabriel Glenn

18

Anda bisa menggunakan catdengan -Apilihan: dari manual:

   -A, --show-all
          equivalent to -vET
   -E, --show-ends
          display $ at end of each line
   -T, --show-tabs
          display TAB characters as ^I
   -v, --show-nonprinting
          use ^ and M- notation, except for LFD and TAB

Jadi cat -A yourscrip.shakan menunjukkan karakter yang tidak terlihat dan aneh.


7
Solusi ini berfungsi: echo "[ -f /etc/openvpn.ovpn ]" | cat -Amengembalikan [ -f /etc/openvpn/client.ovpnM-BM- ]$. Kita dapat melihat ruang bebas-pecah karakter M-BM- UT-8
Gabriel Glenn

@GabrielGlenn senang ini membantu Anda.
Kiwy

9

echo "<your command>" | hdharus bekerja. Cari backspace (0x08) atau karakter dengan kode> = 80. echo "<your command>" | wc -bdan memeriksa apakah hitungannya sesuai dengan yang Anda lihat juga merupakan ide bagus.

Menyalin hal-hal dari file yang diproduksi oleh apa pun dengan "Office" di namanya berbahaya, karena perangkat lunak seperti itu sering mengambil kebebasan untuk mengganti karakter: dalam bahasa Prancis, cari tanda kutip ganda digantikan oleh "guillemets", dalam bahasa Inggris untuk tanda kutip polos digantikan oleh mereka buka / tutup setara. Yang paling sulit yang pernah saya temukan adalah 0-lebar ruang tanpa putus di tengah nama file (3 hari downtime server ...).


2
Layak disebutkan hdpendek hexdumpyang juga disebutkan dalam jawaban Attie.
Mikael Kjær

@ MikaelKjær - Di Ubuntu, hdsetara dengan hexdump -C.
AFH

1
@xenoid: Saya bilang 'diedit di Windows', tidak diedit dengan Office Writer, kami tidak gila;). Jika diedit, itu dengan Notepad ++.
Gabriel Glenn

1
Solusi ini berfungsi: echo "[ -f /etc/openvpn.ovpn ]" | hd mengembalikan [...] c2 a0 [...]. Kita dapat melihat ruang non-breaking karakter c2 a0 UT-8
Gabriel Glenn

2

Bash, dan shell lain seperti zsh, dapat membuka baris perintah saat ini di editor. Pintasan default untuk bash adalah C-x C-e( CtrlX CtrlE), dan terbuka di pertama tersedia $VISUAL, $EDITORdan emacs. Dalam praktiknya ini sangat berharga untuk debugging dan memodifikasi perintah kompleks. Bergantung pada bagaimana Anda melihatnya, zsh lebih ramah daripada bash di sini: ketika editor keluar, bash segera menjalankan perintah, sedangkan zsh menunggu Anda untuk menekan Enter(memberi Anda lebih banyak peluang untuk mengedit perintah).

Setelah membuka perintah dalam editor, Anda dapat mengonfigurasi editor Anda untuk menunjukkan karakter non-ASCII secara berbeda.

Misalnya, dengan Vim , menggunakan pengaturan ini:

set encoding=latin1
set isprint=
set display+=uhex

masukkan deskripsi gambar di sini

Atau, mengadaptasi metode jawaban lain:

bash-4.4$ f() { cat -A "$@"; false; }   # exit false to prevent bash from running the command
bash-4.4$ VISUAL=f
bash-4.4$ [ -f /etc/openvpn/client.conf ] && echo true  # C-x C-e here
[ -f /etc/openvpn/client.confM-BM- ] && echo true$
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.