Làm cách nào tôi có thể thực hiện các thao tác sau với tệp CSV bằng cách sử dụng sed
hoặc awk
?
- Xóa một cột
- Nhân đôi một cột
- Di chuyển một cột
Tôi có một bàn lớn với hơn 200 hàng và tôi không quen thuộc lắm sed
.
Làm cách nào tôi có thể thực hiện các thao tác sau với tệp CSV bằng cách sử dụng sed
hoặc awk
?
Tôi có một bàn lớn với hơn 200 hàng và tôi không quen thuộc lắm sed
.
Câu trả lời:
Ngoài cách cắt và sắp xếp lại các trường (được nêu trong các câu trả lời khác), còn có vấn đề về các trường CSV kỳ quặc.
Nếu dữ liệu của bạn rơi vào danh mục "kỳ quặc" này, một chút lọc trước và sau có thể xử lý nó. Các bộ lọc hiển thị dưới đây yêu cầu các nhân vật \x01
, \x02
, \x03
, \x04
để không xuất hiện bất cứ nơi nào trong dữ liệu của bạn.
Dưới đây là các bộ lọc bao quanh một awk
bãi chứa trường đơn giản .
Lưu ý: trường năm có bố cục "trường được trích dẫn" không hợp lệ / không đầy đủ, nhưng nó là lành tính ở cuối hàng (tùy thuộc vào trình phân tích cú pháp CSV). Nhưng, tất nhiên, nó sẽ gây ra kết quả không có vấn đề nếu nó bị tráo đổi khỏi vị trí cuối hàng hiện tại .
Cập nhật; user121196 đã chỉ ra một lỗi khi dấu phẩy đứng trước dấu ngoặc kép . Đây là cách khắc phục.
Dữ liệu
cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF
Mật mã
sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g'
Đầu ra:
field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five
"15111 N. Hayden Rd., Ste 160,"
""
Đây là bộ lọc trước , mở rộng với ý kiến.
Bộ lọc bài chỉ là một sự đảo ngược của \x01
. \x02
, \x03
,\x04
sed -r '
s/^/,/ # add a leading comma delimiter
s/\\"/\x01/g # obfuscate escaped quotation-mark (\")
s/,"([^"]*)"/,\x02\1\x03/g # obfuscate quotation-marks
s/,"/,\x02/ # when no trailing quote on last field
:MC # obfuscate commas embedded in quotes
s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
tMC
s/^,// # remove spurious leading delimiter
'
Điều này phụ thuộc vào việc tệp CSV của bạn chỉ sử dụng dấu phẩy cho dấu phân cách hoặc nếu bạn có sự điên rồ như:
trường một, "trường, hai", trường ba
Điều này giả sử bạn đang sử dụng tệp CSV đơn giản:
Bạn có thể thoát khỏi một cột duy nhất theo nhiều cách; Tôi đã sử dụng cột 2 làm ví dụ. Cách dễ nhất có lẽ là sử dụng cut
, cho phép bạn chỉ định một dấu phân cách -d
và trường nào bạn muốn in -f
; điều này bảo nó phân chia trên dấu phẩy và trường đầu ra 1 và trường 3 đến hết:
$ cut -d, -f1,3- /path/to/your/file
Nếu bạn thực sự cần sử dụng sed
, bạn có thể viết một biểu thức chính quy khớp với các n-1
trường đầu tiên , trường n
thứ nhất và phần còn lại và bỏ qua đầu ra n
(đây n
là 2, vì vậy nhóm đầu tiên được khớp với 1
thời gian \{1\}
:):
$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file
Có một số cách để làm điều này awk
, không có cách nào đặc biệt thanh lịch. Bạn có thể sử dụng một for
vòng lặp, nhưng xử lý dấu phẩy là một nỗi đau; bỏ qua rằng nó sẽ là một cái gì đó như:
$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file
Tôi thấy việc xuất trường 1 dễ dàng hơn và sau đó sử dụng substr
để loại bỏ mọi thứ sau trường 2:
$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file
Điều này gây khó chịu cho các cột hơn nữa dọc theo mặc dù
Về sed
cơ bản, đây là biểu thức giống như trước đây, nhưng bạn cũng nắm bắt được cột mục tiêu và bao gồm nhóm đó nhiều lần trong thay thế:
$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file
Theo awk
cách lặp for, nó sẽ giống như (một lần nữa bỏ qua dấu phẩy):
$ awk -F, '{
for(i=1; i<=NF; i++) {
if(i == 2) printf "%s,", $i;
printf "%s,", $i
}
print NL
}' /path/to/your/file
Các substr
cách:
$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file
(tcdyl đã đưa ra một phương pháp tốt hơn trong câu trả lời của mình )
Tôi nghĩ rằng sed
giải pháp theo tự nhiên từ những người khác, nhưng nó bắt đầu dài ra một cách lố bịch
awk
là đặt cược tốt nhất của bạn. awk
in các trường theo số, vì vậy ...
awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file
Để xóa một cột, không in nó:
awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file
Để thay đổi thứ tự:
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file
Chuyển hướng đến một tập tin đầu ra.
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file
awk
có thể định dạng đầu ra là tốt.
Cho một tệp được phân tách bằng dấu cách theo định dạng sau:
1 2 3 4 5
Bạn có thể xóa trường 2 bằng awk như vậy:
awk '{ sub($2,""); print}' file
trả về
1 3 4 5
Thay cột 2 bằng cột n khi thích hợp.
Để nhân đôi cột 2,
awk '{ col = $2 " " $2; $2 = col; print }' file
trả về
1 2 2 3 4 5
Để chuyển cột 2 và 3,
awk '{temp = $2; $2 = $3; $3 = temp; print}'
trả về
1 3 2 4 5
awk thường rất giỏi trong việc xử lý khái niệm về các lĩnh vực . Nếu bạn đang xử lý CSV và không phải là tệp được phân tách bằng dấu cách, bạn chỉ cần sử dụng
awk -F,
để xác định trường của bạn dưới dạng dấu phẩy, thay vì dấu cách (là mặc định). Có một số tài nguyên awk tốt trực tuyến, một trong số đó tôi liệt kê dưới dạng nguồn dưới đây.
Nguồn cho # 3
awk
nó, nhưng dường như đầu ra được phân tách bằng dấu cách ngay cả khi dấu tách trường là ,
(dấu tách trường chỉ kiểm soát cách nó xử lý đầu vào)
Điều này sẽ làm việc để xóa
awk '{$2="";$0=$0;$1=$1}1'
Đầu vào
a b c d
Đầu ra
a c d