ความหมายของ IFS = $ '\ n' ในการเขียนสคริปต์ทุบตีคืออะไร?


163

ที่จุดเริ่มต้นของสคริปต์เปลือก bash คือบรรทัดต่อไปนี้:

IFS=$'\n'

อะไรคือความหมายที่อยู่เบื้องหลังชุดสัญลักษณ์นี้


3
ดูเพิ่มเติมunix.stackexchange.com/questions/26784/understanding-ifsและคำถามที่อ้างถึง
Gilles

ไวยากรณ์เป็นกลุ่มเน้นสิ่งนี้เป็นข้อผิดพลาดสำหรับฉัน แก้ไขปัญหา?
theonlygusti

IFS=$'\n'เป็น bashism (+ เชลล์อื่น ๆ ให้ใช้การอ้างอิง ANSI-Cสำหรับการแก้ไขปัญหาดูstackoverflow.com/questions/10748703/ …
pevik

คำตอบ:


199

IFSย่อมาจาก "เขตคั่นภายใน" มันถูกใช้โดยเชลล์เพื่อพิจารณาวิธีแยกคำเช่นวิธีการจดจำขอบเขตของคำ

ลองนี้ในเชลล์เช่น bash (เชลล์อื่นอาจจัดการสิ่งนี้แตกต่างกันเช่น zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done

ค่าเริ่มต้นสำหรับIFSประกอบด้วยอักขระช่องว่าง (ต้องแม่นยำ: space, tab และ newline) ตัวละครแต่ละตัวสามารถเป็นขอบเขตของคำ ดังนั้นด้วยค่าเริ่มต้นของIFSลูปด้านบนจะพิมพ์:

Word: foo:bar
Word: baz
Word: rab

กล่าวอีกนัยหนึ่งเชลล์คิดว่าช่องว่างเป็นขอบเขตของคำ

ตอนนี้ลองตั้งค่าIFS=:ก่อนดำเนินการวนรอบ เวลานี้ผลลัพธ์คือ:

Word: foo
Word: bar baz rab

ตอนนี้เปลือกแยกออกmystringเป็นคำเช่นกัน - แต่ตอนนี้มันใช้เฉพาะกับลำไส้ใหญ่เป็นขอบเขตของคำเท่านั้น

อักขระตัวแรกของIFSคือพิเศษ: มันถูกใช้เพื่อคั่นคำในผลลัพธ์เมื่อใช้$*ตัวแปรพิเศษ(ตัวอย่างที่นำมาจากคำแนะนำการใช้สคริปต์การทุบตีขั้นสูงซึ่งคุณยังสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับตัวแปรพิเศษแบบนั้น):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z

เปรียบเทียบกับ:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z

โปรดทราบว่าในทั้งสองตัวอย่างเชลล์จะยังคงใช้อักขระ:ทั้งหมด-และ;เป็นขอบเขตของคำ $*สิ่งเดียวที่มีการเปลี่ยนแปลงเป็นพฤติกรรมของ

อีกสิ่งที่สำคัญที่จะรู้ว่าเป็นวิธีการที่เรียกว่า "ไอเอฟเอช่องว่าง" ได้รับการปฏิบัติ โดยทั่วไปทันทีที่IFSมีอักขระช่องว่างช่องว่างนำหน้าและต่อท้ายจะถูกแยกออกจากสตริงที่จะแยกก่อนการประมวลผลและลำดับของอักขระช่องว่างต่อเนื่องจะคั่นฟิลด์ด้วย อย่างไรก็ตามสิ่งนี้ใช้กับอักขระช่องว่างที่มีอยู่จริงIFSเท่านั้น

ตัวอย่างเช่นลองดูที่สตริง"a:b:: c d "(ช่องว่างต่อท้ายและอักขระเว้นวรรคสองตัวระหว่างcและd)

  1. ด้วยIFS=:มันจะถูกแบ่งออกเป็นสี่เขต: "a", "b", ""(สตริงว่าง) และ" c d "(อีกครั้งสองช่องว่างระหว่างcและd) หมายเหตุช่องว่างนำหน้าและต่อท้ายในฟิลด์สุดท้าย
  2. ด้วยIFS=' :'ก็จะถูกแบ่งออกเป็นห้าเขต: "a", "b", ""(สตริงว่าง) และ"c" "d"ไม่มีช่องว่างชั้นนำและต่อท้ายที่ใดก็ได้

โปรดสังเกตว่าอักขระช่องว่างต่อเนื่องหลายตัวคั่นขอบเขตสองฟิลด์ในตัวอย่างที่สองในขณะที่เครื่องหมายโคลอนหลายคู่ไม่ทำงาน (เนื่องจากไม่ใช่อักขระช่องว่าง)

ในฐานะที่เป็นIFS=$'\n'ที่เป็นksh93ไวยากรณ์การสนับสนุนโดยbash, zsh, mkshและ FreeBSD sh(ด้วยรูปแบบระหว่างเปลือกหอยทั้งหมด) การอ้างถึง bash manpage:

คำของฟอร์ม 'string' ได้รับการปฏิบัติเป็นพิเศษ คำนี้ขยายเป็น "string" โดยใช้อักขระเครื่องหมายทับขวาทับกลับแทนที่ตามที่ระบุโดยมาตรฐาน ANSI C

\nเป็นลำดับ escape สำหรับการขึ้นบรรทัดใหม่ดังนั้นIFSสิ้นสุดการตั้งค่าเป็นอักขระขึ้นบรรทัดใหม่เดียว


3
นี่เป็นสิ่งที่ดี แต่ในความคิดของฉันคุณควรจะอ่านและทำความเข้าใจกับ POSIX spec ได้ดีกว่าbashคู่มือการเขียนสคริปต์หรืออะไรก็ตาม ในหลักข้อมูลที่มีอยู่เช่นการเชื่อมโยงที่ขาดในวิธีที่สำคัญ อย่างไรก็ตามถึงจุดสำคัญสองข้อที่เกี่ยวข้องกับการแยกเปลือก - กลมและ IFS whitespace
mikeserv

@mikeserv ขอบคุณฉันเพิ่มข้อมูลบางอย่างในช่องว่างของ IFS ไม่รู้เรื่องนั้น :)
Tblue

4
ไม่เกี่ยวข้องเท่ากัน แต่ถ้าคุณอยากรู้คุณอาจต้องการดูว่าunset IFSเชลล์มีพฤติกรรมแตกต่างกันIFS=อย่างไร ไบต์แรกใน IFS ก็มีความสำคัญ"${named_array[*]}"เช่นกัน - แต่ไม่สำคัญว่าเมื่อใดที่การขยายตัวไม่ได้รับการ
ร้องขอ

จุดเพิ่มเติมสองสามข้อ: การแยกคำ 1- คำซึ่งควบคุมโดย$IFSเป็นหนึ่งในสองสิ่งหลักที่ดำเนินการเมื่อขยายตัวแปรที่ไม่มีเครื่องหมายในบริบทรายการ (เป็นsplitส่วนหนึ่งของsplit+globโอเปอเรเตอร์) อีกอันหนึ่งกำลังโค้ง เมื่อใช้การแยกงานโดยทั่วไปคุณจะต้องมีปัญหาset -fในการปิดการใช้งานชิ้นglobส่วน
Stéphane Chazelas

3
3- $IFSยังใช้โดยreadคำสั่ง builtin
Stéphane Chazelas

22

ภายในเครื่องหมายอัญประกาศเดียวตัวละครบางตัวได้รับการประเมินพิเศษ ตัวอย่างเช่น\nแปลเป็นบรรทัดใหม่

ดังนั้นบรรทัดนี้จะกำหนดบรรทัดใหม่ให้กับตัวแปร IFS IFS ในที่สุดก็เป็นตัวแปรพิเศษใน bash: Internal Field Separator อย่างที่man bashบอกไปแล้วว่า

ใช้สำหรับการแยกคำหลังการขยายและเพื่อแยกบรรทัดเป็นคำด้วยreadคำสั่ง builtin <space><tab><newline>ค่าเริ่มต้นคือ


5
+1 สำหรับการกล่าวถึงdollared single quotesซึ่งแตกต่างจากคำพูดเดี่ยวง่าย ๆ
Snowcrash

2
@Snowcrash +1 สำหรับพูด+1 สำหรับการกล่าวขวัญของราคาเดียว dollaredซึ่งแตกต่างจากคำพูดเดียวง่าย ขออภัยไม่สามารถช่วยได้ :) แต่จริงๆแล้วมันเป็นสิ่งที่ดีที่จะชี้ให้เห็นเพราะมันสำคัญ!
Pryftan

1
@Pryftan +1 สำหรับ +1 สำหรับ +1 ... คุณรู้ ... มันสำคัญมาก
0xc0de

@ 0xc0de เห็นด้วยอย่างแน่นอน! ขอบคุณสำหรับสิ่งนั้น! :)
Pryftan

15

สำหรับระยะสั้นIFS=$'\n'กำหนดขึ้นบรรทัดใหม่ให้กับตัวแปร\nIFS

$'string'สร้างเป็นกลไกการอ้างอิงที่ใช้ในการถอดรหัส ANSI C เช่นลำดับหนี ไวยากรณ์นี้มาจากksh93และเป็นแบบพกพาที่ทันสมัยเช่นเปลือกbash, zsh, ,pdkshbusybox sh

รูปแบบนี้ไม่ได้ถูกกำหนดโดย POSIX แต่ได้รับการยอมรับสำหรับปัญหา SUS 7


-1

ฉันชอบที่จะอธิบาย$IFSผ่านตัวอย่าง:
สมมติว่าคุณต้องการ cp หรือ mv หรือไฟล์อื่นที่กำลังดำเนินการ IFS ว่างเปล่าโดยค่าเริ่มต้นเมื่อไฟล์ของคุณมี meta ถ่านหรือพื้นที่เช่น:
Linux Administration.pdfหรือFree Software Fundation.oggแน่นอนคุณจะมี probelm เพราะ: Linux พิจารณา แยกพารามิเตอร์และ Administartion พิจารณาพารามิเตอร์แยกดังนั้นทุบตีมีbuilt-in variableแล้วคุณสามารถเริ่มต้นIFS==$(echo -en "\n\b")แล้วทุบตีทิ้งเมตาถ่านและช่องว่างระหว่างชื่อไฟล์ตัวอย่างเช่น:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
mymusicdir=~/test/dd
find $mymusicdir -name "*" -execdir rename 's/ /_/g' "{}" +
IFS=$SAVEIFS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.