2011-10-10 40 views
36

Tôi muốn tách văn bản bằng dấu phẩy , không cách trong for foo in list. Giả sử tôi có một tập tin CSV CSV_File với văn bản sau đây bên trong nó:Cách chia danh sách bằng dấu phẩy không phải

Hello,World,Questions,Answers,bash shell,script 
... 

tôi đã sử dụng mã sau đây để tách nó ra thành nhiều dòng chữ:

for word in $(cat CSV_File | sed -n 1'p' | tr ',' '\n') 
do echo $word 
done 

It in:

Hello 
World 
Questions 
Answers 
bash 
shell 
script 

Nhưng tôi muốn chia văn bản bằng dấu phẩy không phải dấu cách:

Hello 
World 
Questions 
Answers 
bash shell 
script 

Làm thế nào tôi có thể đạt được điều này trong bash?

+0

Tại sao không sử dụng 'awk'? –

+2

@Carl Bạn có thể cung cấp ví dụ về cách sử dụng 'awk' không? –

Trả lời

38

Sử dụng thay thế vỏ con để phân tích các từ sẽ hoàn tác tất cả công việc bạn đang làm để đặt dấu cách với nhau.

Hãy thử thay vì:

cat CSV_file | sed -n 1'p' | tr ',' '\n' | while read word; do 
    echo $word 
done 

Đó cũng tăng song song. Sử dụng một subshell như trong câu hỏi của bạn buộc toàn bộ quá trình subshell hoàn thành trước khi bạn có thể bắt đầu lặp qua các câu trả lời. Piping cho một subshell (như trong câu trả lời của tôi) cho phép chúng hoạt động song song. Điều này chỉ quan trọng nếu bạn có nhiều dòng trong tập tin, tất nhiên.

+1

Vâng, điều này * là * cách tốt hơn sau đó những gì tôi đã gợi ý. 1 cho các kỹ năng b33t bash mkj :) – chown

+0

+1 Cảm ơn mkj. Đây là những gì tôi đang tìm kiếm –

+1

Thậm chí không cần vòng lặp while. –

8
kent$ echo "Hello,World,Questions,Answers,bash shell,script"|awk -F, '{for (i=1;i<=NF;i++)print $i}' 
Hello 
World 
Questions 
Answers 
bash shell 
script 
+0

+1 Cảm ơn Kent. Tôi sẽ thử giải pháp này sau –

+1

Tôi giả định rằng 'echo $ word' không thực sự là điều thực sự cần được thực hiện với $ word. Trong trường hợp đó, cuộc khai quật của bạn là một cách khác để làm sed và tr trong câu hỏi ban đầu. Tôi nghĩ rằng Eng.Fouad muốn giá trị, với không gian, trong một biến shell để làm một cái gì đó khác với. – mkj

+0

@mkj Giải pháp này là ok để sử dụng thêm dưới dạng biến shell, ví dụ: 'FOO =" Xin chào, Thế giới, Câu hỏi, Trả lời, bash shell, tập lệnh "; BOO = $ (echo $ FOO | awk -F, '{cho (i = 1; i <= NF; i ++) in $ i}'); cho B bằng $ BOO; làm echo "<$B>"; done' –

45

Đặt IFS đến,:

[email protected]:~$ IFS=',' ;for i in `echo "Hello,World,Questions,Answers,bash shell,script"`; do echo $i; done 
Hello 
World 
Questions 
Answers 
bash shell 
script 
[email protected]:~$ 
+0

Tốt đẹp! Tôi quên tất cả về biến env IFS! – chown

+1

Giải pháp sạch nhất, tích hợp nhất. Đây sẽ là câu trả lời. – Marcos

+0

Để sử dụng điều này trong một tập lệnh, bạn nên khôi phục biến IFS về giá trị trước đó. Xem câu trả lời của Andrew Newdigate. – clime

5

đọc: http://linuxmanpages.com/man1/sh.1.php & http://www.gnu.org/s/hello/manual/autoconf/Special-Shell-Variables.html

IFS Các nội Dòng tách được sử dụng cho từ tách sau khi mở rộng và chia dòng vào các từ có lệnh được đọc là . Giá trị mặc định là `` ''.

IFS là biến môi trường vỏ nên nó sẽ không thay đổi trong ngữ cảnh của tập lệnh Shell nhưng không có cách khác trừ khi bạn xuất. C BENG ĐƯỢC CHIA SẺ, rằng IFS sẽ không có khả năng được thừa hưởng từ Môi trường của bạn ở tất cả: xem bài đăng gnu này vì lý do và thông tin thêm về IFS.

Bạn đang đang được viết như thế này:

IFS="," 
for word in $(cat tmptest | sed -n 1'p' | tr ',' '\n'); do echo $word; done; 

nên làm việc, tôi thử nghiệm nó trên dòng lệnh.

sh-3.2#IFS="," 
sh-3.2#for word in $(cat tmptest | sed -n 1'p' | tr ',' '\n'); do echo $word; done; 
World 
Questions 
Answers 
bash shell 
script 
16

Tôi nghĩ rằng phương pháp kinh điển là:

while IFS=, read field1 field2 field3 field4 field5 field6; do 
    do stuff 
done < CSV.file 

Nếu bạn không biết hoặc không quan tâm đến có bao nhiêu lĩnh vực có:

IFS=, 
while read line; do 
    # split into an array 
    field=($line) 
    for word in "${field[@]}"; do echo "$word"; done 

    # or use the positional parameters 
    set -- $line 
    for word in "[email protected]"; do echo "$word"; done 

done < CSV.file 
+0

Rất tiện dụng để có thể tham chiếu đến các trường cụ thể theo tên – HXCaine

+0

@ glenn-jackman Bạn đúng, kinh điển UNIX sẽ sử dụng phương pháp đầu tiên của bạn. Cách thứ hai chỉ hoạt động với việc triển khai hiện đại bash hoặc zsh. –

+1

Lệnh 'read' của bash có tùy chọn' -a' để đọc các từ trong dòng vào một mảng: 'trong khi đọc -a từ; làm cho từ trong "$ {words [@]}" ... ' –

5

Tạo một chức năng bash

split_on_commas() { 
    local IFS=, 
    local WORD_LIST=($1) 
    for word in "${WORD_LIST[@]}"; do 
    echo "$word" 
    done 
} 

split_on_commas "this,is a,list" | while read item; do 
    # Custom logic goes here 
    echo Item: ${item} 
done 

... điều này tạo ra followi ng đầu ra:

Item: this 
Item: is a 
Item: list 

(Lưu ý, câu trả lời này đã được cập nhật theo một số thông tin phản hồi)

+1

Điều này có tác dụng phụ lạ, http://pastebin.com/gNmkzPqj – Val

+0

Lạ. Bất kỳ ý tưởng nào * tại sao * điều đó đang xảy ra? –

+0

Các tác dụng phụ được giải thích tại đây http://superuser.com/questions/781766/ifs-separated-items-in-loop – Val

0

Bạn có thể sử dụng:

cat f.csv | sed 's/,/ /g' | awk '{print $1 "/" $4}' 

hoặc

echo "Hello,World,Questions,Answers,bash shell,script" | sed 's/,/ /g' | awk '{print $1 "/" $4}' 

Đây là phần thay thế dấu phẩy bằng dấu cách

sed 's/,/ /g' 
Các vấn đề liên quan