Jak zachować cytaty w argumentach Basha? [duplikat]

to pytanie ma już odpowiedzi tutaj : Zachowaj Cytaty w argumentach Basha (6 odpowiedzi) Zamknięte 1 rok temu .

Mam skrypt Bash, w którym chcę zachować cudzysłowy w przekazywanych argumentach.

Przykład:

./test.sh this is "some test"

Następnie chcę użyć tych argumentów i użyć ich ponownie, włączając w to cudzysłowy i cudzysłowy wokół całej listy argumentów.

Próbowałem użyć \"$@\", ale to usuwa cytaty z listy.

Jak to osiągnąć?

 86
Author: codeforester, 2009-11-03

14 answers

Po prostu użyj pojedynczych cudzysłowów wokół łańcucha z podwójnymi cudzysłowami:

./test.sh this is '"some test"'

Więc podwójne cudzysłowy wewnątrz pojedynczych cudzysłowów były również interpretowane jako ciąg znaków.

Ale polecam umieścić cały łańcuch pomiędzy pojedynczymi cudzysłowami:

 ./test.sh 'this is "some test" '

Aby zrozumieć, co robi powłoka lub raczej interpretować argumenty w skryptach, możesz napisać taki mały skrypt:

#!/bin/bash

echo $@
echo "$@"

Następnie zobaczysz i przetestujesz, co się dzieje podczas wywoływania skryptu z różne struny

 -17
Author: Randy Sugianto 'Yuku',
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-27 18:03:00

Użycie "$@" zastąpi argumenty jako listę, bez ponownego dzielenia ich na białe znaki (zostały one podzielone raz, gdy skrypt powłoki został wywołany), co jest zazwyczaj dokładnie tym, czego chcesz, jeśli chcesz ponownie przekazać argumenty do innego programu.

Zauważ, że jest to specjalna forma i jest rozpoznawana tylko wtedy, gdy pojawia się dokładnie w ten sposób. Jeśli dodasz coś jeszcze w cudzysłowach, wynik zostanie połączony w jeden argument.

What are you trying to do a w jaki sposób to nie działa?

 104
Author: Chris Dodd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-26 18:22:41

Odpowiedź Juku działa tylko wtedy, gdy jesteś jedynym użytkownikiem skryptu, podczas gdy Dennis Williamson jest świetny, jeśli interesuje Cię głównie drukowanie ciągów i oczekujesz, że nie będą miały cudzysłowów.

Oto wersja, która może być użyta, jeśli chcesz przekazać wszystkie argumenty jako jeden duży argument cytowany do parametru -c z bash lub su:

#!/bin/bash
C=''
for i in "$@"; do 
    i="${i//\\/\\\\}"
    C="$C \"${i//\"/\\\"}\""
done
bash -c "$C"

Więc, wszystkie argumenty mają wokół siebie cytat (nieszkodliwe, jeśli nie było go wcześniej, w tym celu), ale unikamy również żadnych znaków specjalnych, a następnie unikamy cudzysłowów, które były już w argumencie(składnia ${var//from/to} wykonuje globalne zastępowanie podłańcuchów).

Można oczywiście tylko cytować rzeczy, które już miały białe znaki, ale to nie ma znaczenia tutaj. Jednym z narzędzi takiego skryptu jest możliwość posiadania określonego predefiniowanego zestawu zmiennych środowiskowych (lub, w przypadku su, uruchamiania rzeczy jako określony użytkownik, bez tego bałaganu podwójnego cytowania wszystkiego).


Aktualizacja: ostatnio miał powód, aby to zrobić w sposób POSIX z minimalnym rozwidleniem, które prowadzi do tego skryptu (ostatni printf tam wypisuje wiersz poleceń używany do wywołania skryptu, który powinien być w stanie skopiować i wkleić, aby wywołać go z równoważnymi argumentami):

#!/bin/sh
C=''
for i in "$@"; do
    case "$i" in
        *\'*)
            i=`printf "%s" "$i" | sed "s/'/'\"'\"'/g"`
            ;;
        *) : ;;
    esac
    C="$C '$i'"
done
printf "$0%s\n" "$C"

Przełączyłem na '' ponieważ powłoki również interpretują rzeczy takie jak $ i !! w ""-cudzysłów.

 43
Author: unhammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-07 12:32:09

Są dwa bezpieczne sposoby, aby to zrobić:

1. rozszerzenie parametru powłoki : ${variable@Q}:

Przy rozwijaniu zmiennej poprzez ${variable@Q}:

Rozszerzenie jest ciągiem znaków, który jest wartością parametru cytowanego w formacie, który może być ponownie użyty jako dane wejściowe.

Przykład:

$ expand-q() { for i; do echo ${i@Q}; done; }  # Same as for `i in "$@"`...
$ expand-q word "two words" 'new
> line' "single'quote" 'double"quote'
word
'two words'
$'new\nline'
'single'\''quote'
'double"quote'

2. printf %q "$quote-me"

printf obsługuje cytowanie wewnętrznie. Wpis podręcznika dla printf says:

%q powoduje, że printf wypisuje odpowiednie argument w formacie, który może być ponownie użyty jako wejście powłoki.

Przykład:

$ cat test.sh 
#!/bin/bash
printf "%q\n" "$@"
$ 
$ ./test.sh this is "some test" 'new                                                                                                              
>line' "single'quote" 'double"quote'
this
is
some\ test
$'new\nline'
single\'quote
double\"quote
$

Zauważ, że druga droga jest nieco czystsza, jeśli wyświetla cytowany tekst człowiekowi.

Powiązane: dla bash, POSIX SH i zsh: cudzysłów z pojedynczymi cudzysłowami zamiast odwrotnych ukośników

 42
Author: Tom Hale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 08:37:14

Jeśli można bezpiecznie założyć, że argument zawierający spacje musi być (i powinien być) cytowany, możesz dodać je w następujący sposób:

#!/bin/bash
whitespace="[[:space:]]"
for i in "$@"
do
    if [[ $i =~ $whitespace ]]
    then
        i=\"$i\"
    fi
    echo "$i"
done

Oto przykładowy przebieg:

$ ./argtest abc def "ghi jkl" $'mno\tpqr' $'stu\nvwx'
abc
def
"ghi jkl"
"mno    pqr"
"stu
vwx"

Możesz także wstawić literalne tabulatory i nowe linie używając Ctrl-V Tab i Ctrl-V Ctrl-J w podwójnych lub pojedynczych cudzysłowach zamiast używania znaków specjalnych w $'...'.

Notka o wstawianie znaków W Bash: jeśli używasz wiązań klawiszy Vi (set -o vi) w Bash( Emacs jest domyślnie - set -o emacs), musisz być w trybie Wstaw , aby wstawić znaki . W trybie Emacs zawsze jesteś w trybie wstawiania.

 34
Author: Dennis Williamson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-16 23:17:02

Potrzebowałem tego do przesłania wszystkich argumentów do innego interpretera. To co dla mnie dobre to:

bash -c "$(printf ' %q' "$@")"

Przykład (gdy jest nazwany jako forward.sh): {]}

$ ./forward.sh echo "3 4"
3 4
$ ./forward.sh bash -c "bash -c 'echo 3'"
3

(oczywiście faktyczny skrypt, którego używam, jest bardziej złożony, włączając w to mój przypadek nohup i przekierowania itp. ale to jest kluczowa część.)

 16
Author: isarandi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-16 09:53:08

Jak powiedział Tom Hale, jednym ze sposobów na to jest printf użycie %q do cytowania-escape.

Na przykład:

Send_all_args.sh

#!/bin/bash
if [ "$#" -lt 1 ]; then
 quoted_args=""
else
 quoted_args="$(printf " %q" "${@}")"
fi

bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_args}"

Send_fewer_args.sh

#!/bin/bash
if [ "$#" -lt 2 ]; then
 quoted_last_args=""
else
 quoted_last_args="$(printf " %q" "${@:2}")"
fi

bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_last_args}"

Receiver.sh

#!/bin/bash
for arg in "$@"; do
  echo "$arg"
done

Przykładowe użycie:

$ ./send_all_args.sh
$ ./send_all_args.sh a b
a
b
$ ./send_all_args.sh "a' b" 'c "e '
a' b
c "e 
$ ./send_fewer_args.sh
$ ./send_fewer_args.sh a
$ ./send_fewer_args.sh a b
b
$ ./send_fewer_args.sh "a' b" 'c "e '
c "e 
$ ./send_fewer_args.sh "a' b" 'c "e ' 'f " g'
c "e 
f " g
 10
Author: Gary S. Weaver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 22:24:39

Zmieniono unhammer Przykład użycia tablicy.

printargs() { printf "'%s' " "$@"; echo; };  # http://superuser.com/a/361133/126847

C=()
for i in "$@"; do
    C+=("$i")  # Need quotes here to append as a single array element.
done

printargs "${C[@]}"  # Pass array to a program as a list of arguments.                               
 6
Author: JohnMudd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:27

Mój problem był podobny i użyłem mieszanych pomysłów zamieszczonych tutaj.

Mamy serwer ze skryptem PHP, który wysyła e-maile. Następnie mamy drugi serwer, który łączy się z pierwszym serwerem przez SSH i wykonuje go.

Nazwa skryptu jest taka sama na obu serwerach i oba są faktycznie wykonywane za pomocą skryptu bash.

Na serwerze 1 (lokalnym) skrypcie bash mamy tylko:

/usr/bin/php /usr/local/myscript/myscript.php "$@"

Znajduje się na /usr/local/bin/myscript i jest wywoływany przez zdalny serwer. Działa dobrze nawet na argumenty z miejsca.

Ale wtedy na zdalnym serwerze nie możemy użyć tej samej logiki, ponieważ pierwszy serwer nie otrzyma cudzysłowów z "$@". Wykorzystałem pomysły Johnmudda i Dennisa Williamsona, aby odtworzyć tablicę opcji i parametrów z cytatami. Podoba mi się pomysł dodawania unikalnych cytatów tylko wtedy, gdy element ma spacje.

Więc zdalny skrypt działa z:

CSMOPTS=()
whitespace="[[:space:]]"
for i in "$@"
do
    if [[ $i =~ $whitespace ]]
    then
        CSMOPTS+=(\"$i\")
    else
        CSMOPTS+=($i)
    fi
done

/usr/bin/ssh "$USER@$SERVER" "/usr/local/bin/myscript ${CSMOPTS[@]}"

Zauważ, że używam "${CSMOPTS[@]}", aby przekazać tablicę opcji do zdalnego serwera.

Dzięki za każdy, kto tu pisał! To naprawdę mi pomogło! :)

 6
Author: Gus Neves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-29 21:05:50

Po prostu użyj:

"${@}"

Na przykład:

# cat t2.sh
for I in "${@}"
do
   echo "Param: $I"
done
# cat t1.sh
./t2.sh "${@}"

# ./t1.sh "This is a test" "This is another line" a b "and also c"
Param: This is a test
Param: This is another line
Param: a
Param: b
Param: and also c
 6
Author: FrameGrace,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-08 19:24:14

Cudzysłowy są interpretowane przez bash i nie są przechowywane w argumentach wiersza poleceń ani wartościach zmiennych.

Jeśli chcesz użyć cytowanych argumentów, musisz je za każdym razem cytować:

val="$3"
echo "Hello World" > "$val"
 4
Author: mouviciel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 17:03:06

Jak Gary S. Weaver pokazał w swoich wskazówkach dotyczących kodu źródłowego, sztuczka polega na wywołaniu Basha z parametrem '- c', a następnie zacytowaniu następnego.

Np.

bash -c "<your program> <parameters>"

Lub

docker exec -it <my docker> bash -c "$SCRIPT $quoted_args"
 1
Author: Lars,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-22 12:22:10

Tak, wydaje się, że nie jest możliwe zachowanie cytatów, ale dla problemu, z którym miałem do czynienia, nie było to konieczne.

Mam funkcję bash, która rekurencyjnie przeszukuje folder i grep szuka ciągu znaków, problem polega na przekazaniu ciągu zawierającego spacje ,np. "znajdź ten ciąg". Przekazanie tego do skryptu bash pobierze podstawowy argument $N i przekaże go grepowi, co powoduje, że grep wierzy, że są to różne argumenty. Sposób, w jaki to rozwiązałem, wykorzystując fakt, że gdy cytujesz bash, aby wywołać funkcję, grupuje ona elementy w cudzysłowach w jeden argument. Po prostu musiałem udekorować ten argument cudzysłowami i przekazać go do komendy grep.

Jeśli wiesz, jaki argument otrzymujesz w bash, który potrzebuje cudzysłowów do następnego kroku, możesz po prostu ozdobić cudzysłowami.

 0
Author: Tom Orsi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-20 16:55:51

Jeśli chcesz przekazać wszystkie argumenty do bash z innego języka programowania (na przykład, jeśli chcesz wykonać bash -c LUB emit_bash_code | bash), Użyj tego:

  • unikaj wszystkich pojedynczych znaków cudzysłowu, które masz za pomocą '\''.
  • następnie otocz wynik cudzysłowem liczby pojedynczej

Argument abc'def zostanie zatem zamieniony na 'abc'\''def'. Znaki '\'' są interpretowane w następujący sposób: istniejące już cytowanie kończy się pierwszym cytatem, a następnie pojawia się unikalny pojedynczy cytat \', a następnie rozpoczyna się nowe cytowanie.

 0
Author: VasiliNovikov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-20 16:04:32