Odpowiedzi:
foo="Hello"
foo="${foo} World"
echo "${foo}"
> Hello World
Ogólnie rzecz biorąc, aby połączyć dwie zmienne, możesz po prostu zapisać je jedna po drugiej:
a='Hello'
b='World'
c="${a} ${b}"
echo "${c}"
> Hello World
$foo
podwójnych cudzysłowów, w czasach, gdy naprawdę ma to znaczenie.
foo="$fooworld"
? Zakładałbym, że nie ...
fooworld
. Ujednoznacznienie odbywa się za pomocą nawiasów klamrowych, jak w foo="${foo}world"
...
Bash obsługuje również +=
operatora, jak pokazano w tym kodzie:
$ A="X Y"
$ A+=" Z"
$ echo "$A"
X Y Z
export A+="Z"
a może A
zmienną trzeba wyeksportować tylko raz?
export A+=Z
działa również całkiem nieźle.
#!/bin/sh
w skrypcie używającym tej konstrukcji.
bash
niektórych bardziej zaawansowanych powłokach. Nie będzie działał pod busybox sh
lub dash
(który jest /bin/sh
na wielu dystrybucjach), ani z niektórymi innymi powłokami, takimi jak /bin/sh
dostarczone we FreeBSD.
Ponieważ pytanie dotyczy konkretnie Basha , moja pierwsza część odpowiedzi przedstawiłaby różne sposoby właściwego wykonania tego:
+=
: Dołącz do zmiennejSkładni +=
można używać na różne sposoby:
var+=...
(Ponieważ jestem oszczędny, użyję tylko dwie zmienne foo
i a
, a następnie ponownie użyć tego samego w całej odpowiedź. ;-)
a=2
a+=4
echo $a
24
Używając składni pytania Przepełnienie stosu ,
foo="Hello"
foo+=" World"
echo $foo
Hello World
działa w porządku!
((var+=...))
zmienna a
jest ciągiem, ale także liczbą całkowitą
echo $a
24
((a+=12))
echo $a
36
var+=(...)
Nasz a
jest również tablicą tylko jednego elementu.
echo ${a[@]}
36
a+=(18)
echo ${a[@]}
36 18
echo ${a[0]}
36
echo ${a[1]}
18
Zauważ, że między nawiasami znajduje się tablica oddzielona spacjami . Jeśli chcesz zapisać ciąg zawierający spacje w tablicy, musisz je zawrzeć:
a+=(one word "hello world!" )
bash: !": event not found
Hmm .. to nie jest błąd, ale funkcja ... Aby zapobiec bashowi, aby spróbować się rozwinąć !"
, możesz:
a+=(one word "hello world"! 'hello world!' $'hello world\041')
declare -p a
declare -a a='([0]="36" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="h
ello world!" [6]="hello world!")'
printf
: Ponownie konstruuj zmienną za pomocą wbudowanego poleceniaprintf
Wbudowane polecenie daje potężny sposób rysunek format string. Ponieważ jest to wbudowany Bash , istnieje możliwość wysłania sformatowanego ciągu do zmiennej zamiast drukowania na stdout
:
echo ${a[@]}
36 18 one word hello world! hello world! hello world!
W tej tablicy jest siedem ciągów . Więc możemy zbudować sformatowany ciąg zawierający dokładnie siedem argumentów pozycyjnych:
printf -v a "%s./.%s...'%s' '%s', '%s'=='%s'=='%s'" "${a[@]}"
echo $a
36./.18...'one' 'word', 'hello world!'=='hello world!'=='hello world!'
Lub możemy użyć jednego ciągu formatu argumentu, który będzie powtarzany przy liczbie przesłanych argumentów ...
Zauważ, że nasza a
wciąż jest tablicą! Zmienia się tylko pierwszy element!
declare -p a
declare -a a='([0]="36./.18...'\''one'\'' '\''word'\'', '\''hello world!'\''=='\
''hello world!'\''=='\''hello world!'\''" [1]="18" [2]="one" [3]="word" [4]="hel
lo world!" [5]="hello world!" [6]="hello world!")'
W bash, kiedy uzyskujesz dostęp do nazwy zmiennej bez określania indeksu, zawsze adresujesz tylko pierwszy element!
Aby pobrać naszą tablicę siedmiu pól, wystarczy ponownie ustawić pierwszy element:
a=36
declare -p a
declare -a a='([0]="36" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="he
llo world!" [6]="hello world!")'
Ciąg formatu jednego argumentu z wieloma argumentami przekazanymi do:
printf -v a[0] '<%s>\n' "${a[@]}"
echo "$a"
<36>
<18>
<one>
<word>
<hello world!>
<hello world!>
<hello world!>
foo="Hello"
printf -v foo "%s World" $foo
echo $foo
Hello World
Nota: Zastosowanie podwójnych cudzysłowach mogą być użyteczne do manipulowania ciągi, które zawierają spaces
, tabulations
i / lubnewlines
printf -v foo "%s World" "$foo"
Pod powłoką POSIX nie można było używać bashism , więc nie ma wbudowanego printf
.
Ale możesz po prostu zrobić:
foo="Hello"
foo="$foo World"
echo $foo
Hello World
printf
Jeśli chcesz użyć bardziej wyrafinowanych konstrukcji, musisz użyć widelca (nowy proces potomny, który wykona zadanie i zwróci wynik stdout
):
foo="Hello"
foo=$(printf "%s World" "$foo")
echo $foo
Hello World
Historycznie można było użyć backicks do pobrania wyniku rozwidlenia :
foo="Hello"
foo=`printf "%s World" "$foo"`
echo $foo
Hello World
Nie jest to jednak łatwe do zagnieżdżenia :
foo="Today is: "
foo=$(printf "%s %s" "$foo" "$(date)")
echo $foo
Today is: Sun Aug 4 11:58:23 CEST 2013
z backtickami musisz uciec wewnętrznym widelcem z backslashes :
foo="Today is: "
foo=`printf "%s %s" "$foo" "\`date\`"`
echo $foo
Today is: Sun Aug 4 11:59:10 CEST 2013
+=
Operator jest również o wiele szybciej niż $a="$a$b"
w moich testach .. Który ma sens.
var=${var}.sh
przykładu z innych odpowiedzi, co jest bardzo przydatne.
bash
jedyna powłoka z +=
operatorem? Chcę się przekonać, czy jest wystarczająco przenośny
+=
operatorem, ale wszystkie te sposoby to bashizmy , więc nie przenośne! Nawet ty możesz napotkać specjalny błąd w przypadku niewłaściwej wersji bash!
Możesz to również zrobić:
$ var="myscript"
$ echo $var
myscript
$ var=${var}.sh
$ echo $var
myscript.sh
var=myscript;var=$var.sh;echo $var
miałyby takie same efekty (Działa w trybie bash, myślnik, busybox i innych).
echo $var2
Nie produkujemyscript2
.
niedozwolonej kropki w nazwie zmiennej. Jeśli jeszcze echo ${var}2
lub zobacz moją odpowiedź
bla=hello
laber=kthx
echo "${bla}ohai${laber}bye"
Wyjdzie
helloohaikthxbye
Jest to przydatne, gdy
$blaohai
prowadzi do błędu nie znaleziono zmiennej. Lub jeśli masz ciągi znaków lub inne znaki specjalne. "${foo}"
właściwie ucieka przed wszystkim, co do niego włożysz.
foo="Hello "
foo="$foo World"
Sposób, w jaki rozwiązałem problem, jest po prostu
$a$b
Na przykład,
a="Hello"
b=" World"
c=$a$b
echo "$c"
który produkuje
Hello World
Jeśli na przykład spróbujesz połączyć łańcuch z innym łańcuchem,
a="Hello"
c="$a World"
wtedy echo "$c"
wyprodukuje
Hello World
z dodatkową przestrzenią.
$aWorld
nie działa, jak możesz sobie wyobrazić, ale
${a}World
produkuje
HelloWorld
${a}\ World
produkujeHello World
c=$a$b
tutaj, że zrobię to samo, co c=$a World
(które spróbuje uruchomić World
jako polecenie). To chyba oznacza, że przypisanie jest analizowane przed rozwinięciem zmiennych.
Oto zwięzłe podsumowanie tego, o czym mówi większość odpowiedzi.
Powiedzmy, że mamy dwie zmienne, a 1 $ jest ustawiony na „jeden”:
set one two
a=hello
b=world
Poniższa tabela wyjaśnia różne konteksty, gdzie możemy połączyć z wartościami a
i b
aby utworzyć nową zmienną, c
.
Context | Expression | Result (value of c)
--------------------------------------+-----------------------+---------------------
Two variables | c=$a$b | helloworld
A variable and a literal | c=${a}_world | hello_world
A variable and a literal | c=$1world | oneworld
A variable and a literal | c=$a/world | hello/world
A variable, a literal, with a space | c=${a}" world" | hello world
A more complex expression | c="${a}_one|${b}_2" | hello_one|world_2
Using += operator (Bash 3.1 or later) | c=$a; c+=$b | helloworld
Append literal with += | c=$a; c+=" world" | hello world
Kilka uwag:
+=
jest lepszy z punktu widzenia wydajności, jeśli duży ciąg jest budowany małymi przyrostami, szczególnie w pętli{}
wokół nazw zmiennych, aby ujednoznacznić ich rozwinięcie (jak w wierszu 2 w powyższej tabeli). Jak widać w wierszach 3 i 4, nie ma potrzeby, {}
chyba że zmienna jest łączona z łańcuchem rozpoczynającym się od znaku, który jest prawidłowym pierwszym znakiem w nazwie zmiennej powłoki, czyli alfabet lub podkreślnik.Zobacz też:
Jeszcze inne podejście ...
> H="Hello "
> U="$H""universe."
> echo $U
Hello universe.
... i jeszcze jeden.
> H="Hello "
> U=$H"universe."
> echo $U
Hello universe.
Jeśli chcesz dołączyć coś w rodzaju podkreślenia, użyj klawisza Escape (\)
FILEPATH=/opt/myfile
To nie działa:
echo $FILEPATH_$DATEX
Działa to dobrze:
echo $FILEPATH\\_$DATEX
echo $a\_$b
. Jak wskazano w komentarzu Nik O'Lai, podkreślenie jest regularną postacią. Obsługa białych znaków jest znacznie bardziej wrażliwa na ciągi znaków, echo i konkatenację - można używać \
tego wątku i czytać go dokładnie, ponieważ ten problem pojawia się od czasu do czasu.
Najprostszy sposób ze znakami cudzysłowu:
B=Bar
b=bar
var="$B""$b""a"
echo "Hello ""$var"
var=$B$b"a"; echo Hello\ $var
zrobiłbym, wierzę
Możesz konkatenować bez cudzysłowów. Oto przykład:
$Variable1 Open
$Variable2 Systems
$Variable3 $Variable1$Variable2
$echo $Variable3
Ta ostatnia instrukcja wypisze „OpenSystems” (bez cudzysłowów).
To jest przykład skryptu Bash:
v1=hello
v2=world
v3="$v1 $v2"
echo $v3 # Output: hello world
echo "$v3" # Output: hello world
Nawet jeśli operator + = jest teraz dozwolony, został wprowadzony w wersji Bash 3.1 w 2004 roku.
Każdy skrypt używający tego operatora w starszych wersjach Bash nie powiedzie się z błędem „nie znaleziono polecenia”, jeśli masz szczęście lub „błędem składniowym w pobliżu nieoczekiwanego tokena”.
Dla tych, którym zależy na kompatybilności wstecznej, trzymaj się starszych standardowych metod konkatenacji Basha, takich jak te wymienione w wybranej odpowiedzi:
foo="Hello"
foo="$foo World"
echo $foo
> Hello World
Wolę używać nawiasów klamrowych ${}
do rozwijania zmiennej w łańcuchu:
foo="Hello"
foo="${foo} World"
echo $foo
> Hello World
Nawiasy klamrowe będą pasować do ciągłego użycia sznurka:
foo="Hello"
foo="${foo}World"
echo $foo
> HelloWorld
W przeciwnym razie korzystanie foo = "$fooWorld"
nie będzie działać.
Jeśli próbujesz podzielić łańcuch na kilka linii, możesz użyć odwrotnego ukośnika:
$ a="hello\
> world"
$ echo $a
helloworld
Z jednym odstępem pomiędzy:
$ a="hello \
> world"
$ echo $a
hello world
Ten dodaje również tylko jedną spację między:
$ a="hello \
> world"
$ echo $a
hello world
Bezpieczniejszy sposób:
a="AAAAAAAAAAAA"
b="BBBBBBBBBBBB"
c="CCCCCCCCCCCC"
d="DD DD"
s="${a}${b}${c}${d}"
echo "$s"
AAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCDD DD
Ciągi zawierające spacje mogą stać się częścią polecenia, użyj „$ XXX” i „$ {XXX}”, aby uniknąć tych błędów.
Plus spójrz na inną odpowiedź na temat + =
d=DD DD
Dałbym więc DD: command not found
--- zauważ, że jest to ostatni DD, a raczej d, że nie został znaleziony. Jeśli wszystkie operandy są poprawnie sformatowane i zawierają już wymagane spacje, możesz po prostu połączyć je s=${a}${b}${c}${d}; echo $s
z mniejszymi znakami cudzysłowu. Możesz także użyć \
(klawisz Escape), aby uniknąć tych problemów --- d=echo\ echo
nie uruchomi żadnego wywołania echa, podczas gdy d=echo echo
będzie.
Jest jeden szczególny przypadek, w którym powinieneś zachować ostrożność:
user=daniel
cat > output.file << EOF
"$user"san
EOF
Wyjdzie "daniel"san
, a nie danielsan
tak, jak mogłeś chcieć. W takim przypadku powinieneś zamiast tego zrobić:
user=daniel
cat > output.file << EOF
${user}san
EOF
a="Hello,"
a=$a" World!"
echo $a
W ten sposób łączysz dwa łańcuchy.
Jeśli jest to przykład dodawania " World"
do oryginalnego ciągu znaków, może to być:
#!/bin/bash
foo="Hello"
foo=$foo" World"
echo $foo
Wyjście:
Hello World
var1='hello'
var2='world'
var3=$var1" "$var2
echo $var3
var3=$var1\ $var2
Ma również ten sam efekt
Istnieją obawy dotyczące wydajności, ale dane nie są oferowane. Pozwól, że zasugeruję prosty test.
(UWAGA: date
w systemie macOS nie oferuje nanosekund, więc należy to zrobić w systemie Linux).
Utworzyłem append_test.sh na GitHub z zawartością:
#!/bin/bash -e
output(){
ptime=$ctime;
ctime=$(date +%s.%N);
delta=$(bc <<<"$ctime - $ptime");
printf "%2s. %16s chars time: %s delta: %s\n" $n "$(bc <<<"10*(2^$n)")" $ctime $delta;
}
method1(){
echo 'Method: a="$a$a"'
for n in {1..32}; do a="$a$a"; output; done
}
method2(){
echo 'Method: a+="$a"'
for n in {1..32}; do a+="$a"; output; done
}
ctime=0; a="0123456789"; time method$1
Test 1:
$ ./append_test.sh 1
Method: a="$a$a"
1. 20 chars time: 1513640431.861671143 delta: 1513640431.861671143
2. 40 chars time: 1513640431.865036344 delta: .003365201
3. 80 chars time: 1513640431.868200952 delta: .003164608
4. 160 chars time: 1513640431.871273553 delta: .003072601
5. 320 chars time: 1513640431.874358253 delta: .003084700
6. 640 chars time: 1513640431.877454625 delta: .003096372
7. 1280 chars time: 1513640431.880551786 delta: .003097161
8. 2560 chars time: 1513640431.883652169 delta: .003100383
9. 5120 chars time: 1513640431.886777451 delta: .003125282
10. 10240 chars time: 1513640431.890066444 delta: .003288993
11. 20480 chars time: 1513640431.893488326 delta: .003421882
12. 40960 chars time: 1513640431.897273327 delta: .003785001
13. 81920 chars time: 1513640431.901740563 delta: .004467236
14. 163840 chars time: 1513640431.907592388 delta: .005851825
15. 327680 chars time: 1513640431.916233664 delta: .008641276
16. 655360 chars time: 1513640431.930577599 delta: .014343935
17. 1310720 chars time: 1513640431.954343112 delta: .023765513
18. 2621440 chars time: 1513640431.999438581 delta: .045095469
19. 5242880 chars time: 1513640432.086792464 delta: .087353883
20. 10485760 chars time: 1513640432.278492932 delta: .191700468
21. 20971520 chars time: 1513640432.672274631 delta: .393781699
22. 41943040 chars time: 1513640433.456406517 delta: .784131886
23. 83886080 chars time: 1513640435.012385162 delta: 1.555978645
24. 167772160 chars time: 1513640438.103865613 delta: 3.091480451
25. 335544320 chars time: 1513640444.267009677 delta: 6.163144064
./append_test.sh: fork: Cannot allocate memory
Test 2:
$ ./append_test.sh 2
Method: a+="$a"
1. 20 chars time: 1513640473.460480052 delta: 1513640473.460480052
2. 40 chars time: 1513640473.463738638 delta: .003258586
3. 80 chars time: 1513640473.466868613 delta: .003129975
4. 160 chars time: 1513640473.469948300 delta: .003079687
5. 320 chars time: 1513640473.473001255 delta: .003052955
6. 640 chars time: 1513640473.476086165 delta: .003084910
7. 1280 chars time: 1513640473.479196664 delta: .003110499
8. 2560 chars time: 1513640473.482355769 delta: .003159105
9. 5120 chars time: 1513640473.485495401 delta: .003139632
10. 10240 chars time: 1513640473.488655040 delta: .003159639
11. 20480 chars time: 1513640473.491946159 delta: .003291119
12. 40960 chars time: 1513640473.495354094 delta: .003407935
13. 81920 chars time: 1513640473.499138230 delta: .003784136
14. 163840 chars time: 1513640473.503646917 delta: .004508687
15. 327680 chars time: 1513640473.509647651 delta: .006000734
16. 655360 chars time: 1513640473.518517787 delta: .008870136
17. 1310720 chars time: 1513640473.533228130 delta: .014710343
18. 2621440 chars time: 1513640473.560111613 delta: .026883483
19. 5242880 chars time: 1513640473.606959569 delta: .046847956
20. 10485760 chars time: 1513640473.699051712 delta: .092092143
21. 20971520 chars time: 1513640473.898097661 delta: .199045949
22. 41943040 chars time: 1513640474.299620758 delta: .401523097
23. 83886080 chars time: 1513640475.092311556 delta: .792690798
24. 167772160 chars time: 1513640476.660698221 delta: 1.568386665
25. 335544320 chars time: 1513640479.776806227 delta: 3.116108006
./append_test.sh: fork: Cannot allocate memory
Błędy wskazują, że mój Bash dostał się do 335,54432 MB, zanim się zawiesił . Możesz zmienić kod z podwojenia danych na dodawanie stałej, aby uzyskać bardziej szczegółowy wykres i punkt awarii. Ale myślę, że powinno to dać ci wystarczającą ilość informacji, aby zdecydować, czy cię to obchodzi. Osobiście poniżej 100 MB nie. Twój przebieg może się różnić.
join <(LANG=C bash -c 'a="a" c=1 last=${EPOCHREALTIME//.};while :;do a+=$a;now=${EPOCHREALTIME//.};echo $((c++)) ${#a} $((now-last));last=$now;done') <(LANG=C bash -c 'a="a" c=1 last=${EPOCHREALTIME//.};while :;do a=$a$a;now=${EPOCHREALTIME//.};echo $((c++)) ${#a} $((now-last));last=$now;done')|sed -ue '1icnt strlen a+=$a a=$a$a' -e 's/^\([0-9]\+\) \([0-9]\+\) \([0-9]\+\) \2/\1 \2 \3/' | xargs printf "%4s %11s %9s %9s\n"
(Spróbuj tego na nieproduktywnym gospodarzu !!;)
Pamiętaj, że to nie zadziała
foo=HELLO
bar=WORLD
foobar=PREFIX_$foo_$bar
wydaje się, że upuszcza $ foo i pozostawia:
PREFIX_WORLD
ale to zadziała:
foobar=PREFIX_"$foo"_"$bar"
i zostawić prawidłowe wyjście:
PREFIX_HELLO_WORLD
Robię to w wygodny sposób: użyj polecenia wbudowanego!
echo "The current time is `date`"
echo "Current User: `echo $USER`"
date "+The current time is %a %b %d %Y +%T"
zamiast echo ...$(date)
. Zgodnie z niedawnym bash, można napisać: printf "The current time is %(%a %b %d %Y +%T)T\n" -1
.
Moim zdaniem najprostszym sposobem na połączenie dwóch łańcuchów jest napisanie funkcji, która zrobi to za Ciebie, a następnie użycie tej funkcji.
function concat ()
{
prefix=$1
suffix=$2
echo "${prefix}${suffix}"
}
foo="Super"
bar="man"
concat $foo $bar # Superman
alien=$(concat $foo $bar)
echo $alien # Superman
Lubię tworzyć szybką funkcję.
#! /bin/sh -f
function combo() {
echo $@
}
echo $(combo 'foo''bar')
Jeszcze inny sposób na skórowanie kota. Tym razem z funkcjami: D
Nie wiem jeszcze o PHP, ale działa to w Linux Bash. Jeśli nie chcesz wpływać na zmienną, możesz spróbować:
read pp; *# Assumes I will affect Hello to pp*
pp=$( printf $pp ;printf ' World'; printf '!');
echo $pp;
>Hello World!
Możesz umieścić inną zmienną zamiast „Witaj” lub „!”. Możesz także połączyć więcej łańcuchów.
foo="Hello"
foo=$foo" World"
echo $foo
działało to raczej dla „#! / bin / sh”