Друк всього, крім першого поля, з awk


108

У мене є файл, який виглядає приблизно так:

AE  United Arab Emirates
AG  Antigua & Barbuda
AN  Netherlands Antilles
AS  American Samoa
BA  Bosnia and Herzegovina
BF  Burkina Faso
BN  Brunei Darussalam

І я хотів би перевернути замовлення, надрукувавши спочатку все, крім $ 1, а потім $ 1:

United Arab Emirates AE

Як я можу зробити трюк "усе, крім поля 1"?


2
Привіт @cfisher, це можна зробити без англ циклу без зайвого місця.
Хуан Дієго Годой Роблес

Відповіді:


91

Призначення $1робіт, але це залишить провідний простір:awk '{first = $1; $1 = ""; print $0, first; }'

Ви також можете знайти кількість стовпців NFі використовувати це у циклі.


2
Для цілком ледачих; ось код klashxx ' .
Серж Стротобандт

1
Чудово. awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Позбувся

Простір легко видаляється за допомогою VIM, натискаючи "Ctrl + V Gd" у звичайному режимі
Санті,

107

$1=""залишає простір, як згадував Бен Джексон, тому використовуйте forцикл:

awk '{for (i=2; i<=NF; i++) print $i}' filename

Отже, якщо у вашому рядку було "один два три", вихід буде:

два
три

Якщо потрібно результат в одному ряду, ви можете зробити наступне:

awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}' filename

Це дасть вам: "два три"


4
та додатковий простір
NeronLeVelu

2
краще використовувати: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' котрий: друкуйте поля 2 на NF, додайте сепаратор вивідних полів за потребою (тобто, крім $ 2). Останній друк додає остаточний новий рядок, щоб закінчити друк поточного рядка. Це спрацює, якщо ви зміните FS / OFS (тобто, це не завжди буде "простором")
Олів'є Дулак

Другий працював для мене дуже добре. Перший, не дуже. Не дуже впевнений, чому. Це нарізало цілий текст.
голоси

72

Скористайтеся cutкомандою з --complementопцією:

$ echo a b c | cut -f 1 -d ' '
a
$ echo a b c | cut -f 1,2 -d ' '
a b
$ echo a b c | cut -f 1 -d ' ' --complement
b c

2
Не відповідаючи на питання, специфічне для awk, я вважав це найбільш корисним, оскільки awk видаляв повторювані пробіли, а вирізати - ні.
Фмстрат

19
echo a b c | cut -d' ' -f 2- є альтернативою
Луїс

2
Приємно - рішення @Luis працює на Mac, який не підтримує
--доповнення

21

Мабуть, самий стислий спосіб:

$ awk '{$(NF+1)=$1;$1=""}sub(FS,"")' infile
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Пояснення:

$(NF+1)=$1: Генератор "нового" останнього поля.

$1="": Встановіть початкове поле початкового поля

sub(FS,""): Після перших двох дій {$(NF+1)=$1;$1=""}позбудьтесь першого роздільника поля за допомогою підпункту. Остаточний друк неявний.


13
awk '{sub($1 FS,"")}7' YourFile

Видаліть перше поле та роздільник і надрукуйте результат ( 7це не нульове значення, тому надрукуйте $ 0).


Найкраща відповідь! Отримано. Чим він відрізняється від простого використання 1? Я дивуюсь використанню цієї картини і хотів це зрозуміти. Дякую!
Абхієет Растогі

10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

Встановлення першого поля ""залишає єдину копію OFSна початку $0. Якщо припустити, що OFSце лише один символ (за замовчуванням це єдиний пробіл), ми можемо видалити його substr($0, 2). Потім додаємо збережену копію $1.


6

Якщо ви відкриті для рішення Perl ...

perl -lane 'print join " ",@F[1..$#F,0]' file

- це просте рішення з роздільником вводу / виводу одного простору, який виробляє:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Цей наступний трохи складніший

perl -F`  ` -lane 'print join "  ",@F[1..$#F,0]' file

і передбачає, що роздільник вводу / виводу - це два пробіли:

United Arab Emirates  AE
Antigua & Barbuda  AG
Netherlands Antilles  AN
American Samoa  AS
Bosnia and Herzegovina  BA
Burkina Faso  BF
Brunei Darussalam  BN

Ці параметри командного рядка використовуються:

  • -n циклічно навколо кожного рядка вхідного файлу, не друкуйте кожен рядок автоматично

  • -l видаляє нові рядки перед обробкою та додає їх згодом

  • -aрежим автоспліт - розділити вхідні лінії в масив @F. За замовчуванням розділяється на пробіл

  • -F модифікатор автошпліту, у цьому прикладі розбивається на '' (два пробіли)

  • -e виконати наступний код Perl

@F- це масив слів у кожному рядку, індексований починаючи з 0
$#F- це кількість слів @F
@F[1..$#F], це фрагмент масиву елемента 1, через останній елемент
@F[1..$#F,0]є фрагмент масиву елемента 1 через останній елемент плюс елемент 0


1
Я запустив його і мав додаткове число наприкінці, тому я використав цю версію: perl -lane 'shift @F; друкувати приєднуйтесь "", @F '
Ханс

2

Розділювач поля в gawk (принаймні) може бути як рядком, так і символом (він також може бути регулярним виразом). Якщо ваші дані узгоджуються, це спрацює:

awk -F "  " '{print $2,$1}' inputfile

Це два проміжки між подвійними лапками.


Найкраща відповідь на ситуацію, що склалася, але технічно це не відповідає на питання, як надрукувати все, окрім першого поля.
Dan Molding

@DanMoulding: До тих пір , поки файл послідовний у використанні двох просторів , щоб відокремити код країни і немає ніяких інших входжень двох просторів разом, моя відповідь дійсно вирішити питання.
Призупинено до подальшого повідомлення.

2
Люди, які приїхали до цього питання, потрапляють сюди, оскільки хочуть знати, як надрукувати все, окрім першого поля (див. Заголовок питання). Ось як я приземлився тут. Ваша відповідь показує, як надрукувати перше поле, а потім друге поле. Хоча це, мабуть, найкраще рішення конкретної ситуації з ОП, воно не вирішує загальної проблеми, як надрукувати все, окрім першого поля.
Dan Molding


2

Перенесемо всі записи на наступний і встановимо останній як перший:

$ awk '{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' file
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Пояснення

  • a=$1 збережіть перше значення у тимчасовій змінній.
  • for (i=2; i<=NF; i++) $(i-1)=$i збережіть значення N-го поля у (N-1)-му полі.
  • $NF=aзбережіть перше значення ( $1) в останньому полі.
  • {}1справжній стан , щоб зробити awkдію за замовчуванням: {print $0}.

Таким чином, якщо у вас є інший роздільник поля, результат також хороший:

$ cat c
AE-United-Arab-Emirates
AG-Antigua-&-Barbuda
AN-Netherlands-Antilles
AS-American-Samoa
BA-Bosnia-and-Herzegovina
BF-Burkina-Faso
BN-Brunei-Darussalam

$ awk 'BEGIN{OFS=FS="-"}{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' c
United-Arab-Emirates-AE
Antigua-&-Barbuda-AG
Netherlands-Antilles-AN
American-Samoa-AS
Bosnia-and-Herzegovina-BA
Burkina-Faso-BF
Brunei-Darussalam-BN

1

Перший удар у неї, здається, працює для вашого конкретного випадку.

awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'

1

Варіант 1

Є рішення, яке працює з деякими версіями awk:

awk '{ $(NF+1)=$1;$1="";$0=$0;} NF=NF ' infile.txt

Пояснення:

       $(NF+1)=$1                          # add a new field equal to field 1.
                  $1=""                    # erase the contents of field 1.
                        $0=$0;} NF=NF      # force a re-calc of fields.
                                           # and use NF to promote a print.

Результат:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Однак це може не вдатися зі старими версіями awk.


Варіант 2

awk '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Це є:

awk '{                                      # call awk.
       $(NF+1)=$1;                          # Add one trailing field.
                  $1="";                    # Erase first field.
                        sub(OFS,"");        # remove leading OFS.
                                    }1'     # print the line.

Зауважте, що потрібно стерти OFS, а не FS. Рядок перераховується, коли поле $ 1 призначене. Це змінює всі прогони FS на один OFS.


Але навіть цей варіант все-таки не працює з кількома роздільниками, як це наочно показано зміною OFS:

awk -v OFS=';' '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

Цей рядок виведе:

United;Arab;Emirates;AE
Antigua;&;Barbuda;AG
Netherlands;Antilles;AN
American;Samoa;AS
Bosnia;and;Herzegovina;BA
Burkina;Faso;BF
Brunei;Darussalam;BN

Це виявляє, що запуски FS змінюються на один OFS.
Єдиний спосіб уникнути цього - уникнути перерахунку поля.
Однією з функцій, яка може уникнути повторного обчислення, є суб.
Перше поле можна було захопити, потім вилучити з $ 0 за допомогою sub, а потім обидва повторно надруковано.

Варіант 3

awk '{ a=$1;sub("[^"FS"]+["FS"]+",""); print $0, a;}' infile.txt
       a=$1                                   # capture first field.
       sub( "                                 # replace: 
             [^"FS"]+                         # A run of non-FS
                     ["FS"]+                  # followed by a run of FS.
                            " , ""            # for nothing.
                                  )           # Default to $0 (the whole line.
       print $0, a                   # Print in reverse order, with OFS.


United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

Навіть якщо ми змінимо FS, OFS та / або додамо більше роздільників, це працює.
Якщо вхідний файл змінено на:

AE..United....Arab....Emirates
AG..Antigua....&...Barbuda
AN..Netherlands...Antilles
AS..American...Samoa
BA..Bosnia...and...Herzegovina
BF..Burkina...Faso
BN..Brunei...Darussalam

І команда змінюється на:

awk -vFS='.' -vOFS=';' '{a=$1;sub("[^"FS"]+["FS"]+",""); print $0,a;}' infile.txt

Вихід буде (зберігаючи роздільники):

United....Arab....Emirates;AE
Antigua....&...Barbuda;AG
Netherlands...Antilles;AN
American...Samoa;AS
Bosnia...and...Herzegovina;BA
Burkina...Faso;BF
Brunei...Darussalam;BN

Команду можна розширити на декілька полів, але тільки з сучасними awks та з активацією опції --re-interval. Ця команда у вихідному файлі:

awk -vn=2 '{a=$1;b=$2;sub("([^"FS"]+["FS"]+){"n"}","");print $0,a,b;}' infile.txt

Виведе це:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei

1

Якщо ви відкриті до іншого рішення Perl:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file

0

Також є варіант sed ...

 sed 's/\([^ ]*\)  \(.*\)/\2 \1/' inputfile.txt

Пояснив ...

Swap
\([^ ]*\) = Match anything until we reach a space, store in $1
\(.*\)    = Match everything else, store in $2
With
\2        = Retrieve $2
\1        = Retrieve $1

Більш детально пояснено ...

s    = Swap
/    = Beginning of source pattern
\(   = start storing this value
[^ ] = text not matching the space character
*    = 0 or more of the previous pattern
\)   = stop storing this value
\(   = start storing this value
.    = any character
*    = 0 or more of the previous pattern
\)   = stop storing this value
/    = End of source pattern, beginning of replacement
\2   = Retrieve the 2nd stored value
\1   = Retrieve the 1st stored value
/    = end of replacement

0

Ще інший спосіб ...

... це знову приєднує поля 2 через NF з FS і виводить один рядок на рядок введення

awk '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

Я використовую це з git, щоб побачити, які файли були змінені в моєму робочому режимі:

git diff| \
    grep '\-\-git'| \
    awk '{print$NF}'| \
    awk -F"/" '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

-3

Ще один і простий спосіб використання команди cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename

Я прихильнився, оскільки це не динамічний підхід. З цим вам потрібно знати кількість аргументів і вважати, що ваші дані відповідають. Дані майже ніколи не узгоджуються, і ваш підхід повинен враховувати це більшу частину часу.
xh3b4sd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.