Lặp qua một biến shell được phân tách bằng dấu phẩy


108

Giả sử tôi có một biến Unix shell như bên dưới

variable=abc,def,ghij

Tôi muốn trích xuất tất cả các giá trị ( abc, defghij) sử dụng một vòng lặp for và vượt qua từng giá trị vào một thủ tục.

Tập lệnh phải cho phép trích xuất số lượng giá trị được phân tách bằng dấu phẩy tùy ý từ $variable.


Chính xác thì bạn muốn làm gì bằng cách lặp lại?
SMA

1
Tôi muốn chuyển mỗi trường (ví dụ abc) như một đối số cho một thủ tục.
Ramanathan K

Câu trả lời:


124

Bạn có thể sử dụng tập lệnh sau để di chuyển động qua biến của mình, bất kể biến đó có bao nhiêu trường miễn là nó chỉ được phân tách bằng dấu phẩy.

variable=abc,def,ghij
for i in $(echo $variable | sed "s/,/ /g")
do
    # call your procedure/other scripts here below
    echo "$i"
done

Thay vì echo "$i"gọi ở trên, giữa vòng lặp for dodonebên trong, bạn có thể gọi thủ tục của mình proc "$i".


Cập nhật : Đoạn mã trên hoạt động nếu giá trị của biến không chứa khoảng trắng. Nếu bạn có yêu cầu như vậy, vui lòng sử dụng một trong các giải pháp có thể thay đổi IFSvà sau đó phân tích cú pháp biến của bạn.


Hi vọng điêu nay co ich.


8
Đây không phải làm việc nếu $variablecó chứa khoảng trắng, ví dụ variable=a b,c,d=> in 4 dòng (a | b | c | d) chứ không phải là 3 (ab | c | d)
Dan

Cũng như nó được thêm vào các trích dẫn mới như ** "value **. bạn có thể hài lòng cập nhật nếu có bất kỳ regex để loại bỏ ..
GKS

136

Không gây rối với IFS
Không gọi lệnh bên ngoài

variable=abc,def,ghij
for i in ${variable//,/ }
do
    # call your procedure/other scripts here below
    echo "$i"
done

Sử dụng thao tác chuỗi bash http://www.tldp.org/LDP/abs/html/string-manipulation.html


3
Điều thú vị về điều này là bạn có thể lồng nhiều vòng lặp bằng cách sử dụng các dấu phân cách khác nhau. Gợi ý tốt!
Brad Parks

5
Mẹo hay để tránh lộn xộn với IFS!
Laimoncijus

13
Cảnh báo nhỏ - nếu có của các giá trị trong $ichứa khoảng trắng, họ sẽ chia tay (và đó có thể là lý do mà nó được thông qua như bằng dấu phẩy chứ không phải là không gian tách)
Toby Speight

4
Nếu một hàm trước thay đổi cài đặt IFS, thì đây không làm việc ... Thay đổi nó như thế này:for i in ${variable//,/$IFS} do; echo "$i"; done
Joep

2
Tôi nhận được thông báo lỗi "Thay thế không hợp lệ" khi tôi thử phương pháp này.
Lost Crotchet,

55

Nếu bạn đặt một dấu phân tách trường khác, bạn có thể trực tiếp sử dụng một forvòng lặp:

IFS=","
for v in $variable
do
   # things with "$v" ...
done

Bạn cũng có thể lưu trữ các giá trị trong một mảng và sau đó lặp qua nó như được chỉ ra trong Làm cách nào để tách một chuỗi trên dấu phân cách trong Bash? :

IFS=, read -ra values <<< "$variable"
for v in "${values[@]}"
do
   # things with "$v"
done

Kiểm tra

$ variable="abc,def,ghij"
$ IFS=","
$ for v in $variable
> do
> echo "var is $v"
> done
var is abc
var is def
var is ghij

Bạn có thể tìm thấy một cách tiếp cận rộng hơn trong giải pháp này về Cách lặp qua danh sách được phân tách bằng dấu phẩy và thực hiện lệnh cho mỗi mục nhập .

Ví dụ về cách tiếp cận thứ hai:

$ IFS=, read -ra vals <<< "abc,def,ghij"
$ printf "%s\n" "${vals[@]}"
abc
def
ghij
$ for v in "${vals[@]}"; do echo "$v --"; done
abc --
def --
ghij --

Cũng xem stackoverflow.com/questions/918886/… :-) Chúc tất cả may mắn.
shellter

Vâng! Tôi cũng đã nghĩ về điều đó, chỉ là sau đó nó yêu cầu các bước: a whileđể đọc vào một mảng và một bước khác để lặp qua các kết quả.
fedorqui 'SO dừng làm hại'

3
Làm cho tôi hạnh phúc khi ai đó thực sự biết cách sử dụng shell, thay vì kết hợp một loạt các binutils với nhau.
alanwaring

bạn có phải IFSquay lại bất cứ điều gì trước đây không?
pstanton

1
@fedorqui Nó đã làm! Đó là chỉ biến tôi muốn lặp qua và nó làm cho tập tin yaml tôi dễ dàng hơn để đọc (thay vì một biến môi trường DÀI nó có một loạt các hàng)
GammaGames

5
#/bin/bash   
TESTSTR="abc,def,ghij"

for i in $(echo $TESTSTR | tr ',' '\n')
do
echo $i
done

Tôi thích sử dụng tr thay vì sed, vì sed gặp vấn đề với các ký tự đặc biệt như \ r \ n trong một số trường hợp.

giải pháp khác là đặt IFS thành dấu phân tách nhất định


1

Đây là một giải pháp thay thế dựa trên tr không sử dụng tiếng vang, được biểu thị dưới dạng một lớp lót.

for v in $(tr ',' '\n' <<< "$var") ; do something_with "$v" ; done

Nó cảm thấy gọn gàng hơn mà không có tiếng vang nhưng đó chỉ là sở thích cá nhân của tôi.


0

Hãy thử cái này.

#/bin/bash   
testpid="abc,def,ghij" 
count=`echo $testpid | grep -o ',' | wc -l` # this is not a good way
count=`expr $count + 1` 
while [ $count -gt 0 ]  ; do
     echo $testpid | cut -d ',' -f $i
     count=`expr $count - 1 `
done

nhưng điều gì sẽ xảy ra nếu tôi không chắc chắn về số lượng trường trong biến testpid?
Ramanathan K

Ngoài ra, tôi cần chuyển mỗi tệp của biến trên làm đối số cho một thủ tục. Và tôi muốn làm điều này cho đến khi độ dài của biến trở thành 0. (ví dụ, tất cả các lĩnh vực cần được thông qua một lần để các thủ tục để xử lý)
Ramanathan K

Tôi không biết về thủ tục. Từ liên kết này Bạn có thể nhận được số lượng các trường. http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file. Sau đó Biến vòng lặp for đó thành vòng lặp while. Bạn có thể nhận được cái đó.
Karthikeyan.RS

Đây là để đếm các trường từ một tệp mà tôi đoán. Điều gì nếu tôi cần phải đếm các trường trong một biến (giả sử biến là testpid = abc, def, ghij)
Ramanathan K

echo biến và chuyển đầu ra tới awk. Hoặc nếu không hãy xem câu trả lời cập nhật của tôi. Có thể nó là hữu ích.
Karthikeyan.RS

0

Một giải pháp khác không sử dụng IFS và vẫn bảo toàn khoảng trắng:

$ var="a bc,def,ghij"
$ while read line; do echo line="$line"; done < <(echo "$var" | tr ',' '\n')
line=a bc
line=def
line=ghij

Điều này hoạt động chính xác trong bash, tuy nhiên zsh nuốt khoảng trống.
lleaff

0

Đây là giải pháp bash thuần túy của tôi không thay đổi IFS và có thể sử dụng dấu phân cách regex tùy chỉnh.

loop_custom_delimited() {
    local list=$1
    local delimiter=$2
    local item
    if [[ $delimiter != ' ' ]]; then
        list=$(echo $list | sed 's/ /'`echo -e "\010"`'/g' | sed -E "s/$delimiter/ /g")
    fi
    for item in $list; do
        item=$(echo $item | sed 's/'`echo -e "\010"`'/ /g')
        echo "$item"
    done
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.