Menggunakan awk untuk mencetak semua kolom dari tanggal ke yang terakhir


310

Baris ini bekerja sampai saya memiliki spasi putih di bidang kedua.

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

adakah cara untuk mencetak semuanya dalam $ 2 atau lebih? ($ 3, $ 4 .. sampai kita tidak memiliki kolom lagi?)

Saya kira saya harus menambahkan bahwa saya melakukan ini di lingkungan Windows dengan Cygwin.


11
Sebagai tambahan, grep | awkini adalah antipattern - Anda inginkanawk '/!/ { print $2 }'
tripleee

3
Unix "cut" lebih mudah ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
roblogic

Kemungkinan duplikat sisa cetak bidang dalam awk
acm

@tripleee: Saya sangat senang Anda menyebutkan ini - Saya frustrasi melihatnya di mana-mana!
Graham Nicholls

Jawaban:


490

akan mencetak semua kecuali kolom pertama:

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

akan mencetak semua kecuali dua kolom pertama:

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

93
Gotcha: meninggalkan ruang terdepan yang menggantung tentang :(
raphinesse

5
Saya suka pendekatan pragmatis. tidak perlu menggunakan cat, cukup letakkan nama file setelah perintah awk.
kon

45
@raphinesse Anda dapat memperbaikinya denganawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo

6
Ini tidak bekerja dengan pembatas non-spasi putih, menggantikannya dengan spasi.
Dejan

3
Untuk pembatas non-spasi putih, Anda dapat menentukan Pemisah Bidang Output (OFS), misalnya untuk koma: awk -F, -vOFS=, '{$1=""; print $0}'Anda akan berakhir dengan pembatas awal ( $1masih termasuk, seperti string kosong). Anda dapat menghapusnya dengan sed:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt

99

Ada pertanyaan duplikat dengan jawaban sederhana menggunakan cut:

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

-dmenentukan delimeter (spasi) , -fmenentukan daftar kolom (semua dimulai dengan ke-2)


Anda juga dapat menggunakan "-b" untuk menentukan posisi (dari karakter N dan seterusnya).
Dakatine

Sebagai catatan, meskipun ini melakukan tugas yang sama dengan awkversi, ada masalah buffering baris dengan cut, yang awktidak memiliki: stackoverflow.com/questions/14360640/…
sdaau

24
Bagus dan sederhana, tetapi dilengkapi dengan peringatan: awkmemperlakukan beberapa karakter ruang yang berdekatan. sebagai pemisah tunggal , sementara cuttidak; juga - meskipun ini bukan masalah dalam kasus yang dihadapi - cuthanya menerima satu, karakter literal. sebagai pembatas, sedangkan awkmemungkinkan regex.
mklement0

Berdasarkan ini: stackoverflow.com/a/39217130/8852408 , kemungkinan solusi ini tidak terlalu efisien.
FcknGioconda

85

Anda bisa menggunakan for-loop untuk mengulang melalui bidang pencetakan $ 2 hingga $ NF (variabel bawaan yang mewakili jumlah bidang pada baris).

Sunting: Karena "print" menambahkan baris baru, Anda ingin buffer hasilnya:

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

Atau, gunakan printf:

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

Jadi saya mencoba ini, tetapi saya pikir saya kehilangan sesuatu .. ini adalah apa yang saya lakukan svn status | grep '\!' | gawk '{for (i = 1; i <= $ NF; i ++) mencetak $ i "";}'> dihapusProjs
Andy

Karena hasil cetak menambahkan baris baru, Anda ingin buffer hasilnya. Lihat hasil edit saya.
VeeArr

1
Saya suka jawaban ini dengan lebih baik karena ini menunjukkan cara loop melalui bidang.
Edward Falk

3
Jika Anda ingin mencetak menggunakan spasi, ubah pemisah catatan keluaran: awk '{ORS = ""; untuk (i = 2; i <NF; i ++) mencetak $ i} 'somefile
Christian Lescuyer

3
Akan selalu ada terlalu banyak ruang. Ini berfungsi lebih baik: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'Tidak ada ruang depan atau belakang.
Marki

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

Jawaban saya didasarkan pada salah satu VeeArr , tetapi saya perhatikan itu dimulai dengan spasi putih sebelum akan mencetak kolom kedua (dan sisanya). Karena saya hanya memiliki 1 poin reputasi, saya tidak dapat mengomentarinya, jadi ini dia sebagai jawaban baru:

mulai dengan "keluar" sebagai kolom kedua dan kemudian tambahkan semua kolom lainnya (jika ada). Ini berjalan dengan baik selama ada kolom kedua.


2
Luar biasa, Anda juga menghapus $ di depan variabel out yang juga penting.
Alexis Wilke

15

Sebagian besar solusi dengan awk menyisakan ruang. Opsi di sini menghindari masalah itu.

Pilihan 1

Solusi pemotongan sederhana (hanya bekerja dengan pembatas tunggal):

command | cut -d' ' -f3-

pilihan 2

Memaksa re-cal awk kadang-kadang menghapus ruang pimpinan tambahan (OFS) yang tersisa dengan menghapus kolom pertama (berfungsi dengan beberapa versi awk):

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

Opsi 3

Mencetak setiap bidang yang diformat dengan printfakan memberikan kontrol lebih besar:

$ 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

Namun, semua jawaban sebelumnya mengubah semua FS berulang antara bidang menjadi OFS. Mari kita membangun beberapa opsi yang tidak melakukannya.

Opsi 4 (disarankan)

Lingkaran dengan sub untuk menghapus bidang dan pembatas di bagian depan.
Dan menggunakan nilai FS bukan ruang (yang bisa diubah).
Lebih portabel, dan tidak memicu perubahan FS ke OFS: CATATAN: The ^[FS]*adalah menerima masukan dengan spasi terkemuka.

$ 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

Opsi 5

Sangat mungkin untuk membangun solusi yang tidak menambahkan spasi putih tambahan (memimpin atau mengikuti), dan melestarikan spasi putih yang ada menggunakan fungsi gensubdari GNU awk, karena ini:

$ 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 

Itu juga dapat digunakan untuk menukar sekelompok bidang yang diberi hitungan 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  !

Tentu saja, dalam kasus seperti itu, OFS digunakan untuk memisahkan kedua bagian dari garis, dan ruang putih trailing bidang masih dicetak.

CATATAN: [FS]* digunakan untuk memungkinkan spasi di baris input.


13

Saya pribadi mencoba semua jawaban yang disebutkan di atas, tetapi kebanyakan dari mereka agak rumit atau tidak tepat. Cara termudah untuk melakukannya dari sudut pandang saya adalah:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Di mana -F "" mendefinisikan pembatas untuk awk untuk digunakan. Dalam kasus saya adalah spasi putih, yang juga merupakan pembatas default untuk awk. Ini berarti bahwa "" F dapat diabaikan.

  2. Di mana NF menentukan jumlah total bidang / kolom. Oleh karena itu loop akan dimulai dari bidang ke-4 hingga ke bidang / kolom terakhir.

  3. Di mana $ N mengambil nilai bidang Nth. Oleh karena itu cetak $ i akan mencetak bidang / kolom saat ini berdasarkan pada jumlah loop.


4
Masalah, yang mencetak setiap bidang pada garis yang berbeda.
mveroone

tidak ada yang menghentikan Anda menambahkan ini di akhir :-) `| tr '\ n' '' `
koullislp

3
Agak terlambat tetapi awk '{for (i = 5; i <= NF; i ++) {printf "% s", $ i}}'
plitter


7

Ini sangat menjengkelkan saya, saya duduk dan menulis cutparser spesifikasi lapangan seperti, diuji dengan GNU Awk 3.1.7.

Pertama, buat skrip library Awk baru bernama pfcut, dengan eg

sudo nano /usr/share/awk/pfcut

Lalu, rekatkan skrip di bawah ini, dan simpan. Setelah itu, begini tampilannya:

$ 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

Untuk menghindari mengetik semua itu, saya kira yang terbaik bisa dilakukan (lihat sebaliknya Secara otomatis memuat fungsi pengguna saat startup dengan awk? - Unix & Linux Stack Exchange ) adalah menambahkan alias ke ~/.bashrc; misalnya dengan:

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

... maka Anda bisa langsung menelepon:

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

Inilah sumber pfcutnaskahnya:

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

Sepertinya Anda ingin menggunakan cut, bukanawk
roblogic

5

Mencetak kolom mulai dari # 2 (output tidak akan memiliki ruang tambahan di awal):

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

1
Bagus, meskipun Anda harus menambahkan +setelah spasi, karena bidang dapat dipisahkan oleh lebih dari 1 ruang ( awkmemperlakukan beberapa ruang yang berdekatan sebagai pemisah tunggal). Juga, awkakan mengabaikan spasi awal, jadi Anda harus memulai regex dengan ^[ ]*. Dengan ruang sebagai pemisah Anda bahkan bisa menggeneralisasi solusi; misalnya, yang berikut mengembalikan semuanya dari bidang ke-3: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'Namun, hal itu akan semakin rumit dengan pemisah bidang yang sewenang-wenang.
mklement0

5

Apakah ini akan berhasil?

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

Itu meninggalkan beberapa spasi di depan.


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

yang ini menggunakan awk untuk mencetak semua kecuali kolom terakhir


3

Inilah yang saya sukai dari semua rekomendasi:

Mencetak dari kolom ke-6 hingga terakhir.

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

atau

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

2

Jika Anda membutuhkan kolom tertentu yang dicetak dengan delimeter acak:

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

col # 3 col # 4

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

col # 3anythingcol # 4

Jadi, jika Anda memiliki spasi dalam kolom itu akan menjadi dua kolom, tetapi Anda dapat menghubungkannya dengan pembatas apa pun atau tanpa itu.


2

Solusi Perl:

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

Opsi baris perintah ini digunakan:

  • -n lingkaran di sekitar setiap baris dari file input, jangan otomatis mencetak setiap baris

  • -l menghapus baris baru sebelum diproses, dan menambahkannya kembali sesudahnya

  • -amode autosplit - membagi jalur input ke dalam array @F. Default untuk memisahkan di spasi putih

  • -e jalankan kode perl

splice @F,0,1 menghapus kolom 0 dari array @F dengan bersih

join " ",@F bergabung dengan elemen-elemen dari array @F, menggunakan spasi di antara setiap elemen


Solusi Python:

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


1

Jika Anda tidak ingin memformat ulang bagian dari baris yang tidak Anda potong, solusi terbaik yang dapat saya pikirkan tertulis dalam jawaban saya di:

Bagaimana cara mencetak semua kolom setelah nomor tertentu menggunakan awk?

Ini memotong apa yang sebelum nomor bidang yang diberikan N, dan mencetak semua sisa baris, termasuk nomor bidang N dan mempertahankan spasi asli (tidak memformat ulang). Itu tidak mater jika string bidang muncul juga di tempat lain di baris.

Tentukan fungsi:

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

Dan gunakan seperti ini:

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

Keluaran memelihara segalanya, termasuk spasi tambahan

Dalam kasus khusus Anda:

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

Jika file / stream Anda tidak mengandung karakter baris baru di tengah-tengah baris (Anda bisa menggunakan Pemisah Catatan yang berbeda), Anda dapat menggunakan:

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

Kasus pertama akan gagal hanya dalam file / stream yang berisi karakter nomor 1 langka hexadecimal


0

Ini akan berfungsi jika Anda menggunakan Bash dan Anda bisa menggunakan sebanyak 'x' sebagai elemen yang ingin Anda buang dan mengabaikan banyak ruang jika tidak diloloskan.

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
Ini tidak menjawab pertanyaan, yang secara umum mengharuskan pencetakan dari kolom ke-N sampai akhir .
roaima

0

awkFungsi ini mengembalikan substring $0yang mencakup bidang dari beginke 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);
}

Untuk mendapatkan semuanya mulai dari bidang 3:

tail = fields(3);

Untuk mendapatkan bagian $0yang mencakup bidang 3 hingga 5:

middle = fields(3, 5);

b, e, p, iomong kosong dalam daftar parameter fungsi hanyalah awkcara mendeklarasikan variabel lokal.


0

Saya ingin memperluas jawaban yang diusulkan untuk situasi di mana bidang dibatasi oleh beberapa spasi putih - alasan mengapa OP tidak menggunakan cutsaya kira.

Saya tahu OP bertanya awk, tetapi sedpendekatan akan bekerja di sini (misalnya dengan mencetak kolom dari tanggal 5 hingga yang terakhir):

  • pendekatan sed murni

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

    Penjelasan:

    • s/// digunakan cara standar untuk melakukan substitusi
    • ^\s* cocok dengan spasi putih berurutan di awal baris
    • \S+\s+ berarti kolom data (karakter bukan spasi diikuti oleh karakter spasi)
    • (){4} berarti polanya diulang 4 kali.
  • sed dan potong

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

    dengan hanya mengganti spasi putih berurutan dengan satu tab;

  • tr and cut: trjuga dapat digunakan untuk memeras karakter berurutan dengan -sopsi.

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

-1

Contoh awk terlihat rumit di sini, berikut adalah sintaks Bash shell sederhana:

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

Di mana kolom ke- n1 Anda dihitung dari 0.


Contoh

Mengingat konten file ini ( in.txt):

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

di sini adalah output:

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

c2
c2 c3
c2 c3 c4
c2 c3 c4 c5

-1

Saya tidak senang dengan salah satu awksolusi yang disajikan di sini karena saya ingin mengekstrak beberapa kolom pertama dan kemudian mencetak sisanya, jadi saya beralih ke perl. Kode berikut mengekstrak dua kolom pertama, dan menampilkan sisanya seperti:

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

Keuntungan dibandingkan dengan perlsolusi dari Chris Koknat adalah benar-benar hanya elemen n pertama yang dipisahkan dari string input; sisa string tidak terpecah sama sekali dan karenanya tetap utuh sepenuhnya. Contoh saya menunjukkan ini dengan campuran spasi dan tab.

Untuk mengubah jumlah kolom yang harus diekstraksi, ganti 3dalam contoh dengan n +1.


-1
ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }'

dari jawaban ini tidak buruk tetapi jarak alami hilang.
Silakan bandingkan dengan yang ini:

ls -la | cut -d\  -f4-

Maka Anda akan melihat perbedaannya.

Bahkan ls -la | awk '{$1=$2=""; print}'yang didasarkan pada jawaban sebagai yang terbaik sejauh ini tidak mempertahankan format.

Jadi saya akan menggunakan yang berikut, dan juga memungkinkan kolom selektif eksplisit di awal:

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

Perhatikan bahwa setiap spasi juga dihitung untuk kolom, jadi misalnya di bawah ini, kolom 1 dan 3 kosong, 2 adalah INFO dan 4 adalah:

$ 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

Jika Anda ingin teks yang diformat, rantai perintah Anda dengan gema dan gunakan $ 0 untuk mencetak bidang terakhir.

Contoh:

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

Cetakan:

|  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

Karena jawaban yang salah paling banyak dipilih dengan 340 suara, saya baru saja kehilangan 5 menit hidup saya! Adakah yang mencoba jawaban ini sebelum memperbaiki ini? Tampaknya tidak. Benar-benar tidak berguna.

Saya memiliki log di mana setelah $ 5 dengan alamat IP dapat lebih banyak teks atau tidak ada teks. Saya perlu semuanya, mulai dari alamat IP hingga akhir baris seandainya ada sesuatu setelah $ 5. Dalam kasus saya, ini sebenarnya bukan program awk, bukan onkiner awk jadi awk harus menyelesaikan masalah. Ketika saya mencoba untuk menghapus 4 bidang pertama menggunakan jawaban yang paling terbalik tetapi benar-benar salah:

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

itu mengeluarkan respons yang salah dan tidak berguna (saya menambahkan [..] untuk menunjukkan):

[    37.244.182.218 one two three]

Bahkan ada beberapa saran untuk menggabungkan substrat dengan jawaban yang salah ini. Seperti itu komplikasi adalah perbaikan.

Sebaliknya, jika kolom adalah lebar tetap sampai titik potong dan awk diperlukan, jawaban yang benar adalah:

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

yang menghasilkan output yang diinginkan:

[37.244.182.218 one two three]
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.