Trong PHP, các chuỗi được nối với nhau như sau:
$foo = "Hello";
$foo .= " World";
Ở đây, $foo
trở thành "Xin chào thế giới".
Làm thế nào điều này được thực hiện trong Bash?
foo1="World" foo2="Hello" foo3="$foo1$foo2"
Trong PHP, các chuỗi được nối với nhau như sau:
$foo = "Hello";
$foo .= " World";
Ở đây, $foo
trở thành "Xin chào thế giới".
Làm thế nào điều này được thực hiện trong Bash?
foo1="World" foo2="Hello" foo3="$foo1$foo2"
Câu trả lời:
foo="Hello"
foo="${foo} World"
echo "${foo}"
> Hello World
Nói chung để nối hai biến, bạn chỉ có thể viết từng biến một:
a='Hello'
b='World'
c="${a} ${b}"
echo "${c}"
> Hello World
$foo
bên trong dấu ngoặc kép, cho những lúc nó thực sự quan trọng.
foo="$fooworld"
nào? Tôi sẽ giả sử không ...
fooworld
. Định hướng được thực hiện với niềng răng, như trong foo="${foo}world"
...
Bash cũng hỗ trợ một +=
toán tử như trong mã này:
$ A="X Y"
$ A+=" Z"
$ echo "$A"
X Y Z
export A+="Z"
hoặc có thể A
biến chỉ cần được xuất một lần?
export A+=Z
hoạt động khá độc đáo.
#!/bin/sh
trong một kịch bản sử dụng cấu trúc này.
bash
và một số shell tiên tiến khác. Nó sẽ không hoạt động dưới busybox sh
hoặc dash
( /bin/sh
trên nhiều bản phân phối), hoặc một số shell khác như/bin/sh
được cung cấp trên FreeBSD.
Khi câu hỏi này dành riêng cho Bash , phần đầu tiên của câu trả lời của tôi sẽ trình bày các cách khác nhau để thực hiện điều này đúng:
+=
: Nối vào biếnCú pháp +=
có thể được sử dụng theo những cách khác nhau:
var+=...
(Bởi vì tôi tằn tiện, tôi sẽ chỉ sử dụng hai biến foo
và a
và sau đó tái sử dụng như nhau trong toàn bộ câu trả lời ;-).
a=2
a+=4
echo $a
24
Sử dụng cú pháp câu hỏi Stack Overflow ,
foo="Hello"
foo+=" World"
echo $foo
Hello World
hoạt động tốt
((var+=...))
Biến đổi a
là một chuỗi, nhưng cũng là một số nguyên
echo $a
24
((a+=12))
echo $a
36
var+=(...)
Chúng tôi a
cũng là một mảng chỉ có một yếu tố.
echo ${a[@]}
36
a+=(18)
echo ${a[@]}
36 18
echo ${a[0]}
36
echo ${a[1]}
18
Lưu ý rằng giữa các dấu ngoặc đơn, có một mảng được phân tách bằng dấu cách . Nếu bạn muốn lưu trữ một chuỗi chứa khoảng trắng trong mảng của mình, bạn phải kèm theo chúng:
a+=(one word "hello world!" )
bash: !": event not found
Hmm .. đây không phải là một lỗi, mà là một tính năng ... Để ngăn bash cố gắng phát triển !"
, bạn có thể:
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
: Xây dựng lại biến bằng cách sử dụng nội dung lệnhLệnh printf
dựng sẵn cung cấp một cách mạnh mẽ để vẽ định dạng chuỗi. Vì đây là một nội dung Bash , có một tùy chọn để gửi chuỗi được định dạng tới một biến thay vì in trên stdout
:
echo ${a[@]}
36 18 one word hello world! hello world! hello world!
Có bảy chuỗi trong mảng này. Vì vậy, chúng ta có thể xây dựng một chuỗi được định dạng chứa chính xác bảy đối số vị trí:
printf -v a "%s./.%s...'%s' '%s', '%s'=='%s'=='%s'" "${a[@]}"
echo $a
36./.18...'one' 'word', 'hello world!'=='hello world!'=='hello world!'
Hoặc chúng ta có thể sử dụng một chuỗi định dạng đối số sẽ được lặp lại khi có nhiều đối số được gửi ...
Lưu ý rằng chúng tôi a
vẫn là một mảng! Chỉ có yếu tố đầu tiên được thay đổi!
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!")'
Trong bash, khi bạn truy cập một tên biến mà không chỉ định chỉ mục, bạn luôn chỉ xử lý phần tử đầu tiên!
Vì vậy, để lấy lại mảng bảy trường của chúng ta, chúng ta chỉ cần đặt lại phần tử thứ 1:
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!")'
Một chuỗi định dạng đối số có nhiều đối số được truyền cho:
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: Việc sử dụng hai dấu ngoặc kép có thể hữu ích cho các thao tác chuỗi có chứa spaces
, tabulations
và / hoặcnewlines
printf -v foo "%s World" "$foo"
Trong vỏ POSIX , bạn không thể sử dụng bashism , do đó không có nội dung printf
.
Nhưng bạn có thể chỉ cần làm:
foo="Hello"
foo="$foo World"
echo $foo
Hello World
printf
Nếu bạn muốn sử dụng các công trình phức tạp hơn, bạn phải sử dụng một ngã ba (quy trình con mới thực hiện công việc và trả về kết quả thông qua stdout
):
foo="Hello"
foo=$(printf "%s World" "$foo")
echo $foo
Hello World
Trong lịch sử, bạn có thể sử dụng backticks để lấy kết quả của một ngã ba :
foo="Hello"
foo=`printf "%s World" "$foo"`
echo $foo
Hello World
Nhưng điều này không dễ dàng để làm tổ :
foo="Today is: "
foo=$(printf "%s %s" "$foo" "$(date)")
echo $foo
Today is: Sun Aug 4 11:58:23 CEST 2013
với backticks, bạn phải thoát các nhánh bên trong bằng dấu gạch chéo ngược :
foo="Today is: "
foo=`printf "%s %s" "$foo" "\`date\`"`
echo $foo
Today is: Sun Aug 4 11:59:10 CEST 2013
+=
nhà điều hành cũng là nhanh hơn nhiều so với $a="$a$b"
trong các thử nghiệm của tôi .. Mà làm cho ý nghĩa.
var=${var}.sh
ví dụ từ các câu trả lời khác, rất hữu ích.
bash
vỏ duy nhất với +=
toán tử? Tôi muốn xem nó có đủ di động không
+=
toán tử, nhưng tất cả các cách này là bashism , vì vậy không thể di động! Thậm chí bạn có thể gặp lỗi đặc biệt trong trường hợp phiên bản bash sai!
Bạn cũng có thể làm nó như vậy:
$ var="myscript"
$ echo $var
myscript
$ var=${var}.sh
$ echo $var
myscript.sh
var=myscript;var=$var.sh;echo $var
sẽ có hiệu ứng tương tự (Công việc này dưới bash, dash, busybox và các dấu khác).
echo $var2
không sản xuấtmyscript2
.
bất hợp pháp trong tên biến. Nếu khác echo ${var}2
hoặc xem câu trả lời của tôi
bla=hello
laber=kthx
echo "${bla}ohai${laber}bye"
Sẽ xuất
helloohaikthxbye
Điều này rất hữu ích khi
$blaohai
dẫn đến một biến không tìm thấy lỗi. Hoặc nếu bạn có khoảng trắng hoặc các ký tự đặc biệt khác trong chuỗi của bạn. "${foo}"
đúng cách thoát khỏi bất cứ điều gì bạn đặt vào nó.
foo="Hello "
foo="$foo World"
Cách tôi giải quyết vấn đề chỉ là
$a$b
Ví dụ,
a="Hello"
b=" World"
c=$a$b
echo "$c"
sản xuất
Hello World
Ví dụ, nếu bạn cố nối một chuỗi với một chuỗi khác,
a="Hello"
c="$a World"
sau đó echo "$c"
sẽ sản xuất
Hello World
với một không gian thêm.
$aWorld
không hoạt động, như bạn có thể tưởng tượng, nhưng
${a}World
sản xuất
HelloWorld
${a}\ World
tạo raHello World
c=$a$b
ở đây sẽ làm điều tương tự như c=$a World
(mà sẽ cố chạy World
như một lệnh). Tôi đoán điều đó có nghĩa là bài tập được phân tích cú pháp trước khi các biến được mở rộng ..
Dưới đây là một bản tóm tắt ngắn gọn về những gì hầu hết các câu trả lời đang nói về.
Giả sử chúng ta có hai biến và $ 1 được đặt thành 'một':
set one two
a=hello
b=world
Bảng dưới đây giải thích các bối cảnh khác nhau nơi chúng ta có thể kết hợp các giá trị a
và b
để tạo một biến mới , 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
Một vài lưu ý:
+=
sẽ tốt hơn từ quan điểm hiệu suất nếu một chuỗi lớn đang được xây dựng theo từng bước nhỏ, đặc biệt là trong một vòng lặp{}
xung quanh tên biến để định hướng mở rộng của chúng (như trong hàng 2 trong bảng trên). Như đã thấy trên các hàng 3 và 4, không cần thiết {}
trừ khi một biến được nối với một chuỗi bắt đầu bằng một ký tự là ký tự đầu tiên hợp lệ trong tên biến shell, đó là bảng chữ cái hoặc dấu gạch dưới.Xem thêm:
Một cách tiếp cận khác ...
> H="Hello "
> U="$H""universe."
> echo $U
Hello universe.
... và còn một cái nữa.
> H="Hello "
> U=$H"universe."
> echo $U
Hello universe.
Nếu bạn muốn nối thêm một cái gì đó như dấu gạch dưới, hãy sử dụng esc (\)
FILEPATH=/opt/myfile
Điều này không hoạt động:
echo $FILEPATH_$DATEX
Điều này hoạt động tốt:
echo $FILEPATH\\_$DATEX
echo $a\_$b
sẽ làm. Như được gợi ý trong nhận xét của Nik O'Lai, gạch dưới là một nhân vật bình thường. Việc xử lý các khoảng trắng nhạy hơn nhiều đối với các chuỗi, tiếng vang và nối --- người ta có thể sử dụng \
và đọc kỹ chủ đề này vì vấn đề này trở lại ngay bây giờ và sau đó.
Cách đơn giản nhất với dấu ngoặc kép:
B=Bar
b=bar
var="$B""$b""a"
echo "Hello ""$var"
var=$B$b"a"; echo Hello\ $var
tôi sẽ làm, tôi tin
Bạn có thể nối mà không có dấu ngoặc kép. Đây là một ví dụ:
$Variable1 Open
$Variable2 Systems
$Variable3 $Variable1$Variable2
$echo $Variable3
Tuyên bố cuối cùng này sẽ in "OpenSystems" (không có dấu ngoặc kép).
Đây là một ví dụ về tập lệnh Bash:
v1=hello
v2=world
v3="$v1 $v2"
echo $v3 # Output: hello world
echo "$v3" # Output: hello world
Ngay cả khi toán tử + = hiện được phép, nó đã được giới thiệu trong Bash 3.1 năm 2004.
Bất kỳ tập lệnh nào sử dụng toán tử này trên các phiên bản Bash cũ hơn sẽ không thành công với lỗi "không tìm thấy lệnh" nếu bạn may mắn hoặc "lỗi cú pháp gần mã thông báo không mong muốn".
Đối với những người quan tâm đến khả năng tương thích ngược, hãy sử dụng các phương pháp nối Bash tiêu chuẩn cũ hơn, giống như các phương pháp được đề cập trong câu trả lời đã chọn:
foo="Hello"
foo="$foo World"
echo $foo
> Hello World
Tôi thích sử dụng dấu ngoặc nhọn ${}
để mở rộng biến trong chuỗi:
foo="Hello"
foo="${foo} World"
echo $foo
> Hello World
Dấu ngoặc nhọn sẽ phù hợp với việc sử dụng chuỗi liên tục:
foo="Hello"
foo="${foo}World"
echo $foo
> HelloWorld
Nếu không sử dụng foo = "$fooWorld"
sẽ không hoạt động.
Nếu những gì bạn đang cố gắng làm là chia một chuỗi thành nhiều dòng, bạn có thể sử dụng dấu gạch chéo ngược:
$ a="hello\
> world"
$ echo $a
helloworld
Với một khoảng trống ở giữa:
$ a="hello \
> world"
$ echo $a
hello world
Điều này cũng chỉ thêm một không gian ở giữa:
$ a="hello \
> world"
$ echo $a
hello world
Cách an toàn hơn:
a="AAAAAAAAAAAA"
b="BBBBBBBBBBBB"
c="CCCCCCCCCCCC"
d="DD DD"
s="${a}${b}${c}${d}"
echo "$s"
AAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCDD DD
Các chuỗi chứa khoảng trắng có thể trở thành một phần của lệnh, sử dụng "$ XXX" và "$ {XXX}" để tránh các lỗi này.
Ngoài ra, hãy xem câu trả lời khác về + =
d=DD DD
sẽ cung cấp cho DD: command not found
--- lưu ý rằng đó là DD cuối cùng, chứ không phải là không tìm thấy. Nếu tất cả các toán hạng được định dạng chính xác và đã chứa các khoảng trắng cần thiết, bạn có thể chỉ cần ghép nối với s=${a}${b}${c}${d}; echo $s
, với ít dấu ngoặc kép hơn. Ngoài ra, bạn có thể sử dụng \
(khoảng trắng thoát) để tránh những vấn đề này --- d=echo\ echo
sẽ không khởi chạy bất kỳ lời gọi tiếng vang nào, trong khi đó d=echo echo
sẽ.
a="Hello,"
a=$a" World!"
echo $a
Đây là cách bạn nối hai chuỗi.
var1='hello'
var2='world'
var3=$var1" "$var2
echo $var3
var3=$var1\ $var2
có tác dụng tương tự
Có những lo ngại về hiệu suất, nhưng không có dữ liệu được cung cấp. Hãy để tôi đề nghị một bài kiểm tra đơn giản.
(GHI CHÚ: date
trên macOS không cung cấp nano giây, vì vậy điều này phải được thực hiện trên Linux.)
Tôi đã tạo append_test.sh trên GitHub với nội dung:
#!/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
Kiểm tra 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
Bài kiểm tra 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
Các lỗi chỉ ra rằng Bash của tôi đã lên tới 335.54432 MB trước khi nó bị hỏng. Bạn có thể thay đổi mã từ nhân đôi dữ liệu sang nối thêm hằng số để có được biểu đồ chi tiết hơn và điểm thất bại. Nhưng tôi nghĩ điều này sẽ cung cấp cho bạn đủ thông tin để quyết định xem bạn có quan tâm hay không. Cá nhân, dưới 100 MB tôi không. Số dặm của bạn có thể thay đổi.
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"
(Hãy thử điều này trên máy chủ không hiệu quả !!;)
Tôi muốn xây dựng một chuỗi từ một danh sách. Không thể tìm thấy câu trả lời cho điều đó vì vậy tôi đăng nó ở đây. Đây là những gì tôi đã làm:
list=(1 2 3 4 5)
string=''
for elm in "${list[@]}"; do
string="${string} ${elm}"
done
echo ${string}
và sau đó tôi nhận được đầu ra sau đây:
1 2 3 4 5
Lưu ý rằng điều này sẽ không hoạt động
foo=HELLO
bar=WORLD
foobar=PREFIX_$foo_$bar
vì nó dường như giảm $ foo và để lại cho bạn:
PREFIX_WORLD
nhưng điều này sẽ làm việc:
foobar=PREFIX_"$foo"_"$bar"
và để lại cho bạn đầu ra chính xác:
PREFIX_HELLO_WORLD
Tôi làm theo cách này khi thuận tiện: Sử dụng lệnh nội tuyến!
echo "The current time is `date`"
echo "Current User: `echo $USER`"
date "+The current time is %a %b %d %Y +%T"
, thay vì echo ...$(date)
. Theo bash gần đây, bạn có thể viết : printf "The current time is %(%a %b %d %Y +%T)T\n" -1
.
Theo tôi, cách đơn giản nhất để nối hai chuỗi là viết một hàm thực hiện nó cho bạn, sau đó sử dụng hàm đó.
function concat ()
{
prefix=$1
suffix=$2
echo "${prefix}${suffix}"
}
foo="Super"
bar="man"
concat $foo $bar # Superman
alien=$(concat $foo $bar)
echo $alien # Superman
Tôi giống như làm một chức năng nhanh chóng.
#! /bin/sh -f
function combo() {
echo $@
}
echo $(combo 'foo''bar')
Một cách khác để lột da một con mèo. Lần này với các chức năng: D
Tôi chưa biết về PHP, nhưng điều này hoạt động trong Linux Bash. Nếu bạn không muốn ảnh hưởng đến nó đến một biến, bạn có thể thử điều này:
read pp; *# Assumes I will affect Hello to pp*
pp=$( printf $pp ;printf ' World'; printf '!');
echo $pp;
>Hello World!
Bạn có thể đặt một biến khác thay vì 'Xin chào' hoặc '!'. Bạn cũng có thể nối nhiều chuỗi hơn.
foo="Hello"
foo=$foo" World"
echo $foo
cái này khá hiệu quả với "#! / bin / sh"