У меня есть два файла. Один содержит несколько столбцов и строк, а другой содержит только одно значение:

  • файл1:
    абв определение  ги jkl  
  • файл2:
    привет  

Я хочу добавить еще один столбец в file1, который всегда содержит значение, найденное в file2. Итак, вывод должен выглядеть так:

abc def hello
ghi jkl hello

Кто-нибудь может мне в этом помочь?

-2
Braiam 16 Сен 2021 в 16:56
Обсуждение практики тегирования перенесено в чат.
 – 
terdon
15 Сен 2021 в 18:01

5 ответов

Вы можете получить последнюю строку файла с помощью tail, а затем передать ее как внешнюю переменную в awk, указав awk напечатать каждую строку и переменную:

$ awk -v s="$(tail -n1 file2)" '{print $0,s}' file1
abc def hello
ghi jkl hello

Обратите внимание, что это предполагает, что i) вам нужна последняя строка file2; если это не так, вы можете заменить tail -n1 file на cat "$file" и ii) что file2 не содержит управляющих последовательностей (см. Как использовать переменные оболочки в сценарии awk? на SO).

4
ilkkachu 16 Сен 2021 в 02:42

Это будет работать независимо от того, какая строка находится в file2:

$ awk 'NR==FNR{x=$0; next} {print $0, x}' file2 file1
abc def hello
ghi jkl hello

Или, если вы предпочитаете, и если строка в файле2 не может содержать обратную косую черту (-v интерпретирует escape-последовательности):

$ awk -v x="$(< file2)" '{print $0, x}' file1
abc def hello
ghi jkl hello

Вот в чем разница:

$ cat file2
hello\there

$ awk 'NR==FNR{x=$0; next} {print $0, x}' file2 file1
abc def hello\there
ghi jkl hello\there

$ awk -v x=$(< file2) '{print $0, x}' file1
abc def hello   here
ghi jkl hello   here
3
Ed Morton 15 Сен 2021 в 16:48

Поскольку это постоянная строка, которую вы туда добавляете, на ум приходит использование sed с оговоркой, что добавляемая строка встроена в командную строку sed, поэтому все, что обрабатывается sed специально, не будет восприниматься как есть. Например. / будет использоваться для завершения команды s///, а & будет заменен частью шаблона.

$ str=$(cat file2)
$ sed -e "s/\$/ $str/" file1
abc def hello
ghi jkl hello

См., например. обсуждение в: Заменить первое вхождение шаблона в файле, который может содержать косую черту

То же самое с awk, хотя это также не так независимо от содержимого, как можно было бы подумать, поскольку для строк, установленных с помощью -v, awk обрабатывает обратную косую черту в стиле C, поэтому строка foo\tbar превратится в foo[tab]bar, что может быть, а может и не быть тем, что вам нужно.

$ str=$(cat file2)
$ awk -v str="$str" '{print $0 " " str}' < file1
abc def hello
ghi jkl hello

См., например: Использование переменной оболочки в awk


Или, я думаю, вы могли бы использовать и другие инструменты, хотя это получилось немного в стиле Руба Голдберга. Не знаю, что привело меня к этому:

$ paste file1 <( yes "$(cat file2)" ) | head -n "$(wc -l < file1)"
abc def hello
ghi jkl hello
2
ilkkachu 16 Сен 2021 в 11:47
1
Что касается «произвольных значений ...» - file2 не может содержать escape-последовательности в команде awk, поскольку -v интерпретирует их, например. попробуйте, если файл2 содержит hello\there. Вам нужно будет заполнить переменную awk str из ENVIRON или ARGV, чтобы ее можно было воспринимать буквально, см. stackoverflow.com/questions/19075671/….
 – 
Ed Morton
15 Сен 2021 в 16:57
Ха-ха, я сразу же начал вставлять / да!
 – 
glenn jackman
15 Сен 2021 в 17:03
1
@ ЭдМортон, о, верно. Именно по этой причине использование envvars было вещью. Я всегда об этом забываю, спасибо за напоминание.
 – 
ilkkachu
15 Сен 2021 в 17:42
FWIW, моей первой мыслью было seq + wc + paste, но я не довел мысль до конца, так как она начала выглядеть так, будто она может запутаться :-). Я никогда не думаю о yes.
 – 
Ed Morton
15 Сен 2021 в 17:58

Мы можем сделать так, как показано, и не беспокоиться об экранировании каких-либо символов:

$ sed rfile2 file1 | sed 'N;s/\n/ /'
abc def hello
ghi jkl hello
0
guest_7 16 Сен 2021 в 15:01

Использование Raku (ранее известного как Perl_6)

raku -e 'my $str="/path/to/file2".IO.lines(1); .subst(/(^.*$)/, {$0,$str}).put for lines;' file1

Вкратце, первая (и предположительно единственная) строка file2 считывается в переменную $str, затем file1 анализируется построчно, subst-итутирование (действительно, добавление) $str в конец каждой строки, даже пустые строки. Чтобы изменить поведение на добавление «привет» только в конец строки, содержащей символы, измените (^.*$) на (^.+$).

Пример ввода:

  • файл1:
    абв определение  ги jkl  
  • файл2:
    привет  

Пример вывода:

abc def hello
ghi jkl hello

Использование Raku для обработки ввода file1 и file2 (вместо прохождения через переменную оболочки) должно устранить ряд проблем, связанных с правильным экранированием ввода.

https://docs.raku.org/routine/subst
https://www.raku.org

0
jubilatious1 16 Сен 2021 в 21:28