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

Мой файл - это файл с разделителями трубы.

 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04  | GO|OGLE | EUROPE | EURO   | PARIS
 XYZE | 12  | Y|A|HOO | USA    | DOLLAR | SEATTLE
 LMNO | 17  | |FACE|B|O|O|K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98  | A||M|AZ|ON| | AFRICA | AF DOLLAR | CAPETOWN

Мой файл так же сложен, как и это. Нам нужно убрать «|» символ из поля WEB и замените его на ненужное значение, такое как #, $, или что-нибудь еще.

Выход должен быть:

NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN

Я пытался убрать несколько фильтров, чтобы убрать этот беспорядок. Кажется, ничто не находит счастливый конец. Спасибо! Я хотел бы поблагодарить несколько имен, которые ответили на мой предыдущий вопрос: Роман Перехрест, Эд Мортон, Шеллтер, Валь Рог.

1
mac_online 31 Авг 2017 в 14:17

6 ответов

Лучший ответ
$ cat tst.awk
BEGIN { FS=OFS="|" }
NR==1 { outNf=NF; print; next }
{
    end = beg + (NF - outNf) - 1
    for (i=1; i<=NF; i++) {
        sep = (i>=beg && i<=end ? "#" : OFS)
        printf "%s%s", $i, (i<NF ? sep : ORS)
    }
}

$ awk -v beg=3 -f tst.awk file
 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
 XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
 LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN

Как это работает: В первой строке количество полей, которые должны быть выведены, равно количеству полей в этой строке, поэтому он сохраняет это число как outNF. С этого момента любая последующая строка с более чем полем outNF будет иметь поля outNF-NF, начиная с beg, которые будут объединены. Таким образом, внутри цикла он использует OFS между полями от 1 до начала, затем с начала + 1 до начала + (outNF-NF) он использует # между полями для создания одного объединенного выходного поля из полей ввода в этом диапазоне, а затем возвращается к используя OFS между полями.

1
Ed Morton 11 Сен 2017 в 13:37

Вы можете использовать эту команду awk:

awk 'BEGIN{FS=OFS="|"} NR==1{n=NF} NF > n {
s=$3; for (i=4; i<=NF-3; i++) {s = s "#" $i; $i=""} $3=s; gsub(/\|{2,}/, "|")} 1' file

NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN
2
anubhava 31 Авг 2017 в 11:31

Другое awk решение может быть: -

awk  -F'[[:space:]][|][[:space:]]' '{gsub(/\|/,"#",$3);print $1,"|",$2,"|",$3,"|",$4,"|",$5,"|",$6}' file.txt

Объяснение : -

-F - for field separator here it is space|space
gsub - global substitution in field 3. i.e. every occurance of | will be replaced by #. 
print - just print all the columns separated by "|"

Вывод будет: -

NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
ABCD | 04  | GO#OGLE | EUROPE | EURO   | PARIS
XYZE | 12  | Y#A#HOO | USA    | DOLLAR | SEATTLE
LMNO | 17  | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
EDDE | 98  | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN
1
sumitya 31 Авг 2017 в 16:27

Я не пытался поместить это в одну строку, а скорее немного облегчил чтение. Те, кто играет в perl golf, смогут значительно его уменьшить. Идея состоит в том, чтобы привязать первые два поля и последние три.

#!/usr/bin/perl

while(<DATA>) {
  chomp;
  if(($name, $num, $web, $location, $currency, $place) = $_ =~
     /^([^\|]+)\|([^\|]+)\|(.+)\|([^\|]+)\|([^\|]+)\|([^\|]+)$/) {
    $web =~ tr/\|/\_/;
    printf "%s\n", join('|', ($name, $num, $web, $location, $currency, $place));
  }
}
__DATA__
 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04  | GO|OGLE | EUROPE | EURO   | PARIS
 XYZE | 12  | Y|A|HOO | USA    | DOLLAR | SEATTLE
 LMNO | 17  | |FACE|B|O|O|K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98  | A||M|AZ|ON| | AFRICA | AF DOLLAR | CAPETOWN

Выход:

 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04  | GO_OGLE | EUROPE | EURO   | PARIS
 XYZE | 12  | Y_A_HOO | USA    | DOLLAR | SEATTLE
 LMNO | 17  | _FACE_B_O_O_K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98  | A__M_AZ_ON_ | AFRICA | AF DOLLAR | CAPETOWN
0
Erik Bennett 5 Сен 2017 в 05:36

Легко, если вы не возражаете против Perl

Если у него есть место; тогда мы можем напечатать это:

stackoverflow ❱ perl -F'\s+|\s+' -a -le  'print $F[5]' file
WEB
GO|OGLE
Y|A|HOO
|FACE|B|O|O|K
A||M|AZ|ON|
stackoverflow ❱  

Так как мы можем изменить массив @F в Perl ; таким образом мы можем:

$F[5] =~ s/\|/#/g;  

Изменяет только этот столбец, а не другие.

И в конце концов мы можем напечатать это:

stackoverflow ❱ perl -F'\s+|\s+' -lae  '$F[5] =~ s/\|/#/g;print "@F"' file
 NAME | NUM | WEB | LOCATION | CURRENCY | PLACE
 ABCD | 04 | GO#OGLE | EUROPE | EURO | PARIS
 XYZE | 12 | Y#A#HOO | USA | DOLLAR | SEATTLE
 LMNO | 17 | #FACE#B#O#O#K | ASIA | ASIAN DOLLAR | HONGKONG
 EDDE | 98 | A##M#AZ#ON# | AFRICA | AF DOLLAR | CAPETOWN
stackoverflow ❱  

Если в вашем файле нет места , как мне кто-то прокомментировал; тогда вы можете распространять другие столбцы; измените только это и соедините их вместе:

stackoverflow ❱ cat file2
NAME|NUM|WEB|LOCATION|CURRENCY|PLACE
ABCD|04|GO|OGLE|EUROPE|EURO|PARIS
XYZE|12|Y|A|HOO|USA|DOLLAR|SEATTLE
LMNO|17||FACE|B|O|O|K|ASIA|ASIANDOLLAR|HONGKONG
EDDE|98|A||M|AZ|ON||AFRICA|AFDOLLAR|CAPETOWN
stackoverflow ❱ perl -F'\|' -le  '$s=$#F;$e="@F[2..$s-3]";$e=~s/ +/#/g;print join "|", @F[0..1],$e,join "|",@F[$s-2,$s-1,$s]' file2
NAME|NUM|WEB|LOCATION|CURRENCY|PLACE
ABCD|04|GO#OGLE|EUROPE|EURO|PARIS
XYZE|12|Y#A#HOO|USA|DOLLAR|SEATTLE
LMNO|17|#FACE#B#O#O#K|ASIA|ASIANDOLLAR|HONGKONG
EDDE|98|A#M#AZ#ON#|AFRICA|AFDOLLAR|CAPETOWN
1
Shakiba Moshiri 31 Авг 2017 в 15:12

Простое awk решение:

awk  -F "|" '{printf $1} 
{for(i=2; i<=NF; i++) { if(i>3 && i<NF-2)printf "#"$i; else printf "|"$i } printf "\n"} ' file

NAME|NUM|WEB|LOCATION|CURRENCY|PLACE
ABCD|04|GO#OGLE|EUROPE|EURO|PARIS
XYZE|12|Y#A#HOO|USA|DOLLAR|SEATTLE
LMNO|17|#FACE#B#O#O#K|ASIA|ASIANDOLLAR|HONGKONG
EDDE|98|A##M#AZ#ON#|AFRICA|AFDOLLAR|CAPETOWN

if(i>3 && i<NF-2): это условие для дополнительных нежелательных полей после 3-го поля и до NF-2-го поля. Если это удовлетворяет, введите "#" перед печатью этих дополнительных полей.

1
Rahul Verma 31 Авг 2017 в 17:26