Come posso controllare i caratteri testuali di una stringa di comando bash?


15

Ho avuto questo strano comportamento questa mattina in un 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
  • Il primo comando è stato incollato da uno script modificato con gedit.
  • Il secondo è stato digitato direttamente nel terminale.

Dopo qualche ricerca, scopro che la rimozione del 30 ° carattere (lo spazio tra client.conf e "]") e la sua sostituzione con uno spazio ha fatto funzionare nuovamente il comando.

La mia ipotesi era corretta: un personaggio sconosciuto sconosciuto è entrato nel comando , ma la domanda è:

  1. Come posso rivelare quei caratteri nel terminale in modo da poter eseguire il debug del comando? E più importante:
  2. Come posso evitare che ciò accada di nuovo?

A proposito, ho Ubuntu 18.04 / lingua francese, lo script da cui ho incollato il comando è in un'unità USB e potrebbe essere stato modificato anche su Windows.


Grazie per le tue ottime risposte. Il personaggio cattivo è un c2 a0 UTF-8 di spazio non-break . La domanda su come rimuovere il personaggio speciale 'M-BM-' con sed ha fatti interessanti su quel personaggio.

La cosa strana è che la sceneggiatura è libera da questo personaggio. Quindi non so da dove provenga.


3
Usa un editor che evidenzi tali personaggi. L'evidenziazione della sintassi aiuta anche molto. Non incollare mai direttamente dal web al terminale, passare sempre attraverso il suddetto editor.
Choroba,

2
Potresti voler trovare il comando problema nell'elenco della cronologia, quindi reindirizzare l'output tramite un programma di visualizzazione esadecimale. In modo che non sia necessario scorrere un lungo elenco, eseguire nuovamente il comando per posizionarlo in fondo all'elenco cronologico ed eseguire history 2|xxd(poiché il historycomando stesso è sempre l'ultimo nell'elenco), oppure digitare history|grep "CommandWithProblem"|xxd. È possibile utilizzare qualsiasi altro programma di visualizzazione esadecimale invece di xxd, ma l'impostazione predefinita è un formato che mi piace.
AFH,

@Gabriel Glenn, ti preghiamo di contrassegnare la risposta migliore / più utile / qualunque sia " accettata " usando il segno di spunta, piuttosto che commentare ciascuna cosa che la risposta ha aiutato. info
Attie

1
@Attie, sì, di solito aspetto 24 ore prima di accettare le risposte migliori, come suggerito in: meta.stackexchange.com/questions/5234/…
Gabriel Glenn,

1
Personalmente userei set -x. Questo ti mostrerebbe il comando e come è diviso. Non direbbe necessariamente "cattivo personaggio qui", ma ti mostrerebbe che bash non si stava dividendo su quel personaggio.
Patrick,

Risposte:


11

Un'opzione è guardare i personaggi che stai cercando di usare con un visualizzatore o un editor esadecimale. hexdumpè una buona opzione se si è limitati al terminale.

$ 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

Si può vedere che il space, close-square-brace, spacesono corretti - 0x20,0x5D , 0x20.

Questi valori sono codici ASCII, visualizzati in esadecimali . Qualsiasi valore al di fuori dell'intervallo 0x20- 0x7Enon è un " carattere stampabile " quanto riguarda ASCII, e molto probabilmente non giocherà bene con le interfacce della riga di comando.

Nota: ho copiato la tua prima linea " spezzata " per usarla hexdumpnell'esempio sopra, quindi qualcosa ha sostituito lo spazio non-ASCII con uno spazio ASCII tra la tua sorgente originale e la tua domanda di rendering.


Per ripetere ciò, procedi come segue:

  1. genere hexdump -Cv <<"EOF" e premiEnter
  2. Incolla il testo che desideri utilizzare
  3. Digitare EOFsu una riga a sé stante e premereEnter

Terminali e interfacce della riga di comando non gestiscono bene i caratteri speciali, come hai scoperto. Se non stai molto attento con la formattazione dei documenti, avrai anche problemi con Microsoft Word (e altri) usando " virgolette intelligenti " ", trattini, l'elenco continua ...

Trova la differenza: (la parte superiore è " virgolette intelligenti ", la parte inferiore è " virgolette semplici ")

esempio di virgolette intelligenti e virgolette semplici

$ 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

Qui, le virgolette aperti non sono una semplice citazione ASCII ( "), ma sono un Unicode / UTF-8 serie - 0xE2, 0x80, 0x9CoU+201C - che il terminale non gestirà, come ci si potrebbe aspettare.

Il suggerimento di Kiwy di fare cat -Aanche il lavoro:

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

Nota: durante l'utilizzoecho "..." | hd , è possibile che bash sostituisca parti della stringa che si sta tentando di ispezionare. Ciò è particolarmente preoccupante quando si tenta di ispezionare i componenti di uno script.

Ad esempio, prova:

$ echo "${USER}"
attie

$ echo "`whoami`"
attie

$ echo "$(whoami)"
attie

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

Questi metodi stanno sostituendo i componenti con il testo pertinente. Per evitare ciò, utilizzare uno dei seguenti approcci. Nota l'uso di virgolette singole ( ') e un " citato heredoc " ( "EOF").

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

$ echo '`whoami`'
`whoami`

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

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

Questa soluzione funziona: echo "[ -f /etc/openvpn.ovpn ]" | hd ritorna [...] c2 a0 [...]. Possiamo vedere lo spazio non-break del personaggio c2 a0 UT-8
Gabriel Glenn,

18

È possibile utilizzare catcon l' -Aopzione: dal manuale:

   -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

Quindi cat -A yourscrip.shti mostrerà personaggi invisibili e strani.


7
Questa soluzione funziona: echo "[ -f /etc/openvpn.ovpn ]" | cat -Aritorna [ -f /etc/openvpn/client.ovpnM-BM- ]$. Possiamo vedere lo spazio non-break del personaggio M-BM- UT-8
Gabriel Glenn,

@GabrielGlenn felice che questo ti abbia aiutato.
Kiwy,

9

echo "<your command>" | hddovrebbe funzionare. Cerca backspace (0x08) o caratteri con codici> = 80. echo "<your command>" | wc -be anche verificare che il conteggio corrisponda a quello che vedi è una buona idea.

Copiare roba da file prodotti da qualsiasi cosa con "Office" nel suo nome è pericoloso, perché tale software spesso si prende la libertà di sostituire i caratteri: in francese, cerca le doppie virgolette sostituite da "guillemets", in inglese le citazioni semplici sostituite dal loro apri / chiudi equivalenti. Il più difficile che abbia mai trovato è stato uno spazio non-breaking di larghezza 0 nel mezzo di un nome di file (3 giorni di inattività del server ...).


2
Vale la pena ricordare che hdè abbreviato per il hexdumpquale è anche menzionato nella risposta di Attie.
Mikael Kjær,

@ MikaelKjær - Su Ubuntu hdè equivalente a hexdump -C.
AFH,

1
@xenoid: ho detto 'modificato su Windows', non modificato con Office Writer, non siamo pazzi;). Se è stato modificato, è stato con Notepad ++.
Gabriel Glenn,

1
Questa soluzione funziona: echo "[ -f /etc/openvpn.ovpn ]" | hd ritorna [...] c2 a0 [...]. Possiamo vedere lo spazio non-break del personaggio c2 a0 UT-8
Gabriel Glenn,

2

Bash e altre shell come zsh, possono aprire l'attuale riga di comando in un editor. La scorciatoia predefinita per bash è C-x C-e( CtrlX CtrlE), e si apre nella prima disposizione di $VISUAL, $EDITORe emacs. In pratica questo è prezioso per il debug e la modifica di comandi complessi. A seconda di come lo guardi, zsh è più amichevole di bash qui: quando l'editor esce, bash esegue immediatamente il comando, mentre zsh ti aspetta di premere Enter(dandoti più possibilità di modificare il comando).

Dopo aver aperto il comando in un editor, puoi configurare i tuoi editor in modo che mostrino i caratteri non ASCII in modo diverso.

Ad esempio, con Vim , usando queste impostazioni:

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

inserisci qui la descrizione dell'immagine

Oppure, adattando i metodi delle altre risposte:

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$
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.