Jak echo huku!


59

Próbowałem utworzyć skrypt echo, umieszczając zawartość w pliku, zamiast otwierać go za pomocą edytora

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

Dane wyjściowe :

bash:! / bin / bash: nie znaleziono zdarzenia

Wyizolowałem to dziwne zachowanie z hukiem .

$ echo !
!  

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

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • Dlaczego powoduje to huk?
  • Do jakich „zdarzeń” odnosi się bash?
  • Jak obejść ten problem i wydrukować „#! / Bin / bash” na ekranie lub w moim pliku?

Odpowiedzi:


59

Spróbuj użyć pojedynczych cudzysłowów.

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

echo '#!'

echo '#!/bin/bash'

Problem występuje, ponieważ bash przeszukuje swoją historię w poszukiwaniu! / Bin / bash. Użycie pojedynczych cudzysłowów pozwala uniknąć tego zachowania.


3
Wyłącza również interpolację ciągów :(
Hubro

O to chodzi! Możesz dodać inne argumenty do tego samego polecenia echa, używając podwójnych cudzysłowów i uzyskać interpolację tych części.
Richm

3
możesz także użyć łańcucha w stylu C w bash z $''. Na przykład echo $'1!\n2!\n3!\n'drukuje każdą liczbę, po której następuje huk i nowa linia.
spelufo

1
Umieszczenie huku w pojedynczych cudzysłowach nie pomogło mi przy wprowadzaniu ciągu wieloliniowego:! bash - pojedyncze cytaty nie uciekają przed hukiem (naprawdę)
binki

1
@kbolino Masz rację. Pełny przykładowy ciąg to: echo '0123'"$var"'456'zwróć uwagę na dwie pary pojedynczych cudzysłowów i jedną parę podwójnych cudzysłowów.
phyatt,

16

Jak powiedział Richm , bash próbuje dopasować historię . Innym sposobem na uniknięcie tego jest po prostu ucieczka przed hukiem za pomocą \:

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

Pamiętaj jednak, że wewnątrz podwójnych cudzysłowów \nie są one usuwane:

$ echo "\!"
\!

8
W bash, "\!"właściwie rozwija się \!, a nie !podobno dla zgodności z POSIX.
Mikel

@Mikel Sigh. Tak wiele różnic między zsh a bash
Michael Mrozek

Działa to nawet po wprowadzeniu ciągu wielowierszowego. Pamiętanie, aby 1. być poza ciągiem podczas wchodzenia w huk i 2. używać tej metody (ukośnik odwrotny) wydaje się działać z najmniejszą niespodzianką w większości scenariuszy.
binki,

1
Przydałoby się zauważyć, że bash nie wykonuje przeszukiwania historii podczas wykonywania skryptu, więc nie trzeba robić tam nic specjalnego.
binki,

Czy nie ma żadnej składni, która pozwalałaby na uniknięcie !podwójnych cudzysłowów bez magicznego zachowania? To szalone ...
Lassi

13

!rozpoczyna podstawianie historii („zdarzenie” to wiersz w historii poleceń); na przykład !lsrozwija się do ostatniego wiersza poleceń zawierającego lsi !?foorozwija się do ostatniego wiersza poleceń zawierającego foo. Możesz także wyodrębnić określone słowa (np. !!:1Odnosi się do pierwszego słowa poprzedniego polecenia) i więcej; szczegóły w instrukcji obsługi.

Ta funkcja została wymyślona, ​​aby szybko przywoływać poprzednie polecenia w czasach, gdy edycja wiersza poleceń była prymitywna. Dzięki nowoczesnym powłokom (przynajmniej bash i zsh) oraz kopiowaniu i wklejaniu, ekspansja historii nie jest tak przydatna jak kiedyś - wciąż jest pomocna, ale możesz sobie bez niej poradzić.

Możesz zmienić, który znak wyzwala podstawienie historii, ustawiając histcharszmienną; jeśli rzadko korzystasz z podstawiania historii, możesz ustawić np. histchars='¡^'tak, aby ¡zamiast historii uruchamiało się rozszerzanie historii !. Możesz nawet całkowicie wyłączyć tę funkcję za pomocą set +o histexpand.


3

Aby wyłączyć rozszerzanie historii w określonym wierszu polecenia, możesz użyć spacejako trzeciego znaku $histchars:

histchars='!^ '

Następnie, jeśli wprowadzisz polecenie z wiodącym odstępem, ekspansja historii nie zostanie wykonana.

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

Zauważ jednak, że spacje wiodące są również używane, gdy $HISTCONTROLzawiera ignorespacejako sposób na bashto, aby nie zapisywać wiersza poleceń w historii.

Jeśli chcesz obu funkcji niezależnie, musisz wybrać inną postać jako 3. postać $histchars. Chcesz taki, który nie wpływa na sposób interpretacji twojego polecenia. Kilka opcji:

  • using backslash ( \): \echo foodziała, ale efektem ubocznym jest wyłączenie aliasów lub słów kluczowych.
  • TAB: aby wstawić go w pierwszej pozycji, musisz jednak nacisnąć Ctrl+VTab.
  • jeśli nie przeszkadza pisanie dwóch przycisków, można wybrać dowolny znak, że zwykle nie pojawiają się w pierwszej pozycji ( %, @, ?, odbiór we własnym zakresie) i uczynić pustą alias dla niego:

    histchars='!^%'
    alias %=

    Następnie wpisz tę postać, spację i polecenie:

    bash-4.3$ % echo !!
    !!

(nie będziesz w stanie nie zarejestrować polecenia, w którym zastąpiono zastępowanie historii. Pamiętaj też, że domyślnym trzecim znakiem $histcharsjest #to, że ekspansja historii nie jest wykonywana w komentarzach. Jeśli ją zmienisz i dodasz komentarze na monit, powinieneś zdawać sobie sprawę z tego, że sekwencje! mogą tam zostać rozwinięte).


2

Proponowane rozwiązania nie działają, np. W następującym przykładzie:

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

W takim przypadku huk można wydrukować za pomocą ósemkowego kodu ASCII:

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

1
W pierwszym poleceniu !jest pomiędzy podwójnymi cudzysłowami, gdzie, jak inne odpowiedzi wskazują, zachowuje znaczenie ekspansji historii. Możesz użyć bash -c 'echo '\''hello World!'\'lub bash -c "echo 'hello World"\!"'".
Gilles

Bez parametru -i środowisko może zostać zmienione (tak jak twój ~ / .profile nie działa). Jednak wykonanie -i oznacza, że ​​każde wyjście z twojego .profile zostanie przechwycone w wyniku polecenia, które faktycznie chcesz uruchomić.
Brent

-1

Od czasu wersji Bash 4.3 możesz teraz używać podwójnych cudzysłowów, aby zacytować znak rozszerzenia historii:

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

4
Nie, po tym !następuje, "że to już nie jest wyjątkowe. echo "foo!"jest OK, echo "foo!bar"nie jest
Stéphane Chazelas
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.