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

cfischer
источник
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

Это даст вам: «два три»

7винки
источник
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 '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'

Аркку
источник
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
fedorqui 'ТАК, хватит вредить'
источник
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
Сообщество
источник
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
ZeBadger
источник
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,