Jakie znaki należy wstawiać w argumentach wiersza poleceń?

14

W języku Bash, kiedy należy podać argumenty wiersza polecenia, jakie znaki należy uciec?

Są one ograniczone do metaznakami bash: przestrzeni, karcie |, &, ;, (, ), <, i >?

Tim
źródło
Nie zapomnij (możliwe) globowanie nazw plików za pomocą * i?
Jeff Schaller
Dzięki. Czy możesz wyczerpująco wymienić rodzaje znaków, które muszą być poprzedzone znakami argumentów linii cmd?
Tim
Dobrze jest mieć tę listę, ale najważniejszą rzeczą do zrozumienia w cytowaniu jest: Wszystko między pojedynczymi cudzysłowami jest przekazywane dosłownie i bez podziału słów. Bez wyjątków. (Nawiasem mówiąc, oznacza to, że nie ma możliwości osadzenia pojedynczego cytatu w pojedynczych cytatach, ale łatwo to obejść .)
Wildcard

Odpowiedzi:

22

Następujące znaki mają specjalne znaczenie dla samej powłoki w niektórych kontekstach i może wymagać zmiany znaczenia w argumentach:

Niektóre z tych znaków są używane do więcej rzeczy i w większej liczbie miejsc niż ta, którą połączyłem.


Istnieje kilka przypadków narożnych, które są wyraźnie opcjonalne:

  • !można wyłączyć za pomocą set +H, co jest domyślne w nieinteraktywnych powłokach.
  • {można wyłączyć za pomocą set +B.
  • *i ?można je wyłączyć za pomocą set -flubset -o noglob .
  • =Znak równości (U + 003D) również musi być uciekł jeśli set -kczyset -o keyword jest włączona.

Ucieczka od nowego wiersza wymaga cytowania - odwrotne ukośniki nie wykonają zadania. Wszelkie inne znaki wymienione w IFS będą wymagały podobnej obsługi. Nie trzeba uciekać ]albo }, ale nie trzeba uciekać ), bo to operator.

Niektóre z tych postaci mają ścisłe ograniczenia, kiedy naprawdę potrzebują ucieczki, niż inne. Na przykład a#bjest w porządku, ale a #bjest komentarzem, podczas gdy >musiałby uciekać w obu kontekstach. Zresztą nie zaszkodzi im uciec zachowawczo i jest to łatwiejsze niż zapamiętanie drobnych różnic.

Jeśli sama nazwa polecenia jest kluczowe shell ( if, for, do), a następnie będziesz musiał uciec lub zacytować go zbyt. Jedyny interesujący z nich to in, ponieważ nie jest oczywiste, że zawsze jest to słowo kluczowe. Ci nie muszą tego robić dla słów kluczowych używanych w argumentach, tylko wtedy, gdy masz (głupio!) O nazwie polecenia po jednym z nich. Operatorzy powłoki ( (, &itp.) Zawsze potrzebują cytowania, gdziekolwiek się znajdują.


1 Stéphane zauważył, że każda inna jednobajtowa pusta postać z twojego regionu również potrzebuje ucieczki. W najczęstszych, rozsądnych lokalizacjach, przynajmniej tych opartych na C lub UTF-8, są to tylko powyższe białe znaki. W niektórych lokalizacjach ISO-8859-1 przestrzeń bez przerw U + 00A0 jest uważana za pustą, w tym Solaris, BSD i OS X (myślę niepoprawnie). Jeśli masz do czynienia z dowolnym nieznanym miejscem, może ono zawierać prawie wszystko, w tym litery, więc powodzenia.

Można sobie wyobrazić, że pojedynczy bajt uważany za pusty może pojawiać się w wielobajtowym znaku, który nie był pusty, i nie byłoby sposobu, aby uciec od tego, poza umieszczeniem całego zdania w cudzysłowie. Nie jest A0to kwestia teoretyczna: w ustawieniu ISO-8859-1 z góry ten bajt, który jest uważany za pusty, może pojawiać się w znakach wielobajtowych, takich jak UTF-8 zakodowane „à” ( C3 A0). Aby bezpiecznie obchodzić się z tymi postaciami, musisz je zacytować "à". To zachowanie zależy od konfiguracji ustawień regionalnych w środowisku, w którym działa skrypt, a nie od tego, w którym został napisany.

Myślę, że to zachowanie jest zepsute na wiele sposobów, ale musimy rozgrywać rozdanie, które otrzymaliśmy. Jeśli pracujesz z dowolnym niesynchronizującym wielobajtowym zestawem znaków, najbezpieczniej byłoby zacytować wszystko. Jeśli jesteś w UTF-8 lub C, jesteś bezpieczny (na razie).

Michael Homer
źródło
Inne puste miejsca w twoim regionie również będą wymagały ucieczki ( z wyjątkiem obecnie wielobajtowego z powodu błędu )
Stéphane Chazelas
Musisz tylko uciec, !gdy włączone jest rozszerzanie historii csh, zwykle nie w skryptach. [ ! -f a ]lub find . ! -name...są w porządku. Jest to omówione w części dotyczącej bardziej rygorystycznych limitów, ale może warto o tym wyraźnie wspomnieć.
Stéphane Chazelas,
Należy pamiętać, że istnieją konteksty, w których inne postacie muszą cytowania jak: hash[foo"]"]=, ${var-foo"}"}, [[ "!" = b ]], [[ a = "]]" ]], operatorzy regexp dla [[ x =~ ".+[" ]]. Inne niż słowa kluczowe {( if, while, for...) musiałby być cytowane więc nie są one rozpoznawane jako takie ...
Stéphane Chazelas
W zakresie, w jakim w ogóle są to argumenty wiersza polecenia, interpretacja zależy od danego polecenia (podobnie jak ]), więc nie wymienię ich. Nie sądzę, aby jakieś słowo kluczowe wymagało cytowania w pozycji argumentu.
Michael Homer,
2
Cytowanie wbudowanych, myślników lub% nic nie robi.
Michael Homer
3

W GNU Parallel jest to testowane i szeroko stosowane:

$a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go;
# quote newline as '\n'                                                                                                         
$a =~ s/[\n]/'\n'/go;

Jest on testowany w bash, dash, ash, ksh, zsh, i fish. Niektóre znaki nie wymagają cytowania w niektórych (wersjach) powłok, ale powyższe działa we wszystkich testowanych powłokach.

Jeśli chcesz po prostu zacytować ciąg, możesz go potokować w parallel --shellquote:

printf "&*\t*!" | parallel --shellquote
Ole Tange
źródło
Jak nie słyszałem o równoległości przed ...
Tom H
@TomH Będziemy wdzięczni, jeśli poświęcisz 5 minut na zastanowienie się, w jaki sposób mogliśmy do ciebie dotrzeć.
Ole Tange
Myślę, że to problem z postępem. większość ludzi nie potrzebuje ani nie rozumie równolegle, dopóki nie przejdzie przez pewne etapy złożoności. Do tego czasu natknęli się na xargs, nohup i tym podobne. Nie widzę też wielu osób używających równolegle do rozwiązywania problemów w wymianie stosów lub kiedy szukam rozwiązań problemów z bashiem
Tom H
1

W przypadku lekkiego rozwiązania ucieczki w Perlu, kieruję się zasadą pojedynczych cudzysłowów. Ciąg Bash w pojedynczych cudzysłowach może mieć dowolny znak, z wyjątkiem samego pojedynczego cudzysłowu.

Mój kod:

my $bash_reserved_characters_re = qr([ !"#$&'()*;<>?\[\\`{|~\t\n]);

while(<>) {
    if (/$bash_reserved_characters_re/) {
        my $quoted = s/'/'"'"'/gr;
        print "'$quoted'";
    } else {
        print $_;
    }
}

Przykładowy przebieg 1:

$ echo -n "abc" | perl escape_bash_special_chars.pl
abc

Przykładowy przebieg 2:

echo "abc" | perl escape_bash_special_chars.pl
'abc
'

Przykładowy przebieg 3:

echo -n 'ab^c' | perl escape_bash_special_chars.pl
ab^c

Przykładowy przebieg 4:

echo -n 'ab~c' | perl escape_bash_special_chars.pl
'ab~c'

Przykładowy przebieg 5:

echo -n "ab'c" | perl escape_bash_special_chars.pl
'ab'"'"'c'

echo 'ab'"'"'c'
ab'c
Jari Turkia
źródło
Tak, ważne, że to. Moim zdaniem większość osób wyląduje na tej stronie, ponieważ mają problem do rozwiązania. Nie dlatego, że stanowi to interesującą debatę akademicką. Dlatego chciałbym oferować rozwiązania i omawiać ich zalety, nawet jeśli są nieco nie na temat.
Jari Turkia
Mój kod jest tylko implementacją odpowiedzi Michaela Homera. Nie chciałem przekazywać więcej informacji niż to, co zrobił.
Jari Turkia