Come riecheggiare un botto!


59

Ho provato a creare uno script echoinserendo il contenuto in un file, invece di aprirlo con un editor

echo -e "#!/bin/bash \n /usr/bin/command args"  > .scripts/command

L' output :

bash:! / bin / bash: evento non trovato

Ho isolato questo strano comportamento al botto .

$ echo !
!  

$ echo "!"
bash: !: event not found

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • Perché il botto sta causando questo?
  • Quali sono questi "eventi" a cui si riferisce bash?
  • Come posso superare questo problema e stampare "#! / Bin / bash" sullo schermo o sul mio file?

Risposte:


59

Prova a usare virgolette singole.

echo -e '#!/bin/bash \n /usr/bin/command args'  > .scripts/command

echo '#!'

echo '#!/bin/bash'

Il problema si verifica perché bash sta cercando la sua cronologia per! / Bin / bash. L'uso di virgolette singole evita questo comportamento.


3
Disabilita anche l'interpolazione di stringhe :(
Hubro

Questo è il punto! È possibile aggiungere altri argomenti allo stesso comando echo usando le virgolette doppie e ottenere l'interpolazione su quelle parti.
Richm,

3
puoi anche usare una stringa in stile C in bash con $''. Ad esempio, echo $'1!\n2!\n3!\n'stampa ogni numero seguito da un botto e una nuova riga.
spelufo,

1
Mettere il botto tra virgolette singole non mi ha aiutato a inserire una stringa a più righe:! bash: le virgolette singole non sfuggono al botto (per davvero)
binki

1
@kbolino Hai ragione. Quindi la stringa di esempio completa sarebbe:, echo '0123'"$var"'456'nota due coppie di virgolette singole e una coppia di virgolette doppie.
phyatt,

16

Come ha detto Richm , bash sta cercando di fare una partita storica . Un altro modo per evitarlo è semplicemente sfuggire al botto con un \:

$ echo \#\!/bin/bash
#!/bin/bash

Attenzione però che tra virgolette doppie \non viene rimosso:

$ echo "\!"
\!

8
In bash, "\!"effettivamente espande \!, non !, presumibilmente per compatibilità POSIX.
Mikel,

@Mikel Sigh. Tante differenze tra zsh e bash
Michael Mrozek

Funziona anche quando si inserisce una stringa multilinea. Ricordare di essere 1. fuori da una stringa quando si entra nel botto e 2. usare questo metodo (barra rovesciata) sembra funzionare con la minima sorpresa nella maggior parte degli scenari.
binki,

1
Sarebbe utile notare che bash non esegue ricerche cronologiche durante l'esecuzione di uno script, quindi non c'è bisogno di fare nulla di speciale lì.
binki,

Non c'è sintassi per sfuggire semplicemente alle !doppie virgolette senza alcun comportamento magico? Questo è pazzesco ...
Lassi,

13

!avvia una sostituzione della cronologia (un "evento" è una riga nella cronologia dei comandi); ad esempio si !lsespande fino all'ultima riga di comando contenente lse si !?fooespande fino all'ultima riga di comando contenente foo. Puoi anche estrarre parole specifiche (ad es. Si !!:1riferisce alla prima parola del comando precedente) e altro; vedere il manuale per i dettagli.

Questa funzione è stata inventata per richiamare rapidamente i comandi precedenti nei giorni in cui l'edizione della riga di comando era primitiva. Con le shell moderne (almeno bash e zsh) e copia e incolla, l'espansione della cronologia non è così utile come una volta - è ancora utile, ma puoi farcela senza di essa.

Puoi cambiare quale personaggio innesca la sostituzione della cronologia impostando la histcharsvariabile; se si utilizza raramente la sostituzione della cronologia, è possibile impostare ad esempio in histchars='¡^'modo da ¡attivare l'espansione della cronologia anziché !. Puoi anche disattivare la funzione del tutto con set +o histexpand.


3

Per poter disabilitare l'espansione della cronologia su una particolare riga di comando, è possibile utilizzare spacecome terzo carattere di $histchars:

histchars='!^ '

Quindi, se si immette il comando con uno spazio iniziale, l'espansione della cronologia non verrà eseguita.

bash-4.3$ echo "#!/bin/bash"
bash: !/bin/bash: event not found
bash-4.3$  echo "#!/bin/bash"
#!/bin/bash

Si noti tuttavia che gli spazi iniziali vengono utilizzati anche quando $HISTCONTROLcontengono ignorespacecome un modo per dire di bashnon registrare una riga di comando nella cronologia.

Se desideri entrambe le funzionalità in modo indipendente, dovrai scegliere un altro personaggio come terzo personaggio di $histchars. Ne vuoi uno che non influisca sul modo in cui il tuo comando viene interpretato. Alcune opzioni:

  • usando backslash ( \): \echo foofunziona ma ha l'effetto collaterale di disabilitare alias o parole chiave.
  • TAB: per inserirlo in prima posizione, è necessario premere Ctrl+VTabperò.
  • se non ti dispiace digitando due chiavi, è possibile scegliere qualsiasi carattere che normalmente non appare in prima posizione ( %, @, ?, scegliere il proprio) e fa un alias vuoto per esso:

    histchars='!^%'
    alias %=
    

    Quindi inserisci quel personaggio, lo spazio e il tuo comando:

    bash-4.3$ % echo !!
    !!
    

(non sarai in grado di non registrare un comando in cui la sostituzione della cronologia è stata disabilitata comunque. Nota anche che il terzo carattere predefinito $histcharsè in #modo che l'espansione della cronologia non venga eseguita nei commenti. Se la modifichi e inserisci i commenti nella prompt, dovresti essere consapevole del fatto che le sequenze! possono essere espanse lì).


2

Le soluzioni proposte non funzionano, ad esempio nel seguente esempio:

$ bash -c "echo 'hello World!'"
-bash: !'": event not found
$

In questo caso il botto può essere stampato usando il suo codice ASCII ottale:

$ bash -c "echo -e 'hello World\0041'"
hello World!
$

1
Nel tuo primo comando, !è tra virgolette doppie, dove come altre risposte indicano che conserva il significato di espansione della cronologia. Puoi usare bash -c 'echo '\''hello World!'\'o bash -c "echo 'hello World"\!"'".
Gilles 'SO- smetti di essere malvagio' il

Senza il parametro -i, l'ambiente potrebbe essere modificato (come se il tuo file ~ / .profile non fosse eseguito). Tuttavia, facendo -i significa che qualsiasi output dal tuo .profile verrà catturato nell'output del comando che vuoi effettivamente eseguire.
Brent,

-1

Da Bash 4.3, ora puoi usare le virgolette doppie per citare il carattere di espansione della cronologia:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!

4
No, questo è solo !seguito da "quello non è più speciale. echo "foo!"va bene, echo "foo!bar"no
Stéphane Chazelas,
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.