
Я ищу однострочный конвейер для переупорядочивания большого количества столбцов (где ручной ввод номеров столбцов, например, в awk
команде, такой как , awk '{print $3,$2,$1}'
невозможен). Порядок может быть задан схемой сортировки (алфавитной, числовой - то есть как 'sort', но действующей на столбцы, а не на строки.) или может быть произвольно задан в текстовом файле.
решение1
Простое решение с Perl.
Сначала заполните массив значений.
➜ ~ x="$(cat << END
22 79 83 16 25 1 4 82 34 68
48 43 2 26 39 2 71 43 57 41
77 70 73 18 76 33 21 54 67 50
6 65 46 92 25 70 53 28 3 40
32 60 76 39 26 44 34 91 24 39
59 75 96 85 52 98 69 28 72 94
48 0 88 55 6 78 1 54 83 81
3 43 48 24 23 87 28 98 38 67
97 73 74 24 92 67 1 27 90 85
32 55 52 44 26 37 87 37 100 92
END
)"
➜ ~ perl -lane '@i=sort({ @F[$a] <=> @F[$b] } 0..$#F) if $.==1;
print join("\t", @F[@i])' <<< "$x"
1 4 16 22 25 34 68 79 82 83
2 71 26 48 39 57 41 43 43 2
33 21 18 77 76 67 50 70 54 73
70 53 92 6 25 3 40 65 28 46
44 34 39 32 26 24 39 60 91 76
98 69 85 59 52 72 94 75 28 96
78 1 55 48 6 83 81 0 54 88
87 28 24 3 23 38 67 43 98 48
67 1 24 97 92 90 85 73 27 74
37 87 44 32 26 100 92 55 37 52
-a
: включает функцию авторазделения, которая автоматически заполняет@F
массив-n
: читает каждую строку в цикле while$#F
: возвращает наибольший индекс в массиве, начиная с 0<=>
: оператор сравнения для функций сортировки (только числовой ввод, для сравнения строк используйтеcmp
)sort
: возвращает отсортированные индексы из массива0..$#F
(используя встроенные$a
и$b
переменные)@i
: содержит массив отсортированных индексов для@F
(в этом примере,@i = 5 6 3 0 4 8 9 1 7 2
)$. == 1
: и сделать это только в первой строке@F[@i]
: сортирует каждую строку на основе отсортированных индексов
Источник:https://learnbyexample.gitbooks.io/command-line-text-processing/content/perl_the_swiss_knife.html
решение2
Вот потоковое решение.
Я предполагаю, что вы хотите выполнить сортировку по первой строке столбцов, в противном случае адаптируйтесь так, чтобы получить ключ сортировки откуда-то еще.
Сгенерировать ключ сортировки (повторно используя массив Раша):
echo -e "b a c\n5 4 6\n8 7 9" > data
key=$(head -n1 data | sed 's/ \+/\n/g' | nl -n ln | sort -k2 | cut -f1)
$key
теперь содержит:
2
1
3
Теперь используйте ключ для сортировки столбцов:
awk -v key="$key" '
BEGIN { split(key, order, "\n") }
{
for(i=1; i<=length(order); i++) {
printf("%s ", $order[i])
}
printf("\n");
}' data
Выход:
a b c
4 5 6
7 8 9
решение3
Я не уверен, что это лучшее решение, и не уверен, что оно будет быстро работать на больших таблицах, но оно должно работать:
echo -e "2 1 3\n5 4 6\n8 7 9" | \
awk '{for (i=1;i<=NF;i++) {a[NR,i]=$i} } \
NF>p {p=NF} \
END {for (j=1;j<=p;j++) {str=a[1,j]; \
for (i=2;i<=NR;i++) {str=str" "a[i,j];}print str}}' \
| sort -n | \
awk '{for (i=1;i<=NF;i++) {a[NR,i]=$i} } \
NF>p {p=NF} \
END {for (j=1;j<=p;j++) {str=a[1,j]; \
for (i=2;i<=NR;i++) {str=str" "a[i,j];}print str}}'
Как это работает: транспонирует таблицу, затем сортирует ее и транспонирует обратно.
кстати, echo -e "2 1 3\n5 4 6\n8 7 9"
это приведет к
2 1 3
5 4 6
8 7 9
После работы над сценарием это приведет к
1 2 3
4 5 6
7 8 9
ps. Я думаю, что можно отсортировать массив в awk, к сожалению, у меня нет достаточно времени, чтобы сделать это.
решение4
Предположим, что ваш файл — xy.dat, разделенный пробелом:
cat xy.dat | while read line ; do
echo $line | tr ' ' '\n' | sort -nr | tr '\n' ' '
echo
done
Поскольку мои тестовые данные были числовыми в порядке возрастания, я использовал sort -nr в самом сердце, чтобы сделать их в порядке убывания, и увидел некоторый эффект.
Теперь, чтобы сделать его настраиваемым, нужно просто передать в качестве параметров флаги для сортировки, что позволяет сортировать по возрастанию (нет) и убыванию -r (обратный), а также -n (числовой) и многое другое (см.: sort --help
). Еще одна вещь, которую вам может понравиться настраивать, это разделитель. Пробел/Табуляция/Точка с запятой/Запятая? Может быть, группа регулярных выражений хотела бы "[ \t]"
иметь в виду пробел или табуляцию? Но что тогда использовать для вывода? И вы не хотели бы жестко задавать имя файла, а использовать свою программу в качестве фильтра. Вот быстрый подход:
#!/bin/bash
flags=$1
delim=$2
while read line ; do
echo $line | tr "$delim" '\n' | sort $flags | tr '\n' "$delim"
echo
done
призывание:
cat num.dat | bash colsort.sh "-nr" ' '
4 3 2 1
8 7 6 5
11 10 9
cat num.dat | bash colsort.sh "-r" ' '
4 3 2 1
8 7 6 5
9 11 10
cat num.dat | bash colsort.sh "--" ' '
1 2 3 4
5 6 7 8
10 11 9
Посмотрите, как сортируется по умолчанию: -- (в алфавитном порядке: 10 11 9), в обратном порядке (9 10 11) или по числам (11 10 9).
В основном было бы полезно, если бы было задокументировано, как маскировать пробелы, табуляции и т. д.