如何使用 AWK 命令打印 csv 的第 1 列、第 3 列和第 9 列到第 12 列?
How to print column1,column3 and column 9 to column12 of the csv using AWK command?
下面这个命令完美地打印了 csv 文件的 9-12 列
grep -i introd *.csv | awk 'BEGIN { first = 9; last = 12 }{ for (i = first; i < last; i++) {printf("%s ", $i) } print $last }
但我还想打印第 1 列、第 3 列和第 30-35 列。
grep -i introd *.csv | awk 'BEGIN { first = 9; last = 12 }{ print , for (i = first; i < last; i++) {printf("%s ", $i) } print $last
对于少量的字段,很容易明确地完成它们,比如:
{ print ","","","","","","","","","",""," }
如果字段数不太大,这是首选解决方案。
如果 的字段数量更多(您可能与我不同,考虑将 12 个字段包含在其中),您始终可以创建一个关联数组,指示 应该打印哪个字段,包括允许像n-m
这样的范围。答案的这一部分中的代码说明了,首先在 BEGIN
部分:
- 用逗号分隔
COLS
变量,每个元素要么是单个值,要么是范围。
- 对于每一个:
- 拆分连字符以便建立范围界限。
- 如果只有一个值,请将其添加到所需的字段列表中。
- 如果超过一个,使用第一个和第二个作为包含边界并添加该范围内的每个字段。
- 计算出所需的最低和最高字段。
然后,在每个输入行部分:
- 打印出每个字段(从最低到最高)当且仅当它是标记为需要的字段之一时。
执行此操作的代码是:
BEGIN {
split(COLS, ranges, ",")
for (range in ranges) {
split(ranges[range], values, "-")
if (length(values) == 1) {
use[values[1]] = 1
} else {
for (value = values[1]; value <= values[2]; value++) {
use[value] = 1
}
}
}
hi = -1
for (val in use) {
if (hi < 0) {
lo = val
hi = val
} else if (val < lo) {
lo = val
} else if (val > hi) {
hi = val
}
}
}
{
sep = ""
for (idx = lo; idx <= hi; idx++) {
if (use[idx] == 1) {
printf "%s%s", sep, $idx
sep = ","
}
}
print ""
}
此输出显示其工作原理:
pax> echo 101 202 303 404 505 606 707 808 909 | awk -v COLS=2-4,7 -f myprog.awk
202,303,404,707
就两个音符。首先,您不能使用该脚本来打印重复 个字段,例如1-5,3-7
。我不允许这样做,因为您的问题中没有任何内容表明这是一项要求,但是,如果您确实需要该功能,则需要使用不同的方法。
其次,如果您不提供 COLS
或提供的格式不符合预期,则可能会产生意想不到的结果或失败。如果 reader 想让它真正防弹的话,我会把它留作练习 :-)
这两点都没有解决,因为我仍然认为第一个选项最适合您的特定情况。
这是一个通用解决方案,您可以在其中传递由 ,
分隔的多个范围,并且范围值应在其内部由 -
分隔。另外,如果您想传递多个 .csv 文件,是的,您也可以将它们全部传递给该程序:*.csv
。
awk -v range="9-12,30-35" '
BEGIN{
FS=OFS=","
num=split(range,arr,",")
}
{
delete arr1
value=""
for(i=1;i<=num;i++){
split(arr[i],arr1,"-")
for(start=arr1[1];start<=arr1[2];start++){
value=(value?value OFS:"")$start
}
}
print value
}
' Input_file
说明:为以上添加详细说明。
awk -v range="9-12,30-35" ' ##Starting awk program from here and setting range variable which has different ranges, in this case 9 to 12 AND 30 to 35 field numbers here.
BEGIN{ ##Starting BEGIN section of this program from here.
FS=OFS="," ##Setting FS and OFS as comma here.
num=split(range,arr,",") ##Splitting range to arr array with separator of comma here.
}
{
delete arr1 ##Deleting arr1 for safer side.
value="" ##Nullifying value here.
for(i=1;i<=num;i++){ ##Running for loop till value of num here.
split(arr[i],arr1,"-") ##Splitting arr[i[ value into arr1 here with separator of - here.
for(start=arr1[1];start<=arr1[2];start++){ ##Running for loop from arr[1] value to arr1[2] value.
value=(value?value OFS:"")$start ##Creating value which has fields value in it.
}
}
print value ##printing value here.
}
' Input_file ##Mentioning Input_file name here.
像这样的东西(由于没有 input/output 提供测试依据而未经测试)应该这样做:
awk -v r='1 3 9-12 30-35' '
BEGIN {
numRanges = split(r,ranges)
for (rangeNr=1; rangeNr<=numRanges; rangeNr++) {
n = split(ranges[rangeNr],begEnd,/-/)
for (inFldNr=begEnd[1]; inFldNr<=begEnd[n]; inFldNr++) {
out2in[++numOutFlds] = inFldNr
}
}
}
{
for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
inFldNr = out2in[outFldNr]
printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
}
}
' file
下面这个命令完美地打印了 csv 文件的 9-12 列
grep -i introd *.csv | awk 'BEGIN { first = 9; last = 12 }{ for (i = first; i < last; i++) {printf("%s ", $i) } print $last }
但我还想打印第 1 列、第 3 列和第 30-35 列。
grep -i introd *.csv | awk 'BEGIN { first = 9; last = 12 }{ print , for (i = first; i < last; i++) {printf("%s ", $i) } print $last
对于少量的字段,很容易明确地完成它们,比如:
{ print ","","","","","","","","","",""," }
如果字段数不太大,这是首选解决方案。
如果 的字段数量更多(您可能与我不同,考虑将 12 个字段包含在其中),您始终可以创建一个关联数组,指示 应该打印哪个字段,包括允许像n-m
这样的范围。答案的这一部分中的代码说明了,首先在 BEGIN
部分:
- 用逗号分隔
COLS
变量,每个元素要么是单个值,要么是范围。 - 对于每一个:
- 拆分连字符以便建立范围界限。
- 如果只有一个值,请将其添加到所需的字段列表中。
- 如果超过一个,使用第一个和第二个作为包含边界并添加该范围内的每个字段。
- 计算出所需的最低和最高字段。
然后,在每个输入行部分:
- 打印出每个字段(从最低到最高)当且仅当它是标记为需要的字段之一时。
执行此操作的代码是:
BEGIN {
split(COLS, ranges, ",")
for (range in ranges) {
split(ranges[range], values, "-")
if (length(values) == 1) {
use[values[1]] = 1
} else {
for (value = values[1]; value <= values[2]; value++) {
use[value] = 1
}
}
}
hi = -1
for (val in use) {
if (hi < 0) {
lo = val
hi = val
} else if (val < lo) {
lo = val
} else if (val > hi) {
hi = val
}
}
}
{
sep = ""
for (idx = lo; idx <= hi; idx++) {
if (use[idx] == 1) {
printf "%s%s", sep, $idx
sep = ","
}
}
print ""
}
此输出显示其工作原理:
pax> echo 101 202 303 404 505 606 707 808 909 | awk -v COLS=2-4,7 -f myprog.awk
202,303,404,707
就两个音符。首先,您不能使用该脚本来打印重复 个字段,例如1-5,3-7
。我不允许这样做,因为您的问题中没有任何内容表明这是一项要求,但是,如果您确实需要该功能,则需要使用不同的方法。
其次,如果您不提供 COLS
或提供的格式不符合预期,则可能会产生意想不到的结果或失败。如果 reader 想让它真正防弹的话,我会把它留作练习 :-)
这两点都没有解决,因为我仍然认为第一个选项最适合您的特定情况。
这是一个通用解决方案,您可以在其中传递由 ,
分隔的多个范围,并且范围值应在其内部由 -
分隔。另外,如果您想传递多个 .csv 文件,是的,您也可以将它们全部传递给该程序:*.csv
。
awk -v range="9-12,30-35" '
BEGIN{
FS=OFS=","
num=split(range,arr,",")
}
{
delete arr1
value=""
for(i=1;i<=num;i++){
split(arr[i],arr1,"-")
for(start=arr1[1];start<=arr1[2];start++){
value=(value?value OFS:"")$start
}
}
print value
}
' Input_file
说明:为以上添加详细说明。
awk -v range="9-12,30-35" ' ##Starting awk program from here and setting range variable which has different ranges, in this case 9 to 12 AND 30 to 35 field numbers here.
BEGIN{ ##Starting BEGIN section of this program from here.
FS=OFS="," ##Setting FS and OFS as comma here.
num=split(range,arr,",") ##Splitting range to arr array with separator of comma here.
}
{
delete arr1 ##Deleting arr1 for safer side.
value="" ##Nullifying value here.
for(i=1;i<=num;i++){ ##Running for loop till value of num here.
split(arr[i],arr1,"-") ##Splitting arr[i[ value into arr1 here with separator of - here.
for(start=arr1[1];start<=arr1[2];start++){ ##Running for loop from arr[1] value to arr1[2] value.
value=(value?value OFS:"")$start ##Creating value which has fields value in it.
}
}
print value ##printing value here.
}
' Input_file ##Mentioning Input_file name here.
像这样的东西(由于没有 input/output 提供测试依据而未经测试)应该这样做:
awk -v r='1 3 9-12 30-35' '
BEGIN {
numRanges = split(r,ranges)
for (rangeNr=1; rangeNr<=numRanges; rangeNr++) {
n = split(ranges[rangeNr],begEnd,/-/)
for (inFldNr=begEnd[1]; inFldNr<=begEnd[n]; inFldNr++) {
out2in[++numOutFlds] = inFldNr
}
}
}
{
for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
inFldNr = out2in[outFldNr]
printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
}
}
' file