Использование awk для печати всех столбцов от n-го до последнего

310

Эта строка работала, пока у меня не было пробелов во втором поле.

svn status | grep '\!' | gawk '{print $2;}' > removedProjs

Есть ли способ заставить awk распечатать все в $ 2 или больше? (3 доллара, 4 доллара ... пока у нас больше не будет столбцов?)

Я полагаю, я должен добавить, что я делаю это в среде Windows с Cygwin.

Энди
источник
11
Кроме того, grep | awkэто антипаттерн - вы хотитеawk '/!/ { print $2 }'
tripleee
3
Unix «резать» проще ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
roblogic
Возможный дубликат печати остальных полей в awk
acm
@tripleee: Я так счастлив, что ты упомянул об этом - я расстроен, увидев это повсюду
Грэм Николлс

Ответы:

490

напечатает все, кроме самого первого столбца:

awk '{$1=""; print $0}' somefile

напечатает все, кроме двух первых столбцов:

awk '{$1=$2=""; print $0}' somefile
zed_0xff
источник
93
Готча: оставляет ведущий космос болтается о :(
raphinesse
5
Мне нравится прагматичный подход. нет необходимости использовать cat, просто поместите имя файла после команды awk.
Кон
45
@raphinesse, это можно исправить с помощьюawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo
6
Это не работает с разделителями без пробелов, заменяет их пробелом.
Деян
3
Для разделителей без пробелов вы можете указать разделитель выходного поля (OFS), например, через запятую: awk -F, -vOFS=, '{$1=""; print $0}'вы получите начальный разделитель ( $1по-прежнему включен, как пустая строка). Вы можете удалить это с помощью sed:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt
99

Есть дублирующий вопрос с более простым ответом, используя сокращение:

 svn status |  grep '\!' | cut -d\  -f2-

-dуказывает разделитель (пробел) , -fуказывает список столбцов (все начинаются со 2-го)

Джошуа Голдберг
источник
Вы также можете использовать «-b» для указания позиции (начиная с N-го символа и далее).
Дакатин
Как примечание, хотя это выполняет ту же задачу, что и awkверсия, существуют проблемы с буферизацией строки cut, которые awkне имеют: stackoverflow.com/questions/14360640/…
sdaau
24
Красиво и просто, но поставляется с оговоркой: awkрассматривает несколько смежных пробелов. как отдельный разделитель, пока cutнет; также - хотя это не проблема в данном случае - cutпринимает только один буквенный символ. в качестве разделителя, тогда как awkпозволяет регулярное выражение.
mklement0
Исходя из этого: stackoverflow.com/a/39217130/8852408 , вероятно, что это решение не очень эффективно.
FcknGioconda
85

Вы можете использовать цикл for для циклического прохождения полей печати от $ 2 до $ NF (встроенная переменная, которая представляет количество полей в строке).

Изменить: так как «печать» добавляет новую строку, вы хотите буферизировать результаты:

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

В качестве альтернативы используйте printf:

awk '{for(i=2;i<=NF;i++){printf "%s ", $i}; printf "\n"}'
VeeArr
источник
Я попробовал это, но думаю, что я что-то упустил .. вот что я сделал svn status | grep '\!' | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> removeProjs
Энди
Так как print добавляет новую строку, вам нужно буферизовать результаты. Смотрите мое редактирование.
VeeArr
1
Мне больше нравится этот ответ, потому что он показывает, как проходить по полям.
Эдвард Фальк
3
Если вы хотите, чтобы печать использовала пробел, измените разделитель выходной записи: awk '{ORS = ""; for (i = 2; i <NF; i ++) print $ i} 'somefile
Кристиан Лескиер
3
Там всегда будет слишком много пробелов. Это работает лучше: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'без начальных и конечных пробелов.
Марки
24
awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}'

Мой ответ основан на ответе VeeArr , но я заметил, что он начался с пробела, перед тем как напечатать второй столбец (и остальные). Поскольку у меня есть только 1 очко репутации, я не могу это прокомментировать, поэтому здесь это выглядит как новый ответ:

начните с «out» в качестве второго столбца, а затем добавьте все остальные столбцы (если они существуют). Это идет хорошо, пока есть второй столбец.

Wim
источник
2
Отлично, вы также удалили $ перед переменной out, что тоже важно.
Алексис Уилке
15

Большинство решений с awk оставляют место. Варианты здесь позволяют избежать этой проблемы.

Опция 1

Простое решение для резки (работает только с одиночными разделителями):

command | cut -d' ' -f3-

Вариант 2

Принудительный пересчет awk иногда удаляет добавленный начальный пробел (OFS), оставленный путем удаления первых полей (работает с некоторыми версиями awk):

command | awk '{ $1=$2="";$0=$0;} NF=NF'

Вариант 3

Печать каждого поля, отформатированного с помощью printf, даст больше контроля:

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in"|awk -v n=2 '{ for(i=n+1;i<=NF;i++) printf("%s%s",$i,i==NF?RS:OFS);}'
3 4 5 6 7 8

Однако все предыдущие ответы меняют все повторяющиеся FS между полями на OFS. Давайте создадим пару вариантов, которые этого не делают.

Вариант 4 (рекомендуется)

Цикл с подпрограммой для удаления полей и разделителей спереди.
И используя значение FS вместо пробела (который можно изменить).
Является более переносимым и не вызывает смену FS на OFS: ПРИМЕЧАНИЕ . ^[FS]*Принимает ввод с начальными пробелами.

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in" | awk '{ n=2; a="^["FS"]*[^"FS"]+["FS"]+";
  for(i=1;i<=n;i++) sub( a , "" , $0 ) } 1 '
3     4   5   6 7     8

Вариант 5

Вполне возможно построить решение, которое не добавляет лишние (начальные или конечные) пробелы и сохраняет существующие пробелы, используя функцию gensubиз GNU awk, как это:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          { print(gensub(a""b""c,"",1)); }'
3     4   5   6 7     8 

Он также может быть использован для замены группы полей по количеству n:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
          {
            d=gensub(a""b""c,"",1);
            e=gensub("^(.*)"d,"\\1",1,$0);
            print("|"d"|","!"e"!");
          }'
|3     4   5   6 7     8  | !    1    2  !

Конечно, в таком случае OFS используется для разделения обеих частей линии, и завершающий пробел полей по-прежнему печатается.

ПРИМЕЧАНИЕ: [FS]* используется для разрешения начальных пробелов в строке ввода.

оборота user2350426
источник
13

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

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Где -F "" определяет разделитель для использования в awk. В моем случае это пробел, который также является разделителем по умолчанию для awk. Это означает, что -F "" можно игнорировать.

  2. Где NF определяет общее количество полей / столбцов. Поэтому цикл начнется с 4-го поля до последнего поля / столбца.

  3. Где $ N возвращает значение N-го поля. Поэтому print $ i будет печатать текущее поле / столбец на основе количества циклов.

koullislp
источник
4
Проблема в том, что каждое поле печатается на отдельной строке.
mveroone
ничто не мешает вам добавить это в конце :-) `| tr '\ n' '' `
koullislp
3
Немного поздно , но AWK '{для (I = 5; я <= NF; я ++) {Printf "% s", $ I}}'
plitter
8
awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'

Lauhub предложил это правильное, простое и быстрое решение здесь

ajendrex
источник
7

Это меня очень раздражало, я сел и написал cutпарсер спецификации, похожий на полевой, протестированный с GNU Awk 3.1.7.

Сначала создайте новый скрипт библиотеки Awk с именем pfcut, например:

sudo nano /usr/share/awk/pfcut

Затем вставьте скрипт ниже и сохраните. После этого, вот как выглядит использование:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-4"); }'
t1 t2 t3 t4

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("2-"); }'
t2 t3 t4 t5 t6 t7

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Чтобы избежать всего этого, я думаю, что лучшее, что можно сделать (см. Иначе Автоматически загружать пользовательскую функцию при запуске с awk? - Unix & Linux Stack Exchange ) - добавить псевдоним ~/.bashrc; например с:

$ echo "alias awk-pfcut='awk -f pfcut --source'" >> ~/.bashrc
$ source ~/.bashrc     # refresh bash aliases

... тогда вы можете просто позвонить:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk-pfcut '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

Вот источник pfcutскрипта:

# pfcut - print fields like cut
#
# sdaau, GNU GPL
# Nov, 2013

function spfcut(formatstring)
{
  # parse format string
  numsplitscomma = split(formatstring, fsa, ",");
  numspecparts = 0;
  split("", parts); # clear/initialize array (for e.g. `tail` piping into `awk`)
  for(i=1;i<=numsplitscomma;i++) {
    commapart=fsa[i];
    numsplitsminus = split(fsa[i], cpa, "-");
    # assume here a range is always just two parts: "a-b"
    # also assume user has already sorted the ranges
    #print numsplitsminus, cpa[1], cpa[2]; # debug
    if(numsplitsminus==2) {
     if ((cpa[1]) == "") cpa[1] = 1;
     if ((cpa[2]) == "") cpa[2] = NF;
     for(j=cpa[1];j<=cpa[2];j++) {
       parts[numspecparts++] = j;
     }
    } else parts[numspecparts++] = commapart;
  }
  n=asort(parts); outs="";
  for(i=1;i<=n;i++) {
    outs = outs sprintf("%s%s", $parts[i], (i==n)?"":OFS); 
    #print(i, parts[i]); # debug
  }
  return outs;
}

function pfcut(formatstring) {
  print spfcut(formatstring);
}
sdaau
источник
Похоже, вы хотите использовать cut, а неawk
roblogic
5

Распечатка столбцов, начиная с # 2 (вывод не будет заканчиваться пробелом в начале):

ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'
savvadia
источник
1
Хорошо, хотя вы должны добавить +после пробела, так как поля могут быть разделены более чем 1 пробелом ( awkобрабатывает несколько смежных пробелов как один разделитель). Кроме того, awkбудет игнорировать начальные пробелы, поэтому вы должны начать регулярное выражение с ^[ ]*. С пространством в качестве разделителя вы можете даже обобщить решение; Например, следующее возвращает все из 3-го поля: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'Однако становится сложнее с произвольными разделителями полей.
mklement0
5

Будет ли это работать?

awk '{print substr($0,length($1)+1);}' < file

Это оставляет некоторые пробелы впереди, хотя.

Уэйли
источник
4
echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'

этот использует awk для печати всех, кроме последнего поля

Каушал Джа
источник
3

Это то, что я предпочел из всех рекомендаций:

Печать с 6-го по последний столбец.

ls -lthr | awk '{out=$6; for(i=7;i<=NF;i++){out=out" "$i}; print out}'

или

ls -lthr | awk '{ORS=" "; for(i=6;i<=NF;i++) print $i;print "\n"}'
Мануэль Парра
источник
2

Если вам нужны определенные столбцы, напечатанные произвольным разделителем:

awk '{print $3 "  " $4}'

Кол №3 Кол №4

awk '{print $3 "anything" $4}'

цв # 3anythingcol # 4

Поэтому, если у вас есть пробел в столбце, это будет два столбца, но вы можете связать его с любым разделителем или без него.

I159
источник
2

Perl решение:

perl -lane 'splice @F,0,1; print join " ",@F' file

Эти параметры командной строки используются:

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

  • -l удаляет символы новой строки перед обработкой и добавляет их обратно после

  • -aрежим автоматического разделения - разбить входные строки в массив @F. По умолчанию расщепление по пробелам

  • -e выполнить код Perl

splice @F,0,1 чисто удаляет столбец 0 из массива @F

join " ",@F объединяет элементы массива @F, используя пробел между каждым элементом


Решение Python:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[1:]) + '\n') for line in sys.stdin]" < file

Крис Кокнат
источник
1

Если вы не хотите переформатировать часть строки, которую не отрываете, лучшее решение, которое я могу придумать, написано в моем ответе:

Как напечатать все столбцы после определенного числа с помощью awk?

Он прерывает то, что находится перед заданным номером поля N, и печатает всю оставшуюся часть строки, включая номер поля N и поддерживая исходный интервал (он не переформатируется). Не имеет значения, если строка поля появляется также где-то еще в строке.

Определить функцию:

fromField () { 
awk -v m="\x01" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}'
}

И используйте это так:

$ echo "  bat   bi       iru   lau bost   " | fromField 3
iru   lau bost   
$ echo "  bat   bi       iru   lau bost   " | fromField 2
bi       iru   lau bost 

Вывод поддерживает все, включая пробелы

В вашем конкретном случае:

svn status | grep '\!' | fromField 2 > removedProjs

Если ваш файл / поток не содержит символов новой строки в середине строк (возможно, вы используете другой разделитель записей), вы можете использовать:

awk -v m="\x0a" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}'

В первом случае произойдет сбой только в файлах / потоках, которые содержат редкий шестнадцатеричный символ номер 1

Роберт Вила
источник
0

Это сработает, если вы используете Bash и можете использовать столько символов «x», сколько элементов вы хотите отбросить, и он игнорирует несколько пробелов, если они не экранированы.

while read x b; do echo "$b"; done < filename
Стюарт Ротрок
источник
0

Perl:

@m=`ls -ltr dir | grep ^d | awk '{print \$6,\$7,\$8,\$9}'`;
foreach $i (@m)
{
        print "$i\n";

}
пкм
источник
1
Это не отвечает на вопрос, который обобщает требование к печати от N-го столбца до конца .
roaima
0

Эта awkфункция возвращает подстроку $0, включающую поля от beginдо end:

function fields(begin, end,    b, e, p, i) {
    b = 0; e = 0; p = 0;
    for (i = 1; i <= NF; ++i) {
        if (begin == i) { b = p; }
        p += length($i);
        e = p;
        if (end == i) { break; }
        p += length(FS);
    }
    return substr($0, b + 1, e - b);
}

Чтобы получить все, начиная с поля 3:

tail = fields(3);

Чтобы получить раздел, $0который охватывает поля с 3 по 5:

middle = fields(3, 5);

b, e, p, iерунда в списке параметров функции - это просто awkспособ объявления локальных переменных.

wonder.mice
источник
0

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

Я знаю, что ОП спрашивал awk, но sedподход будет работать здесь (пример с печатью столбцов от 5-го до последнего):

  • чистый сед подход

    sed -r 's/^\s*(\S+\s+){4}//' somefile

    Объяснение:

    • s/// используется стандартный способ выполнения замены
    • ^\s* соответствует любому последовательному пробелу в начале строки
    • \S+\s+ означает столбец данных (непробельные символы с последующими пробельными символами)
    • (){4} означает, что шаблон повторяется 4 раза.
  • седь и режь

    sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5-

    просто заменяя последовательные пробелы одной вкладкой;

  • tr и cut: trтакже можно использовать для сжатия последовательных символов с помощью -sопции.

    tr -s [:blank:] <somefile | cut -d' ' -f5-
PlasmaBinturong
источник
-1

Примеры Awk здесь выглядят сложными, вот простой синтаксис оболочки Bash:

command | while read -a cols; do echo ${cols[@]:1}; done

Где 1ваш n- й столбец, считая от 0.


пример

Учитывая это содержимое файла ( in.txt):

c1
c1 c2
c1 c2 c3
c1 c2 c3 c4
c1 c2 c3 c4 c5

вот вывод:

$ while read -a cols; do echo ${cols[@]:1}; done < in.txt 

c2
c2 c3
c2 c3 c4
c2 c3 c4 c5
kenorb
источник
-1

Мне не понравилось ни одно из awkпредставленных здесь решений, потому что я хотел извлечь первые несколько столбцов, а затем распечатать остальные, поэтому perlвместо этого я обратился к ним . Следующий код извлекает первые два столбца и отображает остаток как есть:

echo -e "a  b  c  d\te\t\tf g" | \
  perl -ne 'my @f = split /\s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;'

Преимущество по сравнению с perlрешением Криса Кокната состоит в том, что на самом деле только первые n элементов отделяются от входной строки; остальная часть строки вообще не разделяется и поэтому остается полностью неповрежденной. Мой пример демонстрирует это с помощью сочетания пробелов и табуляции.

Чтобы изменить количество столбцов, которые должны быть извлечены, замените 3в примере на n + 1.

Мартин фон Виттих
источник
-1
ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }'

От этого ответа неплохо, но естественный интервал исчез.
Пожалуйста, сравните это с этим:

ls -la | cut -d\  -f4-

Тогда вы увидите разницу.

Даже то, ls -la | awk '{$1=$2=""; print}'что основано на ответе, проголосовавшем лучше всего, пока не сохраняет форматирование.

Таким образом, я бы использовал следующее, и это также позволяет в начале явно выбирать столбцы:

ls -la | cut -d\  -f1,4-

Обратите внимание, что каждый пробел имеет значение и для столбцов, поэтому, например, в приведенных ниже столбцах 1 и 3 пусто, 2 - ИНФО, а 4 -:

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f1,3

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f2,4
INFO 2014-10-11
$
arntg
источник
-1

Если вы хотите отформатированный текст, объедините ваши команды с echo и используйте $ 0, чтобы напечатать последнее поле.

Пример:

for i in {8..11}; do
   s1="$i"
   s2="str$i"
   s3="str with spaces $i"
   echo -n "$s1 $s2" | awk '{printf "|%3d|%6s",$1,$2}'
   echo -en "$s3" | awk '{printf "|%-19s|\n", $0}'
done

Печать:

|  8|  str8|str with spaces 8  |
|  9|  str9|str with spaces 9  |
| 10| str10|str with spaces 10 |
| 11| str11|str with spaces 11 |
синтаксис
источник
-9

Из-за неправильного ответа большинства проголосовавших с 340 голосами я потерял 5 минут своей жизни! Кто-нибудь пробовал этот ответ, прежде чем проголосовать? Очевидно, нет. Совершенно бесполезно.

У меня есть журнал, где после 5 долларов с IP-адресом может быть больше текста или нет текста. Мне нужно все, от IP-адреса до конца строки, если что-то будет после 5 долларов. В моем случае это на самом деле в программе awk, а не в awk oneliner, поэтому awk должен решить проблему. Когда я пытаюсь удалить первые 4 поля, используя самый голосующий, но совершенно неправильный ответ:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

он выдает неправильный и бесполезный ответ (я добавил [..], чтобы продемонстрировать):

[    37.244.182.218 one two three]

Есть даже некоторые предположения, чтобы объединить substr с этим неправильным ответом. Как это осложнение является улучшением.

Вместо этого, если столбцы имеют фиксированную ширину до точки обрезки и awk, правильный ответ:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{printf "[%s]\n", substr($0,28)}'

который производит желаемый результат:

[37.244.182.218 one two three]
Pila
источник