การพิมพ์ทุกอย่างยกเว้นฟิลด์แรกด้วย awk


108

ฉันมีไฟล์ที่มีลักษณะดังนี้:

AE  United Arab Emirates
AG  Antigua & Barbuda
AN  Netherlands Antilles
AS  American Samoa
BA  Bosnia and Herzegovina
BF  Burkina Faso
BN  Brunei Darussalam

และฉันต้องการที่จะกลับคำสั่งพิมพ์ทุกอย่างก่อนยกเว้น $ 1 แล้ว $ 1:

United Arab Emirates AE

ฉันจะทำเคล็ดลับ "ทุกอย่างยกเว้นช่อง 1" ได้อย่างไร


2
สวัสดี @cfisher สามารถทำได้โดยไม่ต้องวนซ้ำโดยไม่ต้องใช้พื้นที่เพิ่มเติม
Juan Diego Godoy Robles

คำตอบ:


91

การมอบหมาย$1งาน แต่จะเว้นวรรคนำหน้า:awk '{first = $1; $1 = ""; print $0, first; }'

คุณยังสามารถค้นหาจำนวนคอลัมน์ในNFและใช้แบบวนซ้ำได้


2
สำหรับคนขี้เกียจโดยสิ้นเชิง นี่คือรหัส klashxx'
Serge Stroobandt

1
เยี่ยมมาก กำจัดพื้นที่ชั้นนำด้วย sed: awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Thyag

ช่องว่างจะถูกลบออกอย่างง่ายดายด้วย VIM กด 'Ctrl + V Gd' ในโหมดปกติ
สันติ

107

$1=""เว้นช่องว่างตามที่ Ben Jackson พูดถึงดังนั้นให้ใช้forห่วง:

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

ดังนั้นหากสตริงของคุณเป็น "หนึ่งสองสาม" ผลลัพธ์จะเป็น:

สอง
สาม

หากคุณต้องการให้ผลลัพธ์เป็นแถวเดียวคุณสามารถทำได้ดังนี้:

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

สิ่งนี้จะทำให้คุณ: "สองสาม"


4
และช่องว่างต่อท้าย
NeronLeVelu

2
ดีกว่าที่จะใช้: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' ซึ่ง: พิมพ์ฟิลด์ 2 เป็น NF เพิ่มตัวคั่นฟิลด์เอาต์พุตตามต้องการ (เช่นยกเว้นก่อน $ 2) การพิมพ์ครั้งล่าสุดจะเพิ่มบรรทัดสุดท้ายเพื่อสิ้นสุดการพิมพ์บรรทัดปัจจุบัน สิ่งนั้นจะใช้ได้ผลถ้าคุณเปลี่ยน FS / OFS (กล่าวคือจะไม่เป็น "ช่องว่าง" เสมอไป)
Olivier Dulac

อันที่สองใช้งานได้ดีสำหรับฉันจริงๆ คนแรกไม่มาก ไม่แน่ใจจริงๆว่าทำไม มันแบ่งข้อความทั้งหมด
เสียง

72

ใช้cutคำสั่งกับ--complementตัวเลือก:

$ echo a b c | cut -f 1 -d ' '
a
$ echo a b c | cut -f 1,2 -d ' '
a b
$ echo a b c | cut -f 1 -d ' ' --complement
b c

2
ในขณะที่ไม่ได้ตอบคำถามเฉพาะสำหรับ awk แต่ฉันพบว่าสิ่งนี้มีประโยชน์ที่สุดเนื่องจาก awk กำลังลบช่องว่างที่ซ้ำกันและไม่ตัด
Fmstrat

19
echo a b c | cut -d' ' -f 2- เป็นอีกทางเลือกหนึ่ง
Luis

2
Nice - โซลูชัน @Luis ทำงานบน Mac ซึ่งไม่รองรับ --complement
metadaddy

21

อาจเป็นวิธีที่รัดกุมที่สุด:

$ awk '{$(NF+1)=$1;$1=""}sub(FS,"")' infile
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

คำอธิบาย:

$(NF+1)=$1: สร้างฟิลด์สุดท้าย "ใหม่"

$1="": ตั้งค่าฟิลด์แรกดั้งเดิมเป็นโมฆะ

sub(FS,""): หลังจากสองการกระทำแรก{$(NF+1)=$1;$1=""}กำจัดตัวคั่นฟิลด์แรกโดยใช้ย่อย พิมพ์สุดท้ายโดยปริยาย


13
awk '{sub($1 FS,"")}7' YourFile

ลบฟิลด์แรกและตัวคั่นและพิมพ์ผลลัพธ์ ( 7เป็นค่าที่ไม่ใช่ศูนย์ดังนั้นการพิมพ์ $ 0)


ตอบดีที่สุด! โหวตแล้ว ต่างจากแค่ใช้ยัง1ไง? ฉันสงสัยการใช้รูปแบบนี้และต้องการเข้าใจสิ่งนั้น ขอบคุณ!
Abhijeet Rastogi

10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

การตั้งค่าฟิลด์แรกที่""ใบสำเนาเดียวของจุดเริ่มต้นของOFS $0สมมติว่าOFSเป็นเพียงตัวเดียว (โดยค่าเริ่มต้นมันเป็นพื้นที่เดียว) substr($0, 2)เราสามารถลบออกได้ด้วย จากนั้นต่อท้ายสำเนาที่บันทึกไว้ของ$1.


6

หากคุณเปิดรับโซลูชัน Perl ...

perl -lane 'print join " ",@F[1..$#F,0]' file

เป็นวิธีง่ายๆที่มีตัวคั่นอินพุต / เอาต์พุตของช่องว่างเดียวซึ่งสร้าง:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

อันต่อไปนี้ซับซ้อนกว่าเล็กน้อย

perl -F`  ` -lane 'print join "  ",@F[1..$#F,0]' file

และถือว่าตัวคั่นอินพุต / เอาต์พุตเป็นช่องว่างสองช่อง:

United Arab Emirates  AE
Antigua & Barbuda  AG
Netherlands Antilles  AN
American Samoa  AS
Bosnia and Herzegovina  BA
Burkina Faso  BF
Brunei Darussalam  BN

ใช้ตัวเลือกบรรทัดคำสั่งเหล่านี้:

  • -n วนรอบทุกบรรทัดของไฟล์อินพุตอย่าพิมพ์ทุกบรรทัดโดยอัตโนมัติ

  • -l ลบบรรทัดใหม่ก่อนประมวลผลและเพิ่มกลับเข้าไปในภายหลัง

  • -aโหมด autosplit - แยกบรรทัดอินพุตลงในอาร์เรย์ @F ค่าเริ่มต้นคือการแบ่งช่องว่าง

  • -F ตัวปรับเปลี่ยนการแยกอัตโนมัติในตัวอย่างนี้แบ่งเป็น '' (สองช่องว่าง)

  • -e เรียกใช้รหัส perl ต่อไปนี้

@Fคืออาร์เรย์ของคำในแต่ละบรรทัดดัชนีเริ่มต้นด้วย 0
$#Fคือจำนวนคำใน@F
@F[1..$#F]อาร์เรย์ขององค์ประกอบ 1 ถึงองค์ประกอบสุดท้าย
@F[1..$#F,0]คืออาร์เรย์ขององค์ประกอบ 1 ถึงองค์ประกอบสุดท้ายบวกองค์ประกอบ 0


1
ฉันเรียกใช้และมีหมายเลขพิเศษในตอนท้ายดังนั้นฉันจึงใช้เวอร์ชันนี้: perl -lane 'shift @F; print join "", @F '
Hans Poo

2

ตัวคั่นฟิลด์ใน gawk (อย่างน้อย) สามารถเป็นสตริงและอักขระ (อาจเป็นนิพจน์ทั่วไปก็ได้) หากข้อมูลของคุณสอดคล้องกันสิ่งนี้จะได้ผล:

awk -F "  " '{print $2,$1}' inputfile

นั่นคือช่องว่างสองช่องระหว่างเครื่องหมายคำพูดคู่


คำตอบที่ดีที่สุดสำหรับสถานการณ์ในมือ แต่ในทางเทคนิคแล้วสิ่งนี้ไม่ได้ตอบคำถามเกี่ยวกับวิธีการพิมพ์ทุกอย่างยกเว้นฟิลด์แรก
Dan Molding

@DanMoulding: ตราบใดที่ไฟล์มีความสอดคล้องกันในการใช้ช่องว่างสองช่องเพื่อแยกรหัสประเทศและไม่มีการเว้นวรรคสองช่องด้วยกันคำตอบของฉันก็ตอบคำถามได้
หยุดชั่วคราวจนกว่าจะมีประกาศอีกครั้ง

2
ผู้ที่ถามคำถามนี้มาที่นี่เพราะต้องการทราบวิธีพิมพ์ทุกอย่างยกเว้นช่องแรก (ดูหัวข้อคำถาม) นั่นคือสิ่งที่ฉันมาถึงที่นี่ คำตอบของคุณแสดงวิธีการพิมพ์ฟิลด์แรกตามด้วยฟิลด์ที่สอง แม้ว่านี่อาจเป็นวิธีแก้ปัญหาที่ดีที่สุดสำหรับสถานการณ์เฉพาะของ OP แต่ก็ไม่ได้ช่วยแก้ปัญหาทั่วไปในการพิมพ์ทุกอย่างยกเว้นฟิลด์แรก
แดนปั้น


2

ลองย้ายระเบียนทั้งหมดไปที่รายการถัดไปและตั้งค่ารายการสุดท้ายเป็นรายการแรก:

$ awk '{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' file
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

คำอธิบาย

  • a=$1 บันทึกค่าแรกลงในตัวแปรชั่วคราว
  • for (i=2; i<=NF; i++) $(i-1)=$i บันทึกค่าฟิลด์ N ลงในฟิลด์ (N-1) th
  • $NF=aบันทึกค่าแรก ( $1) ลงในฟิลด์สุดท้าย
  • {}1สภาพจริงที่จะทำให้การดำเนินการเริ่มต้น:awk{print $0}

ด้วยวิธีนี้หากคุณมีตัวคั่นฟิลด์อื่นผลลัพธ์ก็จะดีเช่นกัน:

$ cat c
AE-United-Arab-Emirates
AG-Antigua-&-Barbuda
AN-Netherlands-Antilles
AS-American-Samoa
BA-Bosnia-and-Herzegovina
BF-Burkina-Faso
BN-Brunei-Darussalam

$ awk 'BEGIN{OFS=FS="-"}{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' c
United-Arab-Emirates-AE
Antigua-&-Barbuda-AG
Netherlands-Antilles-AN
American-Samoa-AS
Bosnia-and-Herzegovina-BA
Burkina-Faso-BF
Brunei-Darussalam-BN


1

ตัวเลือกที่ 1

มีวิธีแก้ปัญหาที่ใช้ได้กับ awk บางเวอร์ชัน:

awk '{ $(NF+1)=$1;$1="";$0=$0;} NF=NF ' infile.txt

คำอธิบาย:

       $(NF+1)=$1                          # add a new field equal to field 1.
                  $1=""                    # erase the contents of field 1.
                        $0=$0;} NF=NF      # force a re-calc of fields.
                                           # and use NF to promote a print.

ผลลัพธ์:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

อย่างไรก็ตามอาจล้มเหลวกับ awk เวอร์ชันเก่ากว่า


ทางเลือกที่ 2

awk '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

นั่นคือ:

awk '{                                      # call awk.
       $(NF+1)=$1;                          # Add one trailing field.
                  $1="";                    # Erase first field.
                        sub(OFS,"");        # remove leading OFS.
                                    }1'     # print the line.

โปรดทราบว่าสิ่งที่ต้องลบคือ OFS ไม่ใช่ FS บรรทัดได้รับการคำนวณใหม่เมื่อฟิลด์ $ 1 เป็น asigned ที่เปลี่ยนการรัน FS ทั้งหมดเป็นหนึ่ง OFS


แต่ตัวเลือกนั้นก็ยังล้มเหลวด้วยตัวคั่นหลายตัวดังที่แสดงให้เห็นอย่างชัดเจนโดยการเปลี่ยน OFS:

awk -v OFS=';' '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

บรรทัดนั้นจะแสดงผล:

United;Arab;Emirates;AE
Antigua;&;Barbuda;AG
Netherlands;Antilles;AN
American;Samoa;AS
Bosnia;and;Herzegovina;BA
Burkina;Faso;BF
Brunei;Darussalam;BN

นั่นแสดงให้เห็นว่าการทำงานของ FS กำลังเปลี่ยนเป็นหนึ่งใน OFS
วิธีเดียวที่จะหลีกเลี่ยงได้คือหลีกเลี่ยงการคำนวณฟิลด์ซ้ำ
ฟังก์ชันหนึ่งที่สามารถหลีกเลี่ยงการเกิดตะกรันซ้ำได้คือย่อย
สามารถจับฟิลด์แรกจากนั้นลบออกจาก $ 0 พร้อมซับแล้วพิมพ์ใหม่ทั้งคู่

ตัวเลือก 3

awk '{ a=$1;sub("[^"FS"]+["FS"]+",""); print $0, a;}' infile.txt
       a=$1                                   # capture first field.
       sub( "                                 # replace: 
             [^"FS"]+                         # A run of non-FS
                     ["FS"]+                  # followed by a run of FS.
                            " , ""            # for nothing.
                                  )           # Default to $0 (the whole line.
       print $0, a                   # Print in reverse order, with OFS.


United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

แม้ว่าเราจะเปลี่ยน FS, OFS และ / หรือเพิ่มตัวคั่นมากขึ้นก็ใช้ได้
หากไฟล์อินพุตเปลี่ยนเป็น:

AE..United....Arab....Emirates
AG..Antigua....&...Barbuda
AN..Netherlands...Antilles
AS..American...Samoa
BA..Bosnia...and...Herzegovina
BF..Burkina...Faso
BN..Brunei...Darussalam

และคำสั่งเปลี่ยนเป็น:

awk -vFS='.' -vOFS=';' '{a=$1;sub("[^"FS"]+["FS"]+",""); print $0,a;}' infile.txt

ผลลัพธ์จะเป็น (ยังคงรักษาตัวคั่น):

United....Arab....Emirates;AE
Antigua....&...Barbuda;AG
Netherlands...Antilles;AN
American...Samoa;AS
Bosnia...and...Herzegovina;BA
Burkina...Faso;BF
Brunei...Darussalam;BN

คำสั่งสามารถขยายได้หลายช่อง แต่มีเฉพาะ awks ที่ทันสมัยและตัวเลือก --re-interval ที่ใช้งานอยู่ คำสั่งนี้ในไฟล์ต้นฉบับ:

awk -vn=2 '{a=$1;b=$2;sub("([^"FS"]+["FS"]+){"n"}","");print $0,a,b;}' infile.txt

จะส่งออกสิ่งนี้:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei


0

มีตัวเลือก sed ด้วย ...

 sed 's/\([^ ]*\)  \(.*\)/\2 \1/' inputfile.txt

อธิบาย ...

Swap
\([^ ]*\) = Match anything until we reach a space, store in $1
\(.*\)    = Match everything else, store in $2
With
\2        = Retrieve $2
\1        = Retrieve $1

อธิบายเพิ่มเติมอย่างละเอียด ...

s    = Swap
/    = Beginning of source pattern
\(   = start storing this value
[^ ] = text not matching the space character
*    = 0 or more of the previous pattern
\)   = stop storing this value
\(   = start storing this value
.    = any character
*    = 0 or more of the previous pattern
\)   = stop storing this value
/    = End of source pattern, beginning of replacement
\2   = Retrieve the 2nd stored value
\1   = Retrieve the 1st stored value
/    = end of replacement

0

อีกวิธีหนึ่ง ...

... สิ่งนี้จะเข้าร่วมฟิลด์ 2 ถึง NF ด้วย FS อีกครั้งและเอาต์พุตหนึ่งบรรทัดต่อบรรทัดของอินพุต

awk '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

ฉันใช้สิ่งนี้กับคอมไพล์เพื่อดูว่าไฟล์ใดได้รับการแก้ไขในที่ทำงานของฉัน:

git diff| \
    grep '\-\-git'| \
    awk '{print$NF}'| \
    awk -F"/" '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

-3

อีกวิธีที่ง่ายและสะดวกโดยใช้คำสั่ง cat

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename

ฉันลดคะแนนลงเพราะนี่ไม่ใช่แนวทางแบบไดนามิก ด้วยวิธีนี้คุณจำเป็นต้องทราบจำนวนอาร์กิวเมนต์และถือว่าข้อมูลของคุณสอดคล้องกัน ข้อมูลแทบจะไม่สอดคล้องกันเลยและแนวทางของคุณต้องคำนึงถึงสิ่งนี้เป็นส่วนใหญ่
xh3b4sd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.