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.