Роздрукуйте всі, крім перших трьох стовпців


112

Занадто громіздко:

awk '{print " "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13}' things

43
Чи є якась причина, яку ви не можете просто використовувати cut -f3-?
Каскабель

1
@hhh приємний .. Мені подобається ідея стислої відповіді.
Кріс Сеймур

2
@Jefromi - тому що є питання лінії буферних з розрізом, який AWK не має: stackoverflow.com/questions/14360640 / ...
sdaau


@Jefromi - також cutне має регексів перед {}діями, і тоді це набагато глухіше з роздільниками поля (змінна кількість пробілів?), І вам доведеться вказати їх вручну. Я думаю, що ОП хотіла почути про якусь shift Nкоманду, якої не існує. Найближчий є $1="";$2="";(...);print}, але в моєму випадку він залишає деякі провідні простори (ймовірно, роздільники).
Томаш Гандор

Відповіді:


50

Рішення, яке не додає додаткові провідні чи кінцеві пробіли :

awk '{ for(i=4; i<NF; i++) printf "%s",$i OFS; if(NF) printf "%s",$NF; printf ORS}'

### Example ###
$ echo '1 2 3 4 5 6 7' |
  awk '{for(i=4;i<NF;i++)printf"%s",$i OFS;if(NF)printf"%s",$NF;printf ORS}' |
  tr ' ' '-'
4-5-6-7

Sudo_O пропонує елегантне вдосконалення з використанням термінального оператораNF?ORS:OFS

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

EdMorton пропонує рішення, зберігаючи оригінальні пробіли між полями:

$ echo '1   2 3 4   5    6 7' |
  awk '{ sub(/([^ ]+ +){3}/,"") }1' |
  tr ' ' '-'
4---5----6-7

BinaryZebra також пропонує два дивовижні рішення:
(ці рішення навіть зберігають пробіли від початкового рядка)

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ for ( i=1; i<=n; i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 ' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

Рішення, подане у коментарях larsr , майже правильне:

$ echo '1 2 3 4 5 6 7' | 
  awk '{for (i=3;i<=NF;i++) $(i-2)=$i; NF=NF-2; print $0}' | tr  ' ' '-'
3-4-5-6-7

Це фіксована і параметризована версія рішення larsr :

$ echo '1 2 3 4 5 6 7' | 
  awk '{for(i=n;i<=NF;i++)$(i-(n-1))=$i;NF=NF-(n-1);print $0}' n=4 | tr ' ' '-'
4-5-6-7

Усі інші відповіді до вересня 2013 року приємні, але додайте додаткові місця:


Відповідь EdMorton не працювала для мене (bash 4.1.2 (1) -release, GNU Awk 3.1.7 або bash 3.2.25 (1) -release, GNU Awk 3.1.5), але знайшов тут інший спосіб:echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
elysch

1
@elysch ні, це взагалі не буде працювати, воно, здається, працює з урахуванням певних вхідних значень. Дивіться коментар, який я додав під вашим коментарем під моєю відповіддю.
Ед Мортон

1
Привіт @fedorqui. Моя відповідь перша. У своїй оригінальній відповіді я пояснював, чому інша відповідь була невірною (додаткова провідна або відстала пробіли). Деякі люди запропонували покращення в коментарях. Ми попросили ОП вибрати більш правильну відповідь, і він / вона обрав мою. Після того, як деякі інші дописувачі відредагували мою відповідь на посилання на ту відповідь (див. Історію). Вам це зрозуміло? Що ви мені радите, щоб поліпшити зрозумілість моєї відповіді? Ура ;-)
олібре

1
Ви абсолютно праві, і мені дуже шкода мого непорозуміння. Я швидко прочитав відповідь і не помітив вашої оригінальної відповіді (так, я прочитав занадто швидко). +1 для самої відповіді, використовуючи приємний трюк, щоб зібрати на NF-1, а потім надрукувати останній елемент, щоб уникнути зайвого пробілу. І знову шкода! (видаляю мій коментар через день або близько того, щоб уникнути непорозумінь у майбутніх читачів).
fedorqui 'ТАК перестаньте шкодити'

1
Я використовував би такі заголовки: <ваша відповідь>, а потім горизонтальне правило, за яким слід великий заголовок "порівняння інших відповідей". В іншому випадку перенесіть це порівняння до іншої відповіді, оскільки, мабуть, люди, як правило, віддають перевагу коротким відповідям у видінні "дай код мій"
:)

75
awk '{for(i=1;i<4;i++) $i="";print}' file

4
Це залишить провідним, OFSоскільки ви не маєте справу, NFтобто провідний простір у записах.
Кріс Сеймур

70

використовувати розріз

$ cut -f4-13 file

або якщо ви наполягаєте на awk, і 13 доларів - це останнє поле

$ awk '{$1=$2=$3="";print}' file

ще

$ awk '{for(i=4;i<=13;i++)printf "%s ",$i;printf "\n"}' file

14
напевно, краще використовувати "NF", ніж "13" в останньому прикладі.
glenn jackman

2
2 сценарій, за яким залежить ОП. якщо 13 - останнє поле, використання NF - це добре. Якщо ні, то використання 13 доцільно.
ghostdog74

3
2-го потрібно видалити 3 копії OFS з початку $ 0. Третє краще printf "%s ",$i, оскільки ви не знаєте, чи $iможе містити щось %sподібне. Але це видасть додатковий простір наприкінці.
сумнівний

38

Спробуйте це:

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

1
Це приємно через те, наскільки воно динамічне. Ви можете додавати стовпці в кінці і не переписувати свої сценарії.
MinceMan

1
Це демонструє точну проблему, яку ви намагаєтеся вирішити з вами, просто навпаки. Що з друком зі 100-го поля? Зауважте, згадайте, що ви не маєте справу з NFтим, що ви залишаєте провідним OFS.
Кріс Сеймур

24

Правильний спосіб зробити це з інтервалом RE, оскільки він дозволяє просто вказати, скільки полів пропустити, і зберігає міжпольовий інтервал для решти полів.

наприклад, пропустити перші 3 поля, не впливаючи на проміжки між залишками поля, враховуючи формат введення, який ми, здається, обговорюємо в цьому питанні, просто:

$ echo '1   2 3 4   5    6' |
awk '{sub(/([^ ]+ +){3}/,"")}1'
4   5    6

Якщо ви хочете розмістити провідні пробіли та незаповнені пробіли, але знову ж таки з FS за замовчуванням, це:

$ echo '  1   2 3 4   5    6' |
awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){3}/,"")}1'
4   5    6

Якщо у вас є FS, який є RE, який ви не можете заперечувати в наборі символів, ви можете спершу перетворити його в один знак (RS ідеально, якщо це єдиний знак, оскільки RS CANNOT не з’являється в полі, інакше врахуйте SUBSEP), потім застосуйте підстановку інтервалу RE, потім перетворіть на OFS. наприклад, якщо ланцюги "." розділили поля:

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,RS);sub("([^"RS"]+["RS"]+){3}","");gsub(RS,OFS)}1'
4 5 6

Очевидно, якщо OFS є єдиним символом, і він не може з'являтися у полях введення, ви можете зменшити його до:

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,OFS); sub("([^"OFS"]+["OFS"]+){3}","")}1'
4 5 6

Тоді у вас є та сама проблема, що і з усіма рішеннями на основі циклу, які перепризначають поля - FS перетворюються на OFS. Якщо це проблема, вам потрібно вивчити функцію patsplit () GNU awks.


Не працювали для мене (bash 4.1.2 (1) -випуск, GNU Awk 3.1.7 або bash 3.2.25 (1) -release, GNU Awk 3.1.5), але знайшли тут інший спосіб:echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
elysch

2
Ні, це не вдасться, якщо $ 1 або $ 2 містять рядок, на який встановлено $ 3. Спробуйте, наприклад, echo ' That is a test' | awk '{print substr($0, index($0,$3))}'і ви побачите, aщо ціна $ 3 відповідає aвнутрішній Thatу $ 1. У дуже старій версії gawk, як у вас, вам потрібно включити інтервали RE з прапором --re-interval.
Ед Мортон

2
Ви маєте рацію, не помітили. До речі, дуже ціную ваш коментар. Багато разів хотіли використати регулярний вираз з "{}", щоб вказати кількість елементів і ніколи не бачив "--re-interval" у людині. +1 для вас.
elysch

1
1є справжньою умовою і тому викликає типову дію awk друку поточного запису.
Ед Мортон

1
idk наскільки це канонічно, але я додав відповідь зараз.
Ед Мортон

10

Практично всі відповіді в даний час додають або провідні пробіли, пробіли чи іншу проблему розділення. Для вибору з четвертого поля, де роздільником є ​​пробіл, а вихідним роздільником є ​​єдиний простір, використовуючи awk:

awk '{for(i=4;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' file

Для параметризації початкового поля ви можете:

awk '{for(i=n;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' n=4 file

А також закінчувальне поле:

awk '{for(i=n;i<=m=(m>NF?NF:m);i++)printf "%s",$i (i==m?ORS:OFS)}' n=4 m=10 file

6
awk '{$1=$2=$3="";$0=$0;$1=$1}1'

Вхідні дані

1 2 3 4 5 6 7

Вихідні дані

4 5 6 7

4
echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) print $i }'

3
Або, щоб отримати їх в одному рядку, призначте від $ 3 до $ 1 і т.д., а потім змініть NF на потрібну кількість полів. echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) $(i-2)=$i; NF=NF-2; print $0 }'
larsr

Привіт @larsr. Пропонований вам командний рядок - єдина правильна відповідь. Всі інші відповіді додають додаткові пробіли (провідні чи кінцеві). Будь ласка, опублікуйте свій командний рядок у межах нової відповіді, я буду його голосувати ;-)
олібре

1
Привіт @sudo_O, я говорив з @larsr про командний рядок, який він запропонував у своєму коментарі. Я витратив близько п’яти хвилин, перш ніж з'ясувати кіпроко (непорозуміння). Я згоден, відповідь @Vetsin вставляє нові рядки (ORS ) між полями. Браво за вашу ініціативу (мені подобається ваша відповідь). Ура
олібре

3

Ще один спосіб уникнути використання заяви про друк:

 $ awk '{$1=$2=$3=""}sub("^"FS"*","")' file

У сенсі, коли умова справжня, друк є дією за замовчуванням.


У цьому є всі проблеми @lhf відповідь має .. це просто коротше.
Кріс Сеймур

Дуже гарна ідея;) Краще, ніж моя відповідь! (Я вже підтримав вашу відповідь минулого року) Ура
олибре

Це повинно бути: awk '{$1=$2=$3=""}sub("^"OFS"+","")' fileяк і OFS, що залишається після зміни вмісту $ 1, 2 та 3 $.

3

Я не можу повірити, що ніхто не запропонував звичайну оболонку:

while read -r a b c d; do echo "$d"; done < file

+1 для аналогічного рішення ... Але це може мати проблеми з продуктивністю, якщо fileвін великий (> 10-30 КБ). Для великих файлів awkрішення працює краще.
TrueY

3

Варіанти 1 - 3 мають проблеми з кількома пробілами (але вони прості). Це причина для розробки варіантів 4 і 5, які без проблем обробляють кілька пробілів. Звичайно, якщо параметри 4 або 5 використовуються з n=0обома, збережуть будь-який провідний пробіл, оскільки це n=0означає, що немає розщеплення.

Варіант 1

Просте вирізане рішення (працює з одинарними роздільниками):

$ echo '1 2 3 4 5 6 7 8' | cut -d' ' -f4-
4 5 6 7 8

Варіант 2

Примушування повторного виклику awk іноді вирішує проблему (працює з деякими версіями awk) доданих провідних просторів:

$ echo '1 2 3 4 5 6 7 8' | awk '{ $1=$2=$3="";$0=$0;} NF=NF'
4 5 6 7 8

Варіант 3

Друк кожного поля, сформованого за допомогою printf, дасть більше контролю:

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

Однак усі попередні відповіді змінюють всі FS між полями на OFS. Давайте побудуємо пару рішень для цього.

Варіант 4

Цикл із підрядчиком для видалення полів і роздільників є більш портативним і не викликає зміну FS на OFS:

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

ПРИМІТКА: "^ [" FS "] *" повинен приймати вхід із провідними пробілами.

Варіант 5

Цілком можливо створити рішення, яке не додасть додаткового провідного або останнього пробілів, а також зберегти існуючий пробіл за допомогою функції gensubвід GNU awk, як це:

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

Він також може бути використаний для заміни списку полів з урахуванням кількості n:

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

Звичайно, в такому випадку OFS використовується для розділення обох частин рядка, а пробіл білого поля полів все ще надрукований.

Примітка1: ["FS"]* використовується для дозволу провідних пробілів у рядку введення.


Привіт BZ Ваша відповідь приємна. Але Варіант 3 не працює на рядок, який починається з пробілу (наприклад " 1 2 3 4 5 6 7 8 "). Варіант 4 хороший, але залиште провідний пробіл, використовуючи рядок, що починається з пробілу. Як ви думаєте, чи можна це виправити? Ви можете скористатися командою echo " 1 2 3 4 5 6 7 8 " | your awk script | sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/', щоб перевірити провідні / середні / пробіли ... Cheers;)
olibre

Привіт @olibre. Те, що варіант 3 не відповідає пробілу, є причиною розробки варіантів 4 та 5. Варіант 4 залишає провідний простір лише тоді, коли вхід має його, а n встановлено на 0 (n = 0). Я вважаю, що це правильна відповідь, коли немає вибору полів (нічого, щоб виправити IMO). Ура.

Гаразд. Дякуємо за додаткову інформацію :-) Будь ласка, вдосконалюйте свою відповідь, надаючи цю додаткову інформацію :-) Ура
олибре

Ідеально :-) Шкода, що ваш користувач відключений :-(
olibre

1

Cut має прапор --доповнення, що дозволяє легко (і швидко) видаляти стовпці. Отриманий синтаксис аналогічний тому, що ви хочете зробити - полегшуючи рішення для читання / розуміння. Доповнення також працює для випадку, коли ви хочете видалити непомітні стовпці.

$ foo='1 2 3 %s 5 6 7'
$ echo "$foo" | cut --complement -d' ' -f1-3
%s 5 6 7
$

Чи можете ви пояснити більше свою відповідь, будь ласка?
Зулу

Чи допомагає редакція вище в розумінні? Справа в тому, щоб використовувати прапор доповнення крою. Рішення має бути швидшим та більш коротким впровадженням, ніж рішення AWK або perl. Також довільні стовпчики можна вирізати.
Майкл Назад

1

Рішення Perl, яке не додає пробільних чи кінцевих пробілів:

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

@FМасив autosplit perl починається з індексу, 0а поля awk починаються з$1


Рішення Perl для обмежених комами даних:

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

Рішення Python:

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


0

Для мене найбільш компактне і сумісне рішення запиту - це

$ a='1   2\t \t3     4   5   6 7 \t 8\t '; 
$ echo -e "$a" | awk -v n=3 '{while (i<n) {i++; sub($1 FS"*", "")}; print $0}'

І якщо у вас є більше рядків для обробки, як, наприклад, файл foo.txt , не забудьте скинути i до 0:

$ awk -v n=3 '{i=0; while (i<n) {i++; sub($1 FS"*", "")}; print $0}' foo.txt

Дякую вашому форуму.


0

Оскільки я був роздратований першою висококваліфікованою, але неправильною відповіддю, я знайшов достатньо, щоб написати там відповідь, і тут неправильні відповіді позначені як такі, ось мій біт. Мені не подобаються запропоновані рішення, оскільки я не бачу причин робити так складний варіант відповіді.

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

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | 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 one two three" | awk '{printf "[%s]\n", substr($0,28)}'

який дає бажаний вихід:

[37.244.182.218 one two three]

0

Я знайшов цю іншу можливість, можливо, вона може бути корисною також ...

awk 'BEGIN {OFS=ORS="\t" }; {for(i=1; i<14; i++) print $i " "; print $NF "\n" }' your_file

Примітка: 1. Для табличних даних і від стовпців від 1 до 14 доларів


0

Використовуйте розріз:

cut -d <The character between characters> -f <number of first column>,<number of last column> <file name>

наприклад: Якщо у вас є file1:car.is.nice.equal.bmw

Виконати: cut -d . -f1,3 file1 буде надрукованоcar.is.nice


Здається, що ваше рішення може бути зворотним. Перегляньте заголовок питання Роздрукувати все * але * перші три колонки
Стефан Крейн

-1

Це не дуже далеко від деяких попередніх відповідей, але вирішує пару питань:

cols.sh:

#!/bin/bash
awk -v s=$1 '{for(i=s; i<=NF;i++) printf "%-5s", $i; print "" }'

Який ви тепер можете зателефонувати за допомогою аргументу, який буде початковим стовпцем:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 3 
3    4    5    6    7    8    9    10   11   12   13   14

Або:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 
7    8    9    10   11   12   13   14

Це 1-індексований; якщо ви віддаєте перевагу нульовому індексуванню, використовуйте i=s + 1замість цього.

Крім того, якщо ви хочете аргументувати стартовий індекс та кінцевий індекс, змініть файл на:

#!/bin/bash
awk -v s=$1 -v e=$2 '{for(i=s; i<=e;i++) printf "%-5s", $i; print "" }'

Наприклад:

$ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 9 
7    8    9

У %-5sпоєднується результат як 5-символьний широкими колони; якщо цього недостатньо, збільште кількість або використовуйте %s(з пробілом), якщо вам не байдуже вирівнювання.


-1

Рішення на основі AWK printf, що дозволяє уникнути проблеми% і унікальне тим, що нічого не повертає (жодного символу повернення), якщо для друку є менше 4 стовпців:

awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'

Тестування:

$ x='1 2 3 %s 4 5 6'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
%s 4 5 6
$ x='1 2 3'
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$ x='1 2 3 '
$ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
$
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.