Làm cách nào tôi có thể kiểm tra các ký tự nguyên văn của chuỗi lệnh bash?


15

Tôi đã có hành vi kỳ lạ sáng nay trong một bash terminal:

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
  • Lệnh đầu tiên đã được dán từ một tập lệnh được chỉnh sửa bằng gedit.
  • Cái thứ hai được gõ trực tiếp trong thiết bị đầu cuối.

Sau khi đào bới, tôi phát hiện ra rằng việc xóa ký tự thứ 30 (khoảng trắng giữa client.conf và "]") và thay thế nó bằng một khoảng trắng làm cho lệnh hoạt động trở lại.

Giả định của tôi là đúng: một ký tự trống không xác định đã trượt vào lệnh , nhưng câu hỏi là:

  1. Làm thế nào tôi có thể tiết lộ các ký tự trong thiết bị đầu cuối để tôi có thể gỡ lỗi lệnh? Và quan trọng hơn:
  2. Làm thế nào tôi có thể ngăn chặn điều này xảy ra một lần nữa?

BTW, tôi chạy Ubuntu 18.04 / ngôn ngữ tiếng Pháp, tập lệnh mà tôi dán lệnh từ trong ổ USB và có thể cũng đã được chỉnh sửa trên Windows.


Cảm ơn bạn cho câu trả lời rất tốt của bạn. Ký tự xấu là ký tự UTF-8 không gian không phá vỡ c2 a0 . Câu hỏi Làm thế nào để loại bỏ ký tự 'M-BM-' đặc biệt với sed có một sự thật thú vị về nhân vật đó.

Điều kỳ lạ là kịch bản không có nhân vật này. Vì vậy, tôi không biết nó đến từ đâu.


3
Sử dụng một trình soạn thảo làm nổi bật các nhân vật như vậy. Làm nổi bật cú pháp cũng giúp rất nhiều. Không bao giờ dán trực tiếp từ web vào thiết bị đầu cuối, luôn luôn đi qua trình chỉnh sửa nói trên.
choroba

2
Bạn có thể muốn tìm lệnh vấn đề trong danh sách lịch sử của mình, sau đó chuyển đầu ra thông qua chương trình hiển thị hex. Vì vậy, bạn không cần phải lội qua một danh sách dài, hãy chạy lại lệnh để đặt nó ở cuối danh sách lịch sử và chạy history 2|xxd(vì historychính lệnh luôn luôn là cuối cùng trong danh sách) hoặc gõ history|grep "CommandWithProblem"|xxd. Bạn có thể sử dụng bất kỳ chương trình hiển thị hex nào khác thay thế xxd, nhưng điều này mặc định là định dạng tôi thích.
AFH

@Gabriel Glenn, vui lòng đánh dấu câu trả lời tốt nhất / hữu ích nhất / bất cứ điều gì là " được chấp nhận " bằng cách sử dụng đánh dấu - thay vì nhận xét về từng câu trả lời có ích. Thông tin
Attie

1
@Attie, Có, tôi sẽ, tôi thường đợi 24 giờ trước khi chấp nhận câu trả lời hay nhất, như được đề xuất trong: meta.stackexchange.com/questions/5234/ Lỗi
Gabriel Glenn

1
Cá nhân tôi sẽ sử dụng set -x. Điều này sẽ cho bạn thấy lệnh và cách phân chia. Nó không nhất thiết phải nói "nhân vật xấu ở đây", nhưng nó sẽ cho bạn thấy rằng bash không phân chia nhân vật đó.
Patrick

Câu trả lời:


11

Một tùy chọn là xem xét các ký tự bạn đang cố sử dụng với trình xem hex hoặc trình chỉnh sửa. hexdumplà một lựa chọn tốt nếu bạn bị giới hạn trong thiết bị đầu cuối.

$ 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

Bạn có thể thấy ở đây rằng space, close-square-brace, spacelà chính xác - 0x20, 0x5D, 0x20.

Các giá trị này là mã ASCII, được hiển thị dưới dạng thập lục phân . Bất kỳ giá trị nào nằm ngoài phạm vi 0x20- 0x7Ekhông phải là " ký tự có thể in " theo như ASCII có liên quan và rất có thể sẽ không chơi tốt với các giao diện dòng lệnh.

Lưu ý: Tôi đã sao chép dòng " bị hỏng " đầu tiên của bạn để sử dụng trong hexdumpví dụ trên, do đó, một cái gì đó đã thay thế không gian không phải là ASCII bằng một không gian ASCII giữa nguồn ban đầu của bạn và câu hỏi được hiển thị của bạn.


Để lặp lại điều này, hãy thực hiện các bước sau:

  1. hexdump -Cv <<"EOF"và bấmEnter
  2. Dán văn bản bạn muốn sử dụng
  3. Nhập EOFvào một dòng của riêng mình và nhấnEnter

Thiết bị đầu cuối và Giao diện dòng lệnh không xử lý tốt các ký tự đặc biệt - như bạn đã khám phá. Nếu bạn không cẩn thận với việc định dạng tài liệu, bạn cũng sẽ gặp vấn đề với Microsoft Word (và những người khác) khi sử dụng " dấu ngoặc kép thông minh ", dấu gạch ngang, danh sách sẽ tiếp tục ...

Phát hiện sự khác biệt: (trên cùng là " trích dẫn thông minh ", phía dưới là " trích dẫn thẳng ")

ví dụ về trích dẫn thông minh so với trích dẫn thẳng

$ 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

Ở đây, có dấu ngoặc kép mở không phải là một quote ASCII đơn giản ( "), nhưng là một Unicode / UTF-8 series - 0xE2, 0x80, 0x9C, hoặc U+201C- mà nhà ga sẽ không xử lý như bạn mong đợi.

Đề nghị của Kiwy cat -Acũng làm công việc:

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

Lưu ý: khi sử dụngecho "..." | hd, bạn có khả năng bash sẽ thay thế các phần của chuỗi bạn đang cố kiểm tra. Điều này đặc biệt quan tâm khi cố gắng kiểm tra các thành phần của một kịch bản.

Ví dụ thử:

$ echo "${USER}"
attie

$ echo "`whoami`"
attie

$ echo "$(whoami)"
attie

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

Các phương pháp này đang thay thế các thành phần bằng văn bản có liên quan. Để tránh điều này, sử dụng một trong các phương pháp sau. Lưu ý việc sử dụng dấu ngoặc đơn ( ') và " trích dẫn di truyền " ( "EOF").

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

$ echo '`whoami`'
`whoami`

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

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

Giải pháp này hoạt động: echo "[ -f /etc/openvpn.ovpn ]" | hd trả về [...] c2 a0 [...]. Chúng ta có thể thấy không gian không phá vỡ ký tự c2 a0 UT-8
Gabriel Glenn

18

Bạn có thể sử dụng catvới -Atùy chọn: từ hướng dẫn:

   -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

Vì vậy, cat -A yourscrip.shsẽ cho bạn thấy các nhân vật vô hình và lạ.


7
Giải pháp này hoạt động: echo "[ -f /etc/openvpn.ovpn ]" | cat -Atrả về [ -f /etc/openvpn/client.ovpnM-BM- ]$. Chúng ta có thể thấy không gian không phá vỡ ký tự M-BM- UT-8
Gabriel Glenn

@GabrielGlenn rất vui vì điều này đã giúp bạn.
Kiwy

9

echo "<your command>" | hdnên làm việc. Tìm kiếm backspace (0x08) hoặc các ký tự có mã> = 80. echo "<your command>" | wc -bvà kiểm tra xem số lượng phù hợp với những gì bạn nhìn thấy cũng là một ý tưởng tốt.

Sao chép nội dung từ các tệp được tạo bởi bất kỳ thứ gì có tên "Office" là nguy hiểm, bởi vì phần mềm như vậy thường có quyền tự do thay thế các ký tự: trong tiếng Pháp, hãy tìm các dấu ngoặc kép được thay thế bằng "guillemets", bằng tiếng Anh để thay thế dấu ngoặc kép tương đương mở / đóng. Cái khó nhất tôi từng thấy là một không gian không phá vỡ 0 chiều rộng ở giữa tên tệp (3 ngày ngừng hoạt động của máy chủ ...).


2
Điều đáng nói hdlà viết tắt hexdumpcũng được đề cập trong câu trả lời của Attie.
Mikael Kjær

@ MikaelKjær - Trên Ubuntu, hdtương đương với hexdump -C.
AFH

1
@xenoid: Tôi đã nói 'đã chỉnh sửa trên Windows', không được chỉnh sửa bằng Office Writer, chúng tôi không điên;). Nếu nó đã được chỉnh sửa, đó là với Notepad ++.
Gabriel Glenn

1
Giải pháp này hoạt động: echo "[ -f /etc/openvpn.ovpn ]" | hd trả về [...] c2 a0 [...]. Chúng ta có thể thấy không gian không phá vỡ ký tự c2 a0 UT-8
Gabriel Glenn

2

Bash và các shell khác như zsh, có thể mở dòng lệnh hiện tại trong trình soạn thảo. Phím tắt mặc định cho bash là C-x C-e( CtrlX CtrlE), và nó sẽ mở ra trong lần đầu tiên có sẵn của $VISUAL, $EDITORvà emacs. Trong thực tế, điều này là vô giá để gỡ lỗi và sửa đổi các lệnh phức tạp. Tùy thuộc vào cách bạn nhìn vào nó, zsh thân thiện hơn bash ở đây: khi trình soạn thảo thoát, bash chạy lệnh ngay lập tức, trong khi zsh chờ bạn nhấn Enter(cho bạn nhiều cơ hội hơn để chỉnh sửa lệnh).

Sau khi mở lệnh trong trình chỉnh sửa, bạn có thể định cấu hình trình soạn thảo của mình để hiển thị các ký tự không phải ASCII khác nhau.

Ví dụ: với Vim , sử dụng các cài đặt này:

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

nhập mô tả hình ảnh ở đây

Hoặc, điều chỉnh các phương pháp của câu trả lời khác:

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$
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.