Печать всего, кроме первого поля, с помощью 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 .
Serge Stroobandt

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 ;}' which: напечатать поля 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 удалял повторяющиеся пробелы, а cut - нет.
Fmstrat

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

2
Хорошее решение - @Luis работает на Mac, который не поддерживает --complement
metadaddy

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="": Установить для исходного первого поля значение null

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


14
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режим autosplit - разбивать входные строки в массив @F. По умолчанию разделение на пробелы

  • -F модификатор autosplit, в этом примере разбивается на '' (два пробела)

  • -e выполнить следующий код Perl

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


1
Я запустил его и в конце получил дополнительный номер, поэтому использовал эту версию: perl -lane 'shift @F; print join "", @F '
Hans Poo

2

Разделителем полей в gawk (по крайней мере) может быть строка, а также символ (это также может быть регулярное выражение). Если ваши данные согласованы, то это будет работать:

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

Это два пробела между двойными кавычками.


Лучший ответ для данной ситуации, но технически он не отвечает на вопрос, как печатать все, кроме первого поля.
Дэн Молдинг

@DanMoulding: до тех пор, пока в файле последовательно используются два пробела для разделения кода страны и нет других совпадений двух пробелов вместе, мой ответ действительно касается этого вопроса.
Приостановлено до дальнейшего уведомления.

2
Люди, которые задают этот вопрос, попадают сюда, потому что хотят знать, как печатать все, кроме первого поля (см. Заголовок вопроса). Вот как я сюда попал. Ваш ответ показывает, как напечатать первое поле, а затем второе поле. Хотя это, вероятно, лучшее решение для конкретной ситуации OP, оно не решает общей проблемы печати всего, кроме первого поля.
Дэн Молдинг


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.
Единственный способ избежать этого - избежать повторного расчета поля.
Одна функция, которая может избежать повторного вычисления, - это sub.
Первое поле может быть захвачено, затем удалено из $ 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


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.