2013-12-12 2 views
2

Я застрял в проблеме, которая может показаться не слишком сложной для опытных пользователей оболочки. Вот проблема.Замените столбец в файле столбцом из другого файла, сохраняя формат

У меня есть 2 файла:

FILE1 с форматом, как это:

ALPH  1 M GIF M 1  11.111 23.123 -4.412 1.00 0.00   A 
ALPH  2 BA GIF M 1  22.222 78.251 -6.215 2.00 0.00   B 
ALPH  3 C GIF M 1  22.223 46.321 -6.124 3.00 0.00   C 
ALPH  4 D GIF M 1  23.333 15.214 -6.125 4.00 0.00   D 
ALPH  5 AB GIF M 1  24.111 61.458 -8.214 5.00 0.00   E 
ALPH  6 LM GIF M 1  25.333 78.214 -9.321 6.00 0.00   F 
ALPH  7 BA GIF M 1  17.645 87.256 -9.365 7.00 0.00   G 
ALPH  8 BA2 GIF M 1  14.125 19.365 -1.258 8.00 0.00   H 
- 
- 

File2 с одной колонки:

-0.14 
-0.47 
-0.58 
-0.69 
-0.25 
-0.69 
-0.12 
-0.44 

Я хочу, чтобы заменить 11-й столбец в File1 с единственный столбец в файле2. Я делаю следующее

paste File1 File2 | awk '{$11=$13;$13=""}1' > output 

Хотя он заменяет столбец просто отлично, это портит оригинальный формат File1, который я хотел бы сохранить. Поскольку вы можете видеть, что между всеми полями File1 существует различное количество пробелов, и я бы хотел сохранить это даже после замены $ 11.

Я пробовал несколько подходов, включая column и printf, но ни один из них не работает. Может быть, я делаю что-то неправильно.

Кто-нибудь знает, как я могу достичь желаемого результата, желательно с awk или sed?

Спасибо!

Рохит

ответ

2

Если необходимо сохранить фиксированную ширину столбцов, вы могли бы работать с подстрок:

cat file1 
echo 
awk ' 
    NR==FNR {v[FNR]=$1; next} 
    {print substr($0,1,62) sprintf("%-15s", v[FNR]) substr($0,78)} 
' file2 file1 
ALPH  1 M GIF M 1  11.111 23.123 -4.412 1.00 0.00   A 
ALPH  2 BA GIF M 1  22.222 78.251 -6.215 2.00 0.00   B 
ALPH  3 C GIF M 1  22.223 46.321 -6.124 3.00 0.00   C 
ALPH  4 D GIF M 1  23.333 15.214 -6.125 4.00 0.00   D 
ALPH  5 AB GIF M 1  24.111 61.458 -8.214 5.00 0.00   E 
ALPH  6 LM GIF M 1  25.333 78.214 -9.321 6.00 0.00   F 
ALPH  7 BA GIF M 1  17.645 87.256 -9.365 7.00 0.00   G 
ALPH  8 BA2 GIF M 1  14.125 19.365 -1.258 8.00 0.00   H 

ALPH  1 M GIF M 1  11.111 23.123 -4.412 1.00 -0.14   A 
ALPH  2 BA GIF M 1  22.222 78.251 -6.215 2.00 -0.47   B 
ALPH  3 C GIF M 1  22.223 46.321 -6.124 3.00 -0.58   C 
ALPH  4 D GIF M 1  23.333 15.214 -6.125 4.00 -0.69   D 
ALPH  5 AB GIF M 1  24.111 61.458 -8.214 5.00 -0.25   E 
ALPH  6 LM GIF M 1  25.333 78.214 -9.321 6.00 -0.69   F 
ALPH  7 BA GIF M 1  17.645 87.256 -9.365 7.00 -0.12   G 
ALPH  8 BA2 GIF M 1  14.125 19.365 -1.258 8.00 -0.44   H 
+0

Хорошая точка, пока некоторые пробелы в реальном файле1 не являются вкладками, которые были преобразованы в пробелы во время копирования/вставки. Я думаю, что я бы пошел с 'print substr ($ 0,1,62) v [FNR] substr ($ 0,67)', хотя, поскольку он немного проще, и он будет работать, даже если файл1 содержит вкладки, следующие за 11-м полем, поскольку он не заменяет пустое пространство. –

+0

Вы считаете, что новые значения будут 5 символов? –

+0

Нет, что существующие значения - 4 символа и что он хочет сохранить такое же количество пробелов, как раньше. Я вижу вашу точку зрения, но он может захотеть сохранить сумму ширины поля плюс ширину пробелов. –

2

Использование awk

awk 'FNR==NR {a[NR]=$0;next} {$11=a[FNR]}1' OFS="\t" a t 
ALPH 1  M  GIF  M  1  11.111 23.123 -4.412 1.00 -0.14 A 
ALPH 2  BA  GIF  M  1  22.222 78.251 -6.215 2.00 -0.47 B 
ALPH 3  C  GIF  M  1  22.223 46.321 -6.124 3.00 -0.58 C 
ALPH 4  D  GIF  M  1  23.333 15.214 -6.125 4.00 -0.69 D 
ALPH 5  AB  GIF  M  1  24.111 61.458 -8.214 5.00 -0.25 E 
ALPH 6  LM  GIF  M  1  25.333 78.214 -9.321 6.00 -0.69 F 
ALPH 7  BA  GIF  M  1  17.645 87.256 -9.365 7.00 -0.12 G 
ALPH 8  BA2  GIF  M  1  14.125 19.365 -1.258 8.00 -0.44 H 

Edit вернулся к оригиналу из-за ошибки с sub

+1

ОП сказал 'существует различное количество пробелов между всеми полями File1, и я хотел бы сохранить это даже после замены $ 11'. Вышеупомянутое не сохранит пробелы, которые он заменит всеми вкладками. –

+0

@EdMorton Spacing может быть исправлен с помощью 'sub' вместо настройки, поданной как anubhava, и по мере добавления комментария. Это сделало бы мой пост равным его жесткости. – Jotne

+0

Как я прокомментировал ответ @ anubhava, вы никогда не должны использовать 'sub ($ field)'. Подумайте, что произойдет, если в некоторой строке данных OPs 8-е поле имеет значение 10.003? 11-е поле - '0,00', поэтому суб ($ 11) будет sub (0.00), которые совпадают с серединой восьмого поля вместо 11-го поля. Это даже хуже, потому что '.' является метасимволом RE, поэтому есть отличная возможность для ложных совпадений. –

1

чистого раствора AWK:

awk 'FNR==NR{a[NR]=$0;next} {sub($11, a[FNR])}1' file2 file1 
ALPH  1 M GIF M 1  11.111 23.123 -4.412 1.00 -0.14   A 
ALPH  2 BA GIF M 1  22.222 78.251 -6.215 2.00 -0.47   B 
ALPH  3 C GIF M 1  22.223 46.321 -6.124 3.00 -0.58   C 
ALPH  4 D GIF M 1  23.333 15.214 -6.125 4.00 -0.69   D 
ALPH  5 AB GIF M 1  24.111 61.458 -8.214 5.00 -0.25   E 
ALPH  6 LM GIF M 1  25.333 78.214 -9.321 6.00 -0.69   F 
ALPH  7 BA GIF M 1  17.645 87.256 -9.365 7.00 -0.12   G 
ALPH  8 BA2 GIF M 1  14.125 19.365 -1.258 8.00 -0.44   H 
+1

Вы можете использовать 'sub ($ 11, [FNR])' – Jotne

+0

@Jotne: Спасибо и согласились, что это намного чище. – anubhava

+2

Использование 'sub ($ 11, [FNR])' является плохой идеей, так как она будет терпеть неудачу, если /, когда $ 11 содержит любые метасимволы RE, и если /, когда какое-либо другое поле ранее на линии имеет то же значение, что и $ 11. –

3

При присвоении значения к поле в awk, он перекомпилирует текущую используя текущее значение OFS для разделения полей. Чтобы сохранить исходный интервал, вы не можете назначить новое значение для поля. Вместо этого вы должны использовать RE, чтобы описать, сколько пробелов пробелов пробежать до и после вашего назначения. Как это заменить букву «С» (третье поле, следовательно, число «2» ниже для ряда ведущих отраслей перескочить) со словом «BOB» с помощью GNU AWK:

$ echo "a b c d e" | 
gawk '{print gensub(/(([^[:space:]]+[[:space:]]+){2})[^[:space:]]+/,"\\1BOB","")}' 
a b BOB d e 

Это сохраняет расстояние потому что вы работаете над всей записью, а не только с одним полем, и поэтому awk не будет перекомпилировать запись.

Так что для вашего случая это было бы:

$ cat file1 
ALPH  1 M GIF M 1  11.111 23.123 -4.412 1.00 0.00   A 
ALPH  2 BA GIF M 1  22.222 78.251 -6.215 2.00 0.00   B 
ALPH  3 C GIF M 1  22.223 46.321 -6.124 3.00 0.00   C 
ALPH  4 D GIF M 1  23.333 15.214 -6.125 4.00 0.00   D 
ALPH  5 AB GIF M 1  24.111 61.458 -8.214 5.00 0.00   E 
ALPH  6 LM GIF M 1  25.333 78.214 -9.321 6.00 0.00   F 
ALPH  7 BA GIF M 1  17.645 87.256 -9.365 7.00 0.00   G 
ALPH  8 BA2 GIF M 1  14.125 19.365 -1.258 8.00 0.00   H 
$   
$ cat file2 
-0.14 
-0.47 
-0.58 
-0.69 
-0.25 
-0.69 
-0.12 
-0.44 
$ 
$ gawk 'NR==FNR{map[FNR]=$0; next} {print gensub(/(([^[:space:]]+[[:space:]]+){10})[^[:space:]]+/,"\\1" map[FNR],"")}' file2 file1 
ALPH  1 M GIF M 1  11.111 23.123 -4.412 1.00 -0.14   A 
ALPH  2 BA GIF M 1  22.222 78.251 -6.215 2.00 -0.47   B 
ALPH  3 C GIF M 1  22.223 46.321 -6.124 3.00 -0.58   C 
ALPH  4 D GIF M 1  23.333 15.214 -6.125 4.00 -0.69   D 
ALPH  5 AB GIF M 1  24.111 61.458 -8.214 5.00 -0.25   E 
ALPH  6 LM GIF M 1  25.333 78.214 -9.321 6.00 -0.69   F 
ALPH  7 BA GIF M 1  17.645 87.256 -9.365 7.00 -0.12   G 
ALPH  8 BA2 GIF M 1  14.125 19.365 -1.258 8.00 -0.44   H 

Если у вас нет Gawk (для gensub()), вы можете использовать матч(), чтобы найти, где поле вы заботитесь о стартах, второе совпадение() для того, где оно заканчивается, и разумный substr() s, чтобы заменить его новым значением.

@GlennJackman упомянул поля фиксированной ширины в своем решении. Если это то, что у вас есть, вы можете использовать GNU awks переменную FIELDWIDTHS, чтобы указать ширину каждого поля и просто работать с этим. Подробнее см. В руководстве gawk.

+0

Спасибо за ответ Эд. Хотя у меня есть «gawk» и ошибок нет, когда ваша команда запущена, но на выходе я получаю точно такой же файл, как и файл1, т. Е. Столбец не заменяется. Я проверил с 'diff'. Что может быть причиной этого? – rohit

+0

Вы можете использовать старую версию, где интервалы RE ('{10}') по умолчанию отключены. Попробуйте запустить gawk -re-interval '...' ', и если это сработает, получите более новую версию gawk, так как вам не хватает много полезных функций. –

+1

Вы были правы. Спасибо за вашу помощь. – rohit

2

Это может работать для вас (GNU СЭД):

sed = file2 | sed -r '$!N;s|(.*)\n(.*)|\1s/\\S+/\2/11|' | sed -rf - file1 
Смежные вопросы