Dlaczego echo ignoruje moje znaki zapytania?

24

echoPolecenie nie jest w tym pełny tekst, który daję. Na przykład, jeśli zrobię:

   $ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

Wyprowadza:

echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `

Pojedyncze cudzysłowy ( '), które miałem w poleceniu echa, nie są uwzględnione. Jeśli przejdę na podwójne cudzysłowy:

$ echo " echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `  "

echow ogóle nic nie wyświetla! Dlaczego pomija pojedyncze cudzysłowy w pierwszym przykładzie i nic nie daje w drugim? Jak uzyskać to, aby wyświetlało dokładnie to, co wpisałem?

Michał Mrożek
źródło
3
Nie jestem do końca pewien, co próbujesz tutaj osiągnąć. Dlaczego zagnieżdżasz jedną instrukcję echa w innej? A może dosłownie próbujesz wydrukować polecenie (na przykład czy coś takiego)?
Andy Smith,
Muszę wstawić dane wyjściowe echa do innego pliku
Musisz wyjaśnić, co próbujesz zrobić. Na przykład „Chcę dołączyć następujący tekst:„… ”do pliku.”
MikeyB
2
Próbowałem to edytować przez 5 minut, ale poważnie nie wiem, jakie są pożądane wyniki. Edycja : Ok, myślę, że teraz zgaduję, więc spróbowałem edytować. Jeśli to nie to, co zamierzałeś, daj mi znać
Michael Mrozek
1
@Michael - Wow - Gdybym mógł dać ci przedstawiciela do twojej edycji, zrobiłbym - niezłą robotę, czyniąc z tego naprawdę świetne pytanie!
Randall,

Odpowiedzi:

33

Twoja powłoka interpretuje cytaty, 'i "zanim jeszcze do nich dojdą echo. Generalnie po prostu umieszczam podwójne cytaty wokół mojego argumentu, aby powtórzyć, nawet jeśli są niepotrzebne; na przykład:

$ echo "Hello world"
Hello world

Tak więc w pierwszym przykładzie, jeśli chcesz dołączyć dosłowne znaki cudzysłowu, musisz je usunąć:

$ echo \'Hello world\'
'Hello world'

Lub muszą być już użyte w cytowanym argumencie (ale nie może to być ten sam cytat, inaczej trzeba będzie uciec):

$ echo "'Hello world'"
'Hello world'

$ echo '"Hello world"'
"Hello world"

W drugim przykładzie uruchomiono podstawienie polecenia w środku ciągu:

grep  $ARG  /var/tmp/setfile  | awk {print $2}

Rzeczy, które zaczynają się, $są również obsługiwane przez powłokę - traktuje je jak zmienne i zastępuje ich wartościami. Ponieważ najprawdopodobniej żadna z tych zmiennych nie jest ustawiona w twojej powłoce, tak naprawdę po prostu działa

grep /var/tmp/setfile | awk {print}

Ponieważ grepwidzi tylko jeden argument, zakłada, że ​​ten argument jest wzorcem, którego szukasz, i że miejscem, z którego powinien odczytać dane, jest standardowe, więc blokuje oczekiwanie na dane wejściowe. Dlatego wydaje się, że twoje drugie polecenie po prostu się zawiesiło.

Nie stanie się tak, jeśli zacytujesz argument pojedynczo (dlatego właśnie twój pierwszy przykład prawie działał), więc jest to jeden ze sposobów uzyskania pożądanego wyniku:

echo \'' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '\'

Możesz także zacytować go dwukrotnie, ale wtedy będziesz musiał uciec od $s, aby powłoka nie rozpoznała ich jako zmiennych, a także backsticks, aby powłoka nie uruchomiła od razu podstawiania poleceń:

echo "' echo PARAM=\`  grep  \$ARG  /var/tmp/setfile  | awk '{print \$2}' \`    '"
Michał Mrożek
źródło
7

Nie będę szczegółowo omawiał, dlaczego wasze próby zachowują się tak, jak się zachowują, ponieważ odpowiedź Michała Mrożka obejmuje to dobrze. W skrócie, wszystko pomiędzy pojedynczymi cudzysłowami ( '…') jest interpretowane dosłownie (w szczególności pierwszy 'oznacza koniec ciągu dosłownego), `a jednocześnie $zachowują swoje specjalne znaczenie między "…".

Nie ma cytowania w pojedynczych cudzysłowach, więc nie można wstawić pojedynczego cudzysłowu w ciągu pojedynczego cudzysłowu. Istnieje jednak idiom, który wygląda tak:

echo 'foo'\''bar'

Wypisuje się foo'bar, ponieważ argument echoskłada się z trzyznakowego ciągu o pojedynczym cudzysłowie foo, połączonego z pojedynczym znakiem '(uzyskanym przez ochronę znaku 'przed jego specjalnym znaczeniem przez poprzedni \), połączonego z ciągiem z pojedynczym cudzysłowem bar. Chociaż nie do końca dzieje się to za kulisami, możesz pomyśleć o '\''sposobie umieszczenia pojedynczego cudzysłowu w ciągu pojedynczego cudzysłowu.

Jeśli chcesz drukować złożone ciągi wielowierszowe, lepszym narzędziem jest dokument tutaj . Dokument tutaj składa się z dwóch znaków, <<po których następuje znacznik, taki jak <<EOF, następnie niektóre wiersze tekstu, a następnie sam znacznik końcowy na linii. Jeśli znacznik jest cytowany w jakikolwiek sposób ( 'EOF'lub "EOF"lub \EOF„E” „OF” lub…), wówczas tekst jest interpretowany dosłownie (jak w pojedynczych cudzysłowach, z tym wyjątkiem, że nawet 'jest zwykłym znakiem). Jeśli znacznik nie jest w ogóle cytowany, tekst jest interpretowany jak ciąg podwójny, $\`zachowując swój specjalny status (ale "znaki nowej linii są interpretowane dosłownie).

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF
Gilles „SO- przestań być zły”
źródło
1

Ok, to zadziała:

echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

Testowanie tutaj: -

[asmith@yagi ~]$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '
 echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ` 
Andy Smith
źródło
0

Sugestia Gillesa, by użyć tutaj dokumentu jest naprawdę miła, a nawet działa w językach skryptowych takich jak Perl. Jako konkretny przykład oparty na jego odpowiedzi na problem PO:

use strict;
my $cmd = q#cat <<'EOF' # . "\n" .
          q#echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `# . "\n" .
          q#EOF# ;
open (my $OUTPUT, "$cmd |");
print $_ while <$OUTPUT>;
close $OUTPUT;

To prawda, że ​​ten przykład jest nieco wymyślony, ale uważam, że jest to przydatna technika, powiedzmy, wysyłania instrukcji SQL na adres psql(zamiast cat).

(Zauważ, że dowolny niealfanumeryczny znak spacji może być użyty zamiast #ogólnej konstrukcji cytowania powyżej, ale skrót wydaje się dobrze wyróżniać w tym przykładzie i nie jest traktowany jako komentarz).

Randall
źródło
-1

Weźmy twoje polecenie

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

i najpierw podziel go na słowa, używając cudzysłowu jako cudzysłowu:

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“$2}' `    '”

Następnie dokonujemy rozszerzenia parametrów: rozwiń, $…ale nie wewnątrz '…'. Ponieważ $2jest pusty (chyba że ustawisz go np. set -- …), Zostanie zastąpiony pustym ciągiem.

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“}' `    '”

Następnie usuń wycenę.

Arg[1]=“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print”
Arg[2]=“} `    ”

Argumenty te są następnie przekazywane do echa, które wypisze je jeden po drugim, oddzielone pojedynczą spacją.

“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `    ”

Mam nadzieję, że ta instrukcja krok po kroku uczyni obserwowane zachowanie wyraźniejszym. Aby uniknąć tego problemu, unikaj pojedynczych cudzysłowów:

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

Spowoduje to zakończenie ciągu pojedynczego cudzysłowu, następnie doda (cudzą literę) pojedynczy cytat do słowa, a następnie rozpocznie nowy ciąg pojedynczego cudzysłowu, bez rozbijania słowa na części.

MvG
źródło