2012-11-20 47 views
20

Tôi muốn biết những điều sau đây;Chuyển đổi mảng Bash thành chuỗi được phân tách

  1. Tại sao ví dụ không hoạt động nhất định không hoạt động.
  2. Nếu có bất kỳ phương pháp nào khác sạch hơn so với những gì được đưa ra trong ví dụ làm việc.

không làm việc ví dụ

> ids=(1 2 3 4);echo ${ids[*]// /|} 
1 2 3 4 
> ids=(1 2 3 4);echo ${${ids[*]}// /|} 
-bash: ${${ids[*]}// /|}: bad substitution 
> ids=(1 2 3 4);echo ${"${ids[*]}"// /|} 
-bash: ${"${ids[*]}"// /|}: bad substitution 

dụ làm việc

> ids=(1 2 3 4);id="${ids[@]}";echo ${id// /|} 
1|2|3|4 
> ids=(1 2 3 4); lst=$(IFS='|'; echo "${ids[*]}"); echo $lst 
1|2|3|4 

Trong bối cảnh đó, các chuỗi phân định được sử dụng trong một lệnh sed cho phân tích thêm.

+1

'$ {$ {id [*]} ///|}' là một lỗi cú pháp, đó là tất cả các. Dunno những gì bạn đang cố gắng đạt được ở đây. –

+0

Cố gắng để đạt được thay thế biến trong 1 hop, nó sẽ không bao giờ làm việc ... – koola

Trả lời

23
# REVISION: 2017-03-14 
# Use of read and other bash specific features (bashisms) 

Vì ngoặc được sử dụng để phân định một mảng, không phải là một chuỗi :

ids="1 2 3 4";echo ${ids// /|} 
1|2|3|4 

Một số mẫu: Populating $ids với hai chuỗi: a bc d

ids=("a b" "c d") 

echo ${ids[*]// /|} 
a|b c|d 

IFS='|';echo "${ids[*]}";IFS=$' \t\n' 
a b|c d 

... và cuối cùng:

IFS='|';echo "${ids[*]// /|}";IFS=$' \t\n' 
a|b|c|d 

đâu mảng được lắp ráp, cách nhau bằng char 1 của $IFS, nhưng với không gian thay thế bằng | trong mỗi phần tử của mảng.

Khi bạn làm:

id="${ids[@]}" 

bạn chuyển chuỗi xây dựng từ sự kết hợp của các mảngids bởi một không gian cho một biến mới của loại chuỗi.

Lưu ý: khi "${ids[@]}" cho một không gian tách chuỗi, "${ids[*]}" (với một ngôi sao * thay vì những lúc đăng nhập @) sẽ làm cho một chuỗi phân cách bằng ký tự đầu tiên của $IFS.

man bash nói:

man -Len -Pcol\ -b bash | sed -ne '/^ *IFS /{N;N;p;q}' 
    IFS The Internal Field Separator that is used for word splitting 
      after expansion and to split lines into words with the read 
      builtin command. The default value is ``<space><tab><newline>''. 

Chơi với $IFS:

set | grep ^IFS= 
IFS=$' \t\n' 

declare -p IFS 
declare -- IFS=" 
" 
printf "%q\n" "$IFS" 
$' \t\n' 

Nghĩa đen là space, một tabulation(có nghĩa là hoặc) a line-feed. Vì vậy, trong khi ký tự đầu tiên là một khoảng trắng. việc sử dụng * sẽ thực hiện giống như @.

Nhưng:

{ 

# OIFS="$IFS" 
    # IFS=$': \t\n' 
    # unset array 
    # declare -a array=($(echo root:x:0:0:root:/root:/bin/bash)) 

IFS=: read -a array < <(echo root:x:0:0:root:/root:/bin/bash) 

    echo 1 "${array[@]}" 
    echo 2 "${array[*]}" 
    OIFS="$IFS" IFS=: 
    echo 3 "${array[@]}" 
    echo 4 "${array[*]}" 
    IFS="$OIFS" 
} 
1 root x 0 0 root /root /bin/bash 
2 root x 0 0 root /root /bin/bash 
3 root x 0 0 root /root /bin/bash 
4 root:x:0:0:root:/root:/bin/bash 

Lưu ý: Dòng IFS=: read -a array < <(...) sẽ sử dụng : làm dấu phân cách, không được đặt $IFS vĩnh viễn. Điều này là do dòng đầu ra #2 không gian hiện tại làm dấu phân cách.

+0

Vì vậy, nó là cả một typeof và lỗi thay thế biến cho $ {là mong đợi một var của chuỗi loại nhưng không nhận được. Cảm ơn bạn đã giải thích chi tiết. – koola

+0

Người ta cũng có thể bỏ qua việc gán chuỗi và vẫn chuyển đổi một mảng thành một chuỗi phân cách * với dấu phân cách tùy ý *, không nhất thiết là một ký tự đơn, sử dụng 'printf '% smyDelim'" $ {array [@]} "'. Ví dụ cuối cùng của dấu phân cách 'myDelim' có thể được loại bỏ bằng cách đường ống vào' sed -e 's/myDelim $ //' ', nhưng điều đó rất cồng kềnh. Ý tưởng tốt hơn? –

+1

@JonathanY. Nếu vậy, hãy sử dụng 'printf -v myVar '% smyDelim'" $ {array [@]} "; myVar = "$ {myVar% myDelim}" 'thay vì ngã ba thành' sed' –

9

Câu hỏi đầu tiên của bạn đã được giải quyết trong câu trả lời của F. Hauri. Dưới đây là cách kinh điển để tham gia các phần tử của một mảng:

ids=(1 2 3 4) 
IFS=\| eval 'lst="${ids[*]}"' 

Một số người sẽ khóc thật to rằng eval là ác, nhưng nó hoàn toàn an toàn ở đây, nhờ vào dấu nháy đơn. Điều này chỉ có lợi thế: không có subshells, IFS không phải là toàn cầu sửa đổi, nó sẽ không cắt trailing newlines, và nó rất đơn giản.

0

Bạn có thể sử dụng printf quá, mà không cần bất kỳ lệnh bên ngoài hoặc cần phải đối phó với IFS:

ids=(1 2 3 4) 
printf -v ids_d '|%s' "${ids[@]}" # yields "|1|2|3|4" 
ids_d=${ids_d:1}     # remove the leading '|' 
Các vấn đề liên quan