我有兩個 CSV 檔案,如下所示:
檔案 1.csv
label,"Part-A"
"ABC mn","2.0"
"XYZ","3.0"
"PQR SN","6"
檔案2.csv
label,"Part-B"
"XYZ","4.0"
"LMN Wv","8"
"PQR SN","6"
"EFG","1.0"
所需的輸出.csv
label,"Part-A","Part-B"
"ABC mn","2.0",NA
"EFG",NA,"1.0"
"LMN Wv",NA,"8"
"PQR SN","6","6"
"XYZ","3.0","4.0"
目前使用下面的 awk 命令,我可以組合在 PQR 和 XYZ 等檔案中都有標簽條目的匹配命令,但無法附加兩個檔案中都沒有標簽值的那些:
awk -F, 'NR==FNR{a[$1]=substr($0,length($1) 2);next} ($1 in a){print $0","a[$1]}' file1.csv file2.csv
uj5u.com熱心網友回復:
該解決方案使用任何 AWK 都可以準確地列印出希望的結果。請注意,排序演算法取自 mawk 手冊。
# SO71053039.awk
#-------------------------------------------------
# insertion sort of A[1..n]
function isort( A,A_SWAP, n,i,j,hold ) {
n = 0
for (j in A)
A_SWAP[ n] = j
for( i = 2 ; i <= n ; i )
{
hold = A_SWAP[j = i]
while ( A_SWAP[j-1] "" > "" hold )
{ j-- ; A_SWAP[j 1] = A_SWAP[j] }
A_SWAP[j] = hold
}
# sentinel A_SWAP[0] = "" will be created if needed
return n
}
BEGIN {
FS = OFS = ","
out = "Output.csv"
# read file 1
fnr = 0
while ((getline < ARGV[1]) > 0) {
fnr
if (fnr == 1) {
for (i=1; i<=NF; i )
FIELDBYNAME1[$i] = i # e.g. FIELDBYNAME1["label"] = 1
}
else {
LABEL_KEY[$FIELDBYNAME1["label"]]
LABEL_KEY1[$FIELDBYNAME1["label"]] = $FIELDBYNAME1["\"Part-A\""]
}
}
close(ARGV[1])
# read file2
fnr = 0
while ((getline < ARGV[2]) > 0) {
fnr
if (fnr == 1) {
for (i=1; i<=NF; i )
FIELDBYNAME2[$i] = i # e.g. FIELDBYNAME2["label"] = 1
}
else {
LABEL_KEY[$FIELDBYNAME2["label"]]
LABEL_KEY2[$FIELDBYNAME2["label"]] = $FIELDBYNAME2["\"Part-B\""]
}
}
close(ARGV[2])
# print the header
print "label" OFS "\"Part-A\"" OFS "\"Part-B\"" > out
# get the result
z = isort(LABEL_KEY, LABEL_KEY_SWAP)
for (i = 1; i <= z; i ) {
result_string = sprintf("%s", LABEL_KEY_SWAP[i])
if (LABEL_KEY_SWAP[i] in LABEL_KEY1)
result_string = sprintf("%s", result_string OFS LABEL_KEY1[LABEL_KEY_SWAP[i]] OFS (LABEL_KEY_SWAP[i] in LABEL_KEY2 ? LABEL_KEY2[LABEL_KEY_SWAP[i]] : "NA"))
else
result_string = sprintf("%s", result_string OFS "NA" OFS LABEL_KEY2[LABEL_KEY_SWAP[i]])
print result_string > out
}
}
稱呼:
awk -f SO71053039.awk file1.csv file2.csv
=> result file Output.csv with content:
label,"Part-A","Part-B"
"ABC mn","2.0",NA
"EFG",NA,"1.0"
"LMN Wv",NA,"8"
"PQR SN","6","6"
"XYZ","3.0","4.0"
uj5u.com熱心網友回復:
我想向您介紹米勒。它是一個可以用幾種檔案格式做一些事情的工具,并且可以作為獨立的二進制檔案使用。您只需下載存檔,將mlr可執行檔案放在某處(最好在您的 PATH 中),然后您就完成了安裝。
mlr --csv \
join -f file1.csv -j 'label' --ul --ur \
then \
unsparsify --fill-with 'NA' \
then \
sort -f 'label' \
file2.csv
命令部分:
mlr --csv
表示您要讀取 CSV 檔案并輸出 CSV 格式。作為另一個例子,如果你想讀取 CSV 檔案并輸出 JSON 格式,它會是mlr --icsv --ojsonjoin -f file1.csv -j 'label' --ul --ur......file2.csv
表示加入file1.csv和file2.csv上場label并發出兩個檔案的不匹配記錄then是米勒的鏈式操作方式unsparsify --fill-with 'NA'
表示創建每個檔案中不存在的欄位并用NA. 具有 uniq 標簽的記錄需要它thensort -f 'label'
表示對欄位上的記錄進行排序label
關于更新的問題: mlr自行處理 CSV 參考。與您的新預期輸出的唯一區別是它洗掉了多余的引號:
label,Part-A,Part-B
ABC mn,2.0,NA
EFG,NA,1.0
LMN Wv,NA,8
PQR SN,6,6
XYZ,3.0,4.0
uj5u.com熱心網友回復:
awk -v OFS=, '{
if(!o1[$1]) { o1[$1]=$NF; o2[$1]="NA" } else { o2[$1]=$NF }
}
END{
for(v in o1) { print v, o1[v], o2[v] }
}' file{1,2}
## output
LMN,8,NA
ABC,2,NA
PQR,6,6
EFG,1,NA
XYZ,3,4
我認為這會做得很好。
uj5u.com熱心網友回復:
我們建議gawk使用標準 Linux 的腳本awk:
腳本.awk
NR == FNR {
valsStr = sprintf("%s,%s", $2, "na");
rowsArr[$1] = valsStr;
}
NR != FNR && $1 in rowsArr {
split(rowsArr[$1],valsArr);
valsStr = sprintf("%s,%s", valsArr[1], $2);
rowsArr[$1] = valsStr;
next;
}
NR != FNR {
valsStr = sprintf("%s,%s", "na", $2);
rowsArr[$1] = valsStr;
}
END {
printf("%s,%s\n", "label", rowsArr["label"]);
for (rowName in rowsArr) {
if (rowName == "label") continue;
printf("%s,%s\n", rowName, rowsArr[rowName]);
}
}
輸出:
awk -F, -f script.awk input.{1,2}.txt
label,Part-A,Part-B
LMN,na,8
ABC,2,na
PQR,6,6
EFG,na,1
XYZ,3,4
uj5u.com熱心網友回復:
由于您的問題的標題是“如何在 shell 腳本中...... 不一定要使用awk,我將推薦GoCSV,這是一個命令列工具,帶有幾個用于處理 CSV(分隔檔案)的子命令。
它沒有一個命令可以完成您需要的操作,但您可以組合多個命令來獲得正確的結果。
該方案的核心是join命令,可以進行內(默認)、左、右、外連接;你想要一個外連接來保持不重疊的元素:
gocsv join -c 'label' -outer file1.csv file2.csv > joined.csv
echo 'Joined'
gocsv view joined.csv
Joined
------- -------- ------- --------
| label | Part-A | label | Part-B |
------- -------- ------- --------
| ABC | 2 | | |
------- -------- ------- --------
| XYZ | 3 | XYZ | 4 |
------- -------- ------- --------
| PQR | 6 | PQR | 6 |
------- -------- ------- --------
| | | LMN | 8 |
------- -------- ------- --------
| | | EFG | 1 |
------- -------- ------- --------
資料部分是正確的,但需要一些作業才能使列正確,并在NA其中獲取值。
這是一個完整的管道:
gocsv join -c 'label' -outer file1.csv file2.csv \
| gocsv rename -c 1 -names 'Label_A' \
| gocsv rename -c 3 -names 'Label_B' \
| gocsv add -name 'label' -t '{{ list .Label_A .Label_B | compact | first }}' \
| gocsv select -c 'label','Part-A','Part-B' \
| gocsv replace -c 'Part-A','Part-B' -regex '^$' -repl 'NA' \
| gocsv sort -c 'label' \
> final.csv
echo 'Final'
gocsv view final.csv
這為我們提供了正確的最終檔案:
Final pipeline
------- -------- --------
| label | Part-A | Part-B |
------- -------- --------
| ABC | 2 | NA |
------- -------- --------
| EFG | NA | 1 |
------- -------- --------
| LMN | NA | 8 |
------- -------- --------
| PQR | 6 | 6 |
------- -------- --------
| XYZ | 3 | 4 |
------- -------- --------
這條管道中有很多事情要做,重點是:
合并兩個標簽欄位
| gocsv rename -c 1 -names 'Label_A' \
| gocsv rename -c 3 -names 'Label_B' \
| gocsv add -name 'label' -t '{{ list .Label_A .Label_B | compact | first }}' \
減少到您想要的 3 列
| gocsv select -c 'label','Part-A','Part-B' \
添加 NA 值并按標簽排序
| gocsv replace -c 'Part-A','Part-B' -regex '^$' -repl 'NA' \
| gocsv sort -c 'label' \
我已在此 Gist中進行了逐步解釋。
uj5u.com熱心網友回復:
您在我的其他答案的評論中提到了加入,我忘記了這個實用程式:
#!/bin/sh
rm -f *sorted.csv
# Join two files, normally inner-join only, but
# - `-a 1 -a 2`: include "unpaired lines" from file 1 and file 2
# - `-1 1 -2 1`: the first column from each is the "join column"
# - `-o 0,1.2,2.2`: output the "join column" (0) and the second fields from files 1 and 2
join -a 1 -a 2 -1 1 -2 1 -o '0,1.2,2.2' -t, file1.csv file2.csv > joined.csv
# Add NA values
cat joined.csv | sed 's/,,/,NA,/' | sed 's/,$/,NA/' > unsorted.csv
# Sort, pull out header first
head -n 1 unsorted.csv > sorted.csv
# Then sort remainder
tail -n 2 unsorted.csv | sort -t, -k 1 >> sorted.csv
而且,這里是sorted.csv
-------- -------- --------
| label | Part-A | Part-B |
-------- -------- --------
| ABC mn | 2.0 | NA |
-------- -------- --------
| EFG | NA | 1.0 |
-------- -------- --------
| LMN Wv | NA | 8 |
-------- -------- --------
| PQR SN | 6 | 6 |
-------- -------- --------
| XYZ | 3.0 | 4.0 |
-------- -------- --------
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/428241.html
上一篇:如何將陣列的行更改為相反的
下一篇:通過加入僅選擇沒有未來日期的記錄
