Tüm sütunları nn'den sonuncuya yazdırmak için awk kullanma


310

Bu çizgi, ikinci alanda boşluk olana kadar çalıştı.

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

awk her şeyi 2 $ veya daha fazla yazdırmanın bir yolu var mı? (3 $, 4 $ .. artık sütun kalmayıncaya kadar?)

Sanırım bunu Cygwin ile Windows ortamında yaptığımı eklemeliyim.


11
Bir kenara, grep | awkbirawk '/!/ { print $2 }'
antipattern

3
Unix "kesim" daha kolay ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
Roblogic


@tripleee: Bundan bahsettiğiniz için çok mutluyum - her yerde görmek beni hayal kırıklığına uğrattı!
Graham Nicholls

Yanıtlar:


490

ilk sütun hariç tümü yazdıracak:

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

ilk sütun dışındaki tüm sütunları yazdırır:

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

93
gotcha: hakkında önde gelen bir boşluk bırakıyor :(
raphinesse

5
Pragmatik yaklaşımı seviyorum. kedi kullanmaya gerek yok, sadece awk komutundan sonra dosya adını koyun.
kon

45
@raphinesse bunu düzeltebilirsinizawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo

6
Bu boşluk olmayan sınırlayıcılarla çalışmaz, bunları bir boşlukla değiştirir.
Dejan

3
Boşluk olmayan sınırlayıcılar için Çıktı Alanı Ayırıcısını (OFS), örneğin virgül olarak belirtebilirsiniz: awk -F, -vOFS=, '{$1=""; print $0}'Bir başlangıç ​​sınırlayıcısı ( $1yine de boş bir dize olarak dahil edilir) ile sonuçlanırsınız . Bunu ile şerit olabilir sed:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt

99

Kesmeyi kullanarak daha basit bir cevapla yinelenen bir soru var :

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

-dbelirtir delimeter (boşluk) , -fsütunların listesini belirtir (tüm 2. ile başlayan)


Konumu belirtmek için "-b" kullanabilirsiniz (N. Karakterden itibaren).
Dakatine

Bir not olarak, her ne kadar bu gerçekleştirdiği aynı görevi olarak awksürümü olan satır tamponlama sorunları vardır cutki, awk: yok stackoverflow.com/questions/14360640/...
sdaau

24
Güzel ve basit, ancak bir uyarı ile birlikte gelir: awkbirden fazla bitişik boşluk karakterini tedavi eder. tek bir ayırıcı olarak cutdeğil; ayrıca - eldeki durumda bu bir sorun olmasa da - cutsadece tek, gerçek bir karakter kabul eder. sınırlayıcı olarak, awkregex sağlar.
mklement0

Buna dayanarak: stackoverflow.com/a/39217130/8852408 , bu çözümün çok verimli olmaması muhtemeldir.
FcknGioconda

85

$ 2 ile $ NF arasındaki alanları (satırdaki alan sayısını temsil eden yerleşik değişken) yazdırmak için bir for-loop kullanabilirsiniz.

Düzenleme: "Yazdır" bir satırsonu eklediğinden, sonuçları arabelleğe almak istersiniz:

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

Alternatif olarak printf:

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

Bu yüzden denedim, ama bir şey eksik düşünüyorum .. İşte svn statüsü ne yaptı | grep '\!' | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> removeProjs
Andy

Yazdırma bir satırsonu eklediğinden, sonuçları arabelleğe almak istersiniz. Düzenlememe bakın.
VeeArr

1
Bu cevabı daha iyi seviyorum çünkü alanlar arasında nasıl döngü yapılacağını gösteriyor.
Edward Falk

3
Yazdırmanın boşluk kullanmasını istiyorsanız, çıktı kaydı ayırıcısını değiştirin: awk '{ORS = ""; için (i = 2; i <NF; i ++) $ i} 'somefile yazdır
Christian Lescuyer

3
Her zaman çok fazla boşluk olacak. Bu daha iyi çalışır: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'Önde veya arkada boşluk yok.
Marki

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

Cevabım VeeArr'ınkine dayanıyor , ancak ikinci sütunu (ve geri kalanını) yazdırmadan önce beyaz bir boşlukla başladığını fark ettim. Ben sadece 1 itibar noktası var, ben bu konuda yorum yapamam, bu yüzden burada yeni bir cevap olarak gider:

ikinci sütun olarak "out" ile başlayın ve sonra diğer tüm sütunları ekleyin (varsa). Bu, ikinci bir sütun olduğu sürece iyi gider.


2
Mükemmel, ayrıca önemli olan çıkış değişkeninin önündeki $ 'ı kaldırdınız.
Alexis Wilke

15

Awk ile çoğu çözüm bir boşluk bırakır. Buradaki seçenekler bu sorunu önler.

seçenek 1

Basit bir kesim çözümü (sadece tek sınırlayıcılarla çalışır):

command | cut -d' ' -f3-

seçenek 2

Bir awk re-calc'i zorlamak bazen ilk alanları kaldırarak kalan ek boşlukları (OFS) kaldırır (awk'ın bazı sürümleriyle çalışır):

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

Seçenek 3

Biçimlendirilmiş her alanın yazdırılması printfdaha fazla kontrol sağlar:

$ 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

Ancak, önceki tüm yanıtlar alanlar arasındaki tüm tekrarlanan FS'leri OFS olarak değiştirir. Bunu yapmayan birkaç seçenek oluşturalım.

Seçenek 4 (önerilir)

Öndeki alanları ve sınırlayıcıları kaldırmak için alt içeren bir döngü.
Ve boşluk yerine FS değerini kullanarak (değiştirilebilir).
Daha taşınabilir mıdır ve OFS FS değişikliği tetiklemez: NOT:^[FS]* öncü boşluklu bir giriş kabul etmektir.

$ 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

Seçenek 5

Ekstra (öncü veya sondaki) boşluk eklemeyen bir çözüm oluşturmak ve gensubGNU awk işlevini kullanarak mevcut boşlukları korumak mümkündür :

$ 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 

Ayrıca, bir sayı verilen bir grup alanı takas etmek için de kullanılabilir 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  !

Tabii ki, bu durumda OFS, hattın her iki parçasını ayırmak için kullanılır ve alanların sondaki beyaz boşluğu hala yazdırılır.

NOT: [FS]* giriş satırında önde gelen boşluklara izin vermek için kullanılır.


13

Ben şahsen yukarıda belirtilen tüm cevapları denedim, ama çoğu biraz karmaşık veya sadece doğru değildi. Benim açımdan bunu yapmanın en kolay yolu:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Burada -F "" awk'ın kullanacağı sınırlayıcıyı tanımlar. Benim durumumda da awk için varsayılan sınırlayıcı olan boşluk. Bu, -F "" öğesinin yok sayılabileceği anlamına gelir.

  2. Burada NF toplam alan / sütun sayısını tanımlar. Bu nedenle döngü 4. alandan son alana / sütuna kadar başlar.

  3. Burada $ N, Nth alanının değerini alır. Bu nedenle $ i yazdır, döngü sayısına bağlı olarak geçerli alanı / sütunu yazdırır.


4
Sorun, her alanı farklı bir satıra yazdırır.
mveroone

hiçbir şey sonunda bu eklemeyi durur :-) `| tr '\ n' '' '
koullislp

3
Biraz geç ama awk '{for (i = 5; i <= NF; i ++) {printf "% s", $ i}}'
plitter


7

Bu beni çok rahatsız ediyordu, oturdum ve cutGNU Awk 3.1.7 ile test edilen benzeri bir alan spesifikasyonu ayrıştırıcısı yazdım .

İlk olarak pfcut, örn.

sudo nano /usr/share/awk/pfcut

Ardından, aşağıdaki komut dosyasını yapıştırın ve kaydedin. Bundan sonra, kullanım şöyle görünür:

$ 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

Tüm bunları yazmaktan kaçınmak için, yapabileceğimden en iyisini (başka türlü bkz. Otomatik olarak awk ile başlangıçta bir kullanıcı işlevi yükleyin? - Unix & Linux Stack Exchange ) bir takma ad eklemek ~/.bashrc; örneğin:

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

... o zaman arayabilirsiniz:

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

İşte pfcutbetiğin kaynağı :

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

cutawk
Kullanmak

5

# 2'den başlayan sütunları yazdırma (çıktının başında boşluk olmayacaktır):

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

1
Güzel, ancak +boşluktan sonra eklemeniz gerekir , çünkü alanlar 1'den awkfazla boşlukla ayrılabilir ( birden fazla bitişik boşluğu tek bir ayırıcı olarak ele alır). Ayrıca, awkönde gelen boşlukları yok sayar, bu nedenle normal ifadeyle başlamalısınız ^[ ]*. Ayırıcı olarak boşluk sayesinde çözümü genelleştirebilirsiniz; örneğin, aşağıdaki 3. alandan her şeyi döndürür: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'Yine de, rastgele alan ayırıcıları ile daha zorlaşır.
mklement0

5

Bu işe yarar mı?

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

Önde biraz boşluk bırakıyor.


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

bu, son alan hariç tümünü yazdırmak için awk kullanır


3

Tüm önerilerden tercih ettiğim şey bu:

6. sütundan son sütuna yazdırma.

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

veya

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

2

Rasgele sınırlayıcıyla yazdırılmış belirli sütunlara ihtiyacınız varsa:

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

col # 3 col # 4

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

col # 3anythingcol 4.

Bir sütunda boşluk varsa, bu iki sütun olacaktır, ancak herhangi bir sınırlayıcıyla veya onsuz bağlayabilirsiniz.


2

Perl çözeltisi:

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

Bu komut satırı seçenekleri kullanılır:

  • -n giriş dosyasının her satırı etrafında döngü, her satırı otomatik olarak yazdırmayın

  • -l işlemeden önce yeni satırları kaldırır ve daha sonra tekrar ekler

  • -aotomatik bölme modu - giriş satırlarını @F dizisine böler. Boşlukta bölme varsayılanları

  • -e perl kodunu çalıştır

splice @F,0,1 @F dizisinden 0 sütununu temiz bir şekilde kaldırır

join " ",@F her öğenin arasında bir boşluk kullanarak @F dizisinin öğelerine katılır


Python çözümü:

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


1

Çizginin kesmediğin kısmını yeniden biçimlendirmek istemiyorsanız, aklıma gelen en iyi çözüm cevabımda şöyle yazılıyor:

Awk kullanarak belirli bir sayıdan sonra tüm sütunları nasıl yazdırabilirim?

Verilen alan numarası N'den önce olanı keser ve alan numarası N dahil olmak üzere orijinal aralığın korunması da dahil olmak üzere satırın geri kalanını yazdırır (yeniden biçimlendirmez). Alanın dizesinin satırda başka bir yerde de görünüp görünmediğini bilmiyor.

Bir fonksiyon tanımlayın:

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

Ve şu şekilde kullanın:

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

Çıktı, sondaki boşluklar dahil her şeyi korur

Belirli bir durumda:

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

Dosyanız / akışınız satırların ortasında yeni satır karakterleri içermiyorsa (farklı bir Kayıt Ayırıcı kullanıyor olabilirsiniz), şunları kullanabilirsiniz:

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

İlk durum yalnızca nadir onaltılık karakter sayısını içeren dosyalarda / akışlarda başarısız olur 1


0

Bu, Bash kullanıyorsanız ve atmak istediğiniz öğelerin sayısı kadar 'x' kullanabildiğinizde işe yarar ve kaçmazlarsa birden çok alanı yok sayar.

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
Bu , N sütunundan sonuna kadar yazdırma gereksinimini genelleştiren soruya cevap vermez .
roaima

0

Bu awkişlev $0, alanlarını içeren alt dizeyibegin için 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);
}

Alan 3'ten başlayarak her şeyi almak için:

tail = fields(3);

Bunun bir bölümünü almak için $03 ila 5 arasındaki alanları kapsar:

middle = fields(3, 5);

b, e, p, iişlev parametre listesindeki anlamsızlık, awkyerel değişkenleri bildirmenin bir yoludur.


0

Önerilen cevapları, alanların muhtemelen birkaç boşlukla sınırlandırıldığı duruma , OP'nin kullanmadığı nedene uzatmak istiyorumcut inanıyorum.

OP'nin sorduğunu biliyorum awk, ama sedburada bir yaklaşım işe yarayacaktı (5'ten sonuncuya kadar sütunları yazdırmakla örnek):

  • saf sed yaklaşım

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

    Açıklama:

    • s/// yerine koymanın standart yolu kullanılır
    • ^\s* satırın başındaki ardışık boşluklarla eşleşir
    • \S+\s+ bir veri sütunu anlamına gelir (boşluk olmayan grafikler ve ardından boşluk karakterleri)
    • (){4} desen 4 kez tekrarlanır anlamına gelir.
  • sed ve kes

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

    ardışık beyaz boşlukları tek bir sekme ile değiştirerek;

  • tr ve cut: seçeneği ile ardışık karakterleri sıkıştırmaktr için de kullanılabilir .-s

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

-1

Awk örnekleri burada karmaşık görünüyor, işte basit Bash kabuğu sözdizimi:

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

Nerede 1senin olan , n 0 ila sütun sayma inci.


Misal

Bu dosya içeriği ( in.txt) verildiğinde :

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

İşte çıktı:

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

c2
c2 c3
c2 c3 c4
c2 c3 c4 c5

-1

awkBurada sunulan çözümlerden hiçbirinden memnun kalmadım çünkü ilk birkaç sütunu ayıklayıp sonra kalanını yazdırmak istedim, perlbunun yerine döndüm . Aşağıdaki kod ilk iki sütunu ayıklar ve geri kalanını şu şekilde görüntüler:

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

Chris Koknat'ınperl çözümüne kıyasla avantajı , gerçekten sadece ilk n öğenin giriş dizesinden ayrılmasıdır; ipin geri kalanı hiç bölünmez ve bu nedenle tamamen bozulmadan kalır. Örneğim bunu boşluklar ve sekmelerden oluşan bir karışımla gösteriyor.

Çıkarılması gereken sütun miktarını değiştirmek için 3, örnekteki n + 1 ile değiştirin .


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

dan bu cevabı fena değil ama doğal aralık kayboldu.
Lütfen daha sonra bunu karşılaştırın:

ls -la | cut -d\  -f4-

Sonra farkı görürsünüz.

Şimdiye kadar en iyi oyu alan cevabals -la | awk '{$1=$2=""; print}' dayanan bile biçimlendirmeyi korumak değildir.

Böylece aşağıdakileri kullanırdım ve başlangıçta açık seçmeli sütunlara da izin verir:

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

Her boşluğun sütunlar için de sayıldığına dikkat edin, örneğin aşağıdaki sütun 1 ve 3 boş, 2 INFO ve 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

Biçimlendirilmiş metin istiyorsanız, komutlarınızı yankı ile zincirleyin ve son alanı yazdırmak için $ 0 kullanın.

Misal:

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

Baskılar:

|  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 oyla en çok oylanan yanlış cevap yüzünden hayatımın 5 dakikasını kaybettim! Bunu cevaplamadan önce kimse bu cevabı denedi mi? Görünüşe göre değil. Tamamen işe yaramaz.

Bir IP adresi ile 5 $ sonra daha fazla metin veya metin olabilir bir günlük var. 5 $ sonra bir şey olmalı IP adresinden satırın sonuna kadar her şeye ihtiyacım var. Benim durumumda, bu aslında bir awk programı ile bir awk oneliner değil, bu yüzden awk sorunu çözmelidir. En çok oylanan ama tamamen yanlış cevabı kullanarak ilk 4 alanı kaldırmaya çalıştığımda:

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

yanlış ve işe yaramaz cevap tükürür (ben göstermek için [..] ekledi):

[    37.244.182.218 one two three]

Substr'yi bu yanlış cevapla birleştirmek için bazı öneriler bile var. Bu komplikasyon gibi bir iyileşme.

Bunun yerine, sütunlar kesme noktası ve awk gerekli olana kadar sabit genişlikteyse, doğru cevap:

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

istenen çıktıyı üreten:

[37.244.182.218 one two three]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.