Ответ 1
Присвоение $1
работает, но оно оставляет ведущее пространство: awk '{first = $1; $1 = ""; print $0, first; }'
Вы также можете найти количество столбцов в NF
и использовать это в цикле.
У меня есть файл, который выглядит так:
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"?
Присвоение $1
работает, но оно оставляет ведущее пространство: awk '{first = $1; $1 = ""; print $0, first; }'
Вы также можете найти количество столбцов в NF
и использовать это в цикле.
$1=""
оставляет пробел, как упоминал Бен Джексон, поэтому используйте цикл for
:
awk '{for (i=2; i<=NF; i++) print $i}' filename
Итак, если ваша строка была "одна две три", вывод будет:
два
три
Если вы хотите получить результат в одной строке, вы можете сделать следующее:
awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}' filename
Это даст вам: "два три"
Используйте команду 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
Возможно, самый сжатый способ:
$ 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. Окончательный вывод неявный.
awk '{sub($1 FS,"")}7' YourFile
Удалите первое поле и разделитель и напечатайте результат (7
- это ненулевое значение, поэтому печать $0).
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'
Установка первого поля в ""
оставляет единственную копию OFS
в начале $0
. Предполагая, что OFS
- это только один символ (по умолчанию это одно пространство), мы можем удалить его с помощью substr($0, 2)
. Затем добавим сохраненную копию $1
.
Если вы открыты для решения 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
модификатор autosplit, в этом примере разбивается на '' (два пробела)
-e
выполните следующий код perl
@F
- это массив слов в каждой строке, индексированный начиная с 0
$#F
- количество слов в @F
@F[1..$#F]
- это срез массива элемента 1 через последний элемент
@F[1..$#F,0]
- это срез массива элемента 1 через последний элемент плюс элемент 0
Разделитель полей в gawk (по крайней мере) может быть строкой, а также символом (он также может быть регулярным выражением). Если ваши данные согласованы, то это будет работать:
awk -F " " '{print $2,$1}' inputfile
Это два пробела между двойными кавычками.
awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'
Переместите все записи в следующую и установите последнее как первое:
$ 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) th.$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
Первый удар в нем, похоже, работает для вашего конкретного случая.
awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'
Существует решение, которое работает с некоторыми версиями 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.
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, а затем перепечатать.
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
Если вы открыты для другого решения Perl:
perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
Здесь есть опция 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
Еще один способ...
... это присоединяет поля 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}'
Еще один простой способ использования команды cat
cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename