Використовуючи 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


@tripleee: Я такий щасливий, що ти згадав про це - мені неприємно бачити це всюди!
Грем Ніколлс

Відповіді:


489

надрукує перший, але не перший стовпець:

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

буде надрукувати всі, крім двох перших стовпців:

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

93
gotcha: залишає провідний простір, що звисає про :(
raphinesse

5
мені подобається прагматичний підхід. не потрібно використовувати cat, однак просто покладіть ім'я файлу після команди awk.
кон

45
@raphinesse ви можете це виправитиawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo

6
Це не працює з розмежувачами, що не містять пробілів, замінює їх пробілом.
Деян

3
Для розмежувачів, які не awk -F, -vOFS=, '{$1=""; print $0}'містять пробілів, ви можете вказати роздільник поля виводу (OFS), наприклад, до коми: Ви отримаєте початковий роздільник ( $1все ще включений, як порожній рядок). Ви можете зняти це, sedхоча:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
Чердт

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 (вбудована змінна, яка представляє кількість полів у рядку).

Редагувати: Оскільки "print" додає новий рядок, вам потрібно буде розмістити результати:

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"}'

Тому я спробував це, але думаю, що мені щось не вистачає .. ось що я зробив статус svn | grep '\!' | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> видаленоProjs
Енді

Оскільки друк додає новий рядок, вам потрібно буде розмістити результати. Дивіться мою редакцію.
VeeArr

1
Мені подобається ця відповідь краще, тому що вона показує, як перебирати поля.
Едвард Фолк

3
Якщо ви хочете для друку використовувати пробіл, змініть роздільник вихідних записів: awk '{ORS = ""; для (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 , але я помітив, що вона почалася з пробілу перед тим, як надрукувати другу колонку (та решту). Оскільки у мене є лише одна точка репутації, я не можу це коментувати, тому тут це нова відповідь:

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


2
Відмінно, ви також видалили $ перед вихідною змінною, що теж важливо.
Алексіс Вілке

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

Однак усі попередні відповіді змінюють всі повторювані ФС між полями на 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

Цілком можливо створити рішення, яке не додасть додаткового (провідного чи трейлінг) пробілу, а також зберегти існуючі пробіли, використовуючи функцію gensubGNU 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]* використовується для дозволу провідних пробілів у рядку введення.


13

Я особисто спробував усі вищезгадані відповіді, але більшість з них були трохи складними або просто невірними. Найпростіший спосіб зробити це з моєї точки зору:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Де -F "" визначає роздільник для awk у використанні. У моєму випадку це пробіл, який також є роздільником за замовчуванням для awk. Це означає, що -F "" "можна ігнорувати.

  2. Де NF визначає загальну кількість полів / стовпців. Тому цикл розпочнеться від 4-го поля до останнього поля / стовпця.

  3. Де $ N отримує значення N-го поля. Тому print $ i надрукує поточне поле / стовпець на основі кількості циклу.


4
Проблема, яка друкує кожне поле на іншій лінії.
mveroone

ніщо не зупиняє вас додавати це наприкінці :-) `| tr '\ n' '' `
koullislp

3
Трохи пізно, але awk '{for (i = 5; i <= NF; i ++) {printf "% s", $ i}}'
плітер

8
awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'

Lauhub запропонував це правильне, просте і швидке рішення тут


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);
}

Здається, ви хочете використовувати cut, а неawk
roblogic

5

Друк стовпців, починаючи з №2 (на виході не буде місця на початку):

ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'

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}'

col # 3 col # 4

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

col # 3anythingcol №4

Так що якщо у вас є пробіл у стовпці, це буде два стовпці, але ви можете з'єднати його з будь-яким роздільником або без нього.


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спосіб оголошення локальних змінних.


0

Я хочу поширити запропоновані відповіді на ситуацію, коли поля розмежовані можливо кількома пробілами - cutя вважаю, причиною, чому ОП не використовується .

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

  • чистий sed підхід

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

    Пояснення:

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

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

    просто замінивши послідовних пробілів однією вкладкою;

  • tr і вирізати: trтакож можна використовувати для видавлення послідовних символів за допомогою -sпараметра.

    tr -s [:blank:] <somefile | cut -d' ' -f5-

-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

-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рішенням від Кріса Кокната в тому, що насправді лише перші п ять елементів відщеплені від вхідного рядка; решта струни взагалі не розщеплені і для цього залишаються повністю недоторканими. Мій приклад демонструє це поєднанням пробілів та вкладок.

Щоб змінити кількість стовпців, які слід витягнути, замініть 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 - INFO, а 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
$

-1

Якщо ви хочете відформатованого тексту, зв’яжіть свої команди ехо і використовуйте $ 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]

Навіть є кілька пропозицій, щоб поєднати субстр з цією неправильною відповіддю. Як і це ускладнення - це поліпшення.

Натомість, якщо стовпці мають фіксовану ширину до потрібної точки зрізу та 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]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.