Изменить порядок столбцов текстового файла в «Пользовательский порядок» с помощью сценария оболочки - PullRequest
4 голосов
/ 31 марта 2020

У меня есть текстовый файл с разделителями табуляции с именем «data.txt», который выглядит как

data.txt

col2   col3   col4   col1
val1   val5   val9   val13
val2   val6   val10  val14
val3   val7   val11  val15
val4   val8   val12  val16
...

, и у меня есть массив col_order = [col1, col2, col3, col4]

Цель состоит в том, чтобы переставить столбцы в «data.txt» в соответствии с порядком в массиве «col_order» , используя скрипт оболочки .

Окончательный вывод

col1   col2   col3   col4
val13  val1   val5   val9
val14  val2   val6   val10
val15  val3   val7   val11
val16  val4   val8   val12

Мой прогресс на данный момент

awk 'BEGIN{ORS=RS="\n"; S=OFS="\t"}{for (i=1; i<=NF; i++) {f[$i] = i}{ print $(f["col1"]),$(f["col2"]),$(f["col3"]),$(f["col4"])}}' data.txt> data_corrected.txt

Вышеприведенный оператор работает, как и ожидалось, но порядок жестко закодирован в операторе. Не удалось выяснить, как взять заказ из массива и передать его в операторе.

Я открыт для любого другого подхода.

Ответы [ 2 ]

3 голосов
/ 31 марта 2020

Не могли бы вы попробовать следующее.

cat script.bash
List=( col1 col2 col3 col4 )
##echo ${List[*]}
awk -v bash_arr_val="${List[*]}" '
BEGIN{
  num=split(bash_arr_val,array," ")
  for(i=1;i<=num;i++){
    array_with_bash_values_as_index[array[i]]=i
  }
}

FNR==1{
 for(i=1;i<=NF;i++){
   if($i in array_with_bash_values_as_index){
     actual_array[array_with_bash_values_as_index[$i]]=i
   }
 }
}
{
 for(i=1;i<=num;i++){
   printf("%s%s",$actual_array[i],i==NF?ORS:OFS)
 }
}
'  Input_file

Вывод будет следующим.

col1 col2 col3 col4
val13 val1 val5 val9
val14 val2 val6 val10
val15 val3 val7 val11
val16 val4 val8 val12

РЕДАКТИРОВАТЬ Эд Мортон для подсказки имен переменных:

$ cat tst.awk
BEGIN{
    numOutFlds = split(bash_arr_val,outNr2name)
    for ( outNr=1; outNr<=numOutFlds; outNr++ ) {
        fldName = outNr2name[outNr]
        name2outNr[fldName] = outNr
    }
}

FNR==1 {
    for ( inNr=1; inNr<=NF; inNr++ ) {
        fldName = $inNr
        outNr = name2outNr[fldName]
        outNr2inNr[outNr] = inNr
    }
}
{
    for ( outNr=1; outNr<=numOutFlds; outNr++ ) {
        inNr = outNr2inNr[outNr]
        fldValue = $inNr
        printf "%s%s", fldValue, (outNr<numOutFlds ? OFS : ORS)
    }
}

$ awk -v bash_arr_val='col1 col2 col3 col4' -f tst.awk file
col1 col2 col3 col4
val13 val1 val5 val9
val14 val2 val6 val10
val15 val3 val7 val11
val16 val4 val8 val12

и вот как я бы действительно написал это, если бы просто создавал сценарий для себя, а не использовал так много временных переменных и т. Д. c. чтобы как можно понятнее понять другим:

$ cat tst.awk
BEGIN{
    numOutFlds = split(bash_arr_val,outNr2name)
    for ( outNr=1; outNr<=numOutFlds; outNr++ ) {
        name2outNr[outNr2name[outNr]] = outNr
    }
}

FNR==1 {
    for ( inNr=1; inNr<=NF; inNr++ ) {
        f[name2outNr[$inNr]] = inNr
    }
}
{
    for ( outNr=1; outNr<=numOutFlds; outNr++ ) {
        printf "%s%s", $(f[outNr]), (outNr<numOutFlds ? OFS : ORS)
    }
}

$ awk -v bash_arr_val='col1 col2 col3 col4' -f tst.awk file
col1 col2 col3 col4
val13 val1 val5 val9
val14 val2 val6 val10
val15 val3 val7 val11
val16 val4 val8 val12
1 голос
/ 31 марта 2020

Это не окончательное решение, а улучшение кода PO, в котором порядок столбцов не задан жестко:

#!/bin/bash

col_order=$1

awk -v col_order_string="$col_order" -v OFS='\t' '
  BEGIN {
    split(col_order_string, col_order, ",");
  }
  NR == 1 {
    for (i=1; i<=NF; i++) {
      f[$i] = i
    }
  }
  {
    s = ""
    for (i=1; i <= NF; i++) {
      printf "%s%s", s, $(f[col_order[i]]);
      s = OFS
    }
    printf "\n"
  }
' data.txt

Вызывается как

./script 'col1,col2,col3,col4'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...