0

Consider the following code:

renamed_column='2
6
10
8
22
20
6-
18
8-
12
16'

array1='2 0.00000 -1.45191
6 0.81778 -0.63413
10 0.85020 -0.60170
8 1.40260 -0.04931
22 3.25781 1.80590
20 4.32051 2.86860
6 0.00000 -0.93906
18 0.07618 -0.86288
8 0.36922 -0.56984
12 0.71195 -0.22711
16 0.88517 -0.05389'

I want to replace the first field $1 of array1 with renamed_column using the awk command.

My attempt was based on using awk -v v="$renamed_column" '{$1=v; print $0}' <<< "$array1"but that doesn't work.

The desired output reads,

2 0.00000 -1.45191
6 0.81778 -0.63413
10 0.85020 -0.60170
8 1.40260 -0.04931
22 3.25781 1.80590
20 4.32051 2.86860
6- 0.00000 -0.93906
18 0.07618 -0.86288
8- 0.36922 -0.56984
12 0.71195 -0.22711
16 0.88517 -0.05389
1
  • Having the contents of renamed_column be almost identical to the first column of array1 and so the expected output looking almost identical to the input array1 makes this question much harder to understand than it could have been. If you used letters A through K (or something else easily noticeable in the output) for the contents of renamed_column it'd be instantly clear what it is you're trying to do. Naming your scalar variable array1 also adds to the confusion - you should rename it to something more meaningful and not wrong.
    – Ed Morton
    May 22 at 20:49

3 Answers 3

1
$ awk -v v="$renamed_column" 'BEGIN{split(v,r)} {$1=r[NR]} 1' <<<"$array1"
2 0.00000 -1.45191
6 0.81778 -0.63413
10 0.85020 -0.60170
8 1.40260 -0.04931
22 3.25781 1.80590
20 4.32051 2.86860
6- 0.00000 -0.93906
18 0.07618 -0.86288
8- 0.36922 -0.56984
12 0.71195 -0.22711
16 0.88517 -0.05389

There's lots of other ways you could do this, of course, the above is just showing how to do it by making minimal changes to your original command.

That above saves the contents of the shell scalar variable renamed_column into the awk scalar variable v, then splits the contents of v into an awk array r, then input line by input line changes the value of each first field (from <<<"$array1") to append the contents of r[] indexed by the current line number as stored in NR.

4
  • What is the purpose of split(v,r)? May 22 at 21:10
  • Please look up split() in the awk man page and that'll tell you what it does. Ask yourself what v contains and what r contains (hint - v is a scalar and r is an array). If you're still confused after that, let me know what it is you don't understand and I'll try to help clarify it.
    – Ed Morton
    May 22 at 21:13
  • If it was that easy to understand I would not ask here :) Even though you answered my question correctly, I didn't understand how it works May 22 at 21:35
  • I added an explanation, hope it helps.
    – Ed Morton
    May 22 at 22:00
0

If you had the data in the two files array1.txt and renamed_column.txt, you could do something like this:

awk -v replfile=renamed_column.txt '{getline x < replfile; $1 = x; print; }' array1.txt  > output.txt

E.g. if renamed_column.txt is your input data, abbreviated a bit

20
6-
18
8-
12

and array1.txt is the corresponding

20 4.32051 2.86860
6 0.00000 -0.93906
18 0.07618 -0.86288
8 0.36922 -0.56984
12 0.71195 -0.22711

then the result, in output.txt would be

20 4.32051 2.86860
6- 0.00000 -0.93906
18 0.07618 -0.86288
8- 0.36922 -0.56984
12 0.71195 -0.22711

If you have the data in shell variables like that (and insist on keeping it that way), you could use process substitutions (in Bash/ksh/zsh) to pipe the data to awk:

awk -v replfile=<(echo "$renamed_column") '{getline x < replfile; $1 = x; print; }' <(echo "$array1")

But if you're going to use awk, handling data in files would be easier.

7
  • Could you explain briefly what happened in this code? May 22 at 16:30
  • @EnthusiastiC, getline reads a line of data from the given file (to variable x here, with the filename passed through replfile), and that's then assigned to the first field of the current line (from the main input), and the whole line then printed. What awk does in general is to split the input line into fields ($1, $2, ...) and then run the given commands.
    – ilkkachu
    May 22 at 16:42
  • @ikkachu you meant awk reads rename_file as a row line, not as a column field? May 22 at 16:48
  • @EnthusiastiC, getline reads full lines, so yes, the script will take a full line from replfile/renamed_column and use that to replace the first field of the corresponding line from array1. That's what I read your question as asking.
    – ilkkachu
    May 22 at 17:18
  • @EnthusiastiC, I don't know what you mean. Where would you use that?
    – ilkkachu
    May 22 at 20:00
0

According to comments to this answer, the data is tab-delimited, not space-delimited as it appears in the question. The code below has been modified with this in mind.

Using paste and cut instead of awk, and assuming you are using a shell that supports here-string redirection with <<<"..." and process substitution with <(...):

paste - <( cut -f 2- <<<"$array1" ) <<<"$renamed_column"

This uses paste to create two tab-delimited fields read from the standard input of the utility and a temporary file.

The standard input, symbolized by the - placeholder on the command line of paste, is the first field and comes from the input redirection of the string in $renamed_column.

The temporary file is created on the fly by a process substitution and provides the second field processed by paste. The process substitution runs cut, which extracts all but the first tab-delimited field from the string in $array1, which it reads via another here-string.

If you have the data in files instead, just replace the here-string redirections above with the filenames:

paste new_column_1 <( cut -f 2- old_data )
6
  • I used the first version of your answer. However, this appends the old $1 field of array1 to the output of your code where instead the $1 field should be replaced by the new one renamed_column as is in the last part of the question. May 22 at 17:42
  • Also, "which extracts all but the first space-delimited field from the string in $array1" I'm still confused about what cut did to "$array1"? I run it alone, it returns the same "$array1". Did you mean it should only extract the first field $1? May 22 at 17:49
  • Are you saying cut -d ' ' -f 2- <<<"$array1" returns three columns? If so, your data is not using spaces as delimiters and what you are showing is not what you actually have. Try copying and pasting the two variables from your question and test again. That cut command should result in the last two columns of your array1 data.
    – Kusalananda
    May 22 at 17:53
  • It's my fault, the data is delimited by tabs instead of single spaces. Because I obtain array1 by executing another command. I sufficed to show its content. May 22 at 17:58
  • It's possible to edit your code to this one paste - <( cut --fields=2- <<<"$array1" ) <<<"$renamed_column" if both array1 and renamed_column were delimited by tabs. And YES your code works fine, except that my intention was on awk. Thank you very much for that. May 22 at 20:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.