-1

i use bash i have table like this

001_1_174    [g/n                         474536         482492          
mo[g/n                    482492         504062          
'er/                      504062         517352          
ruze                      517352         529562                  
001_1_400    uz[`f                         960192        966656           
.ire                      966656         984416           
tuf/[                     984416         1006166

i want copy string of first line(only contain digits and underline) to other line until another number and do this for next number

sth like this

001_1_174    [g/n                    474536         482492           
001_1_174  mo[g/n                    482492         504062           
001_1_174  'er/                      504062         517352           
001_1_174  ruze                      517352       529562                  
001_1_400    uz[`f                   960192        966656           
001_1_400   .ire                      966656         984416           
001_1_400   tuf/[                     984416         1006166
2

Using Perl, if you're fine with a oneliner:

$ perl -pe 'if (/^\s*([\d_]+)/) {$x=$1} else {$_="$x $_"}' input.txt

Otherwise, as a script:

use warnings;
use strict;

my $prefix;
while (<>) {
    if ( /^\s*([\d_]+)/ )
        { $prefix = $1 }
    else { $_ = "$prefix $_" }
    print;
}

Both output:

001_1_174    [g/n                         474536         482492          
001_1_174 mo[g/n                    482492         504062          
001_1_174 'er/                      504062         517352          
001_1_174 ruze                      517352         529562                  
001_1_400    uz[`f                         960192        966656           
001_1_400 .ire                      966656         984416           
001_1_400 tuf/[                     984416         1006166

The regular expression is based on your specification "only contain digits and underline", and I allow whitespace at the beginning of that line (remove the \s* if you don't want that). Also, this will not work correctly if the first line read does not start with a number!

2
$ awk 'NF == 4 { col1 = $1; print; next } { print col1, $0 }' file
001_1_174    [g/n                         474536         482492
001_1_174 mo[g/n                    482492         504062
001_1_174 'er/                      504062         517352
001_1_174 ruze                      517352         529562
001_1_400    uz[`f                         960192        966656
001_1_400 .ire                      966656         984416
001_1_400 tuf/[                     984416         1006166

This first detects whether there are four columns on the input line. If there are, the col1 variable is set to the value in the first column, the line is printed as is, and the script continues with the next line of input. Otherwise, the line is printed, but with the value of col1 inserted before the original first column.

This only works if the seemingly random gibberish does not contain any whitespace characters. If it does, you may have to use -F 'delimiter', where delimiter is the delimiter used between the columns (for example '\t' for tabs).

It also assumes that the very first line has four columns, or the col1 variable would remain unset for that line.


Implementing suggestions in comments from user cas, which makes it agnostic to the number of columns in the input data (it just assumes that the first line holds the correct number of columns for any line that contains the special first column):

$ awk 'NR == 1 { cols = NF } NF == cols { col1 = $1; print; next } { print col1, $0 }' file
001_1_174    [g/n                         474536         482492
001_1_174 mo[g/n                    482492         504062
001_1_174 'er/                      504062         517352
001_1_174 ruze                      517352         529562
001_1_400    uz[`f                         960192        966656
001_1_400 .ire                      966656         984416
001_1_400 tuf/[                     984416         1006166
  • +1. that's the same algorithm i was about to write in perl, but a perl version would be about twice as long, and offer no real advantage over awk. – cas Feb 5 '18 at 14:06
  • 1
    about the only way to improve it would be to not hard-code it for 4 columns. instead, assume the first line has the full number of columns and set both withNR == 1 { maxcol=NF; col1=$1; print; next}, and then test for NF == maxcol` instead of NF == 4. – cas Feb 5 '18 at 14:11
  • 1
    @cas Implemented, with modifications. – Kusalananda Feb 5 '18 at 14:17
  • yep, your modifications are better. – cas Feb 5 '18 at 14:24
0

The shortest awk one:

awk 'NF < 4{ $0=n OFS $0 }{ n=$1 }1' file

The output:

001_1_174    [g/n                         474536         482492          
001_1_174 mo[g/n                    482492         504062          
001_1_174 'er/                      504062         517352          
001_1_174 ruze                      517352         529562                  
001_1_400    uz[`f                         960192        966656           
001_1_400 .ire                      966656         984416           
001_1_400 tuf/[                     984416         1006166

To rely on specific field format you may change the above with the following:

awk 'NF<4{ $0=n OFS $0 }$1~/^[0-9_]+$/{ n=$1 }1' file

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.