ติดตั้งแบบแทนที่ธงซึ่งทำงานได้ทั้งบน Mac (BSD) และ Linux


237

มีการร้องขอsedการแก้ไขสิ่งที่ต้องทำแบบไม่มีการสำรองข้อมูลที่ทำงานทั้งบน Linux และ Mac หรือไม่? ในขณะที่ BSD sedจัดส่งมาพร้อมกับ OS X ดูเหมือนว่าต้องการsed -i '' …แต่การแจกจ่าย GNU sedLinux มักจะมาพร้อมกับการตีความคำพูดเป็นชื่อไฟล์อินพุตว่าง (แทนนามสกุลการสำรองข้อมูล) และต้องการsed -i …แทน

มีไวยากรณ์บรรทัดคำสั่งใดบ้างที่ใช้ได้กับทั้งสองรสชาติดังนั้นฉันจึงสามารถใช้สคริปต์เดียวกันกับทั้งสองระบบได้หรือไม่


Perl ไม่ใช่ตัวเลือก? คุณต้องใช้ sed หรือไม่
Noufal Ibrahim

อาจติดตั้งรุ่น GNU และใช้งาน! topbug.net/blog/2013/04/14/… ? ฉันจะลองก่อน จากนั้นอีกครั้งประเภทของการดูดที่เกินไป
Dmitry Minkovsky

2
@dimadima: อาจจะน่าสนใจสำหรับคนอื่น ๆ ที่กำลังดูคำถามนี้ที่มีสคริปต์ส่วนบุคคลที่แตกบนเครื่อง OS X ของพวกเขา ในกรณีของฉันฉันต้องการมันสำหรับระบบ build ของโครงการโอเพ่นซอร์สซึ่งการบอกให้ผู้ใช้ของคุณติดตั้ง GNU sed ก่อนจะต้องเอาชนะจุดประสงค์ดั้งเดิมของแบบฝึกหัดนี้ (แก้ไขไฟล์ไม่กี่ไฟล์ในแฟชั่น .
dnadlinger

@klickverbot ใช่ทำให้รู้สึก ฉันเพิ่มความคิดเห็นเป็นคำตอบก่อนจากนั้นจึงลบออกโดยตระหนักว่าไม่ใช่คำตอบสำหรับคำถามของคุณ :)
Dmitry Minkovsky

คำตอบ:


212

หากคุณต้องการใช้sed -i'ง่าย' จริงๆสิ่งต่อไปนี้สามารถใช้ได้ทั้ง GNU และ BSD / Mac sed:

sed -i.bak 's/foo/bar/' filename

สังเกตการขาดพื้นที่และจุด

พิสูจน์:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

เห็นได้ชัดว่าคุณสามารถลบ.bakไฟล์ได้


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

สิ่งนี้ใช้กับ mac ได้ แต่ใน Ubuntu 16.04 มันเขียนทับไฟล์ต้นฉบับไปที่ 0 ไบต์และสร้างไฟล์. bak ด้วย 0 ไบต์ด้วยหรือไม่
house9

1
โน้ตบน MacOS เซีย (และอาจจะก่อนหน้านี้ผมไม่ได้มีเครื่องที่มีประโยชน์) sedไม่ยอมรับตำแหน่งcommandโต้เถียงตอนที่เธอเรียกด้วย-i- มันต้อง-eมาพร้อมกับธงอื่น ดังนั้นคำสั่งจะกลายเป็น:sed -i.bak -e 's/foo/bar/' filename
ELLIOTTCABLE

5
สิ่งนี้จะสร้างการสำรองข้อมูลในขณะที่ OP ขอให้แก้ไขโดยเฉพาะ
Marc-André Lafortune

8
ดังนั้นวิธีการแก้ปัญหาอย่างสมบูรณ์คือ:sed -i.bak 's/foo/bar/' file && rm file.bak
joeytwiddle

112

ใช้งานได้กับ GNU sed แต่ไม่ทำงานบน OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

ใช้งานได้กับ OS X แต่ไม่ทำงานกับ GNU sed:

sed -i '' -e 's/foo/bar/' target.file

บน OS X คุณ

  • ไม่สามารถใช้งานได้sed -i -eเนื่องจากส่วนขยายของไฟล์สำรองจะถูกตั้งค่าเป็น-e
  • ไม่สามารถใช้sed -i'' -eสำหรับเดียวกันเหตุผลที่มันต้องการพื้นที่ระหว่างและ-i''

1
@AlexanderMills บอกได้เลยว่าอาร์กิวเมนต์ต่อไปคือคำสั่ง - สามารถข้ามไปได้ที่นี่
Benjamin W.

4
โปรดสังเกตว่า-iและ-i''เหมือนกันในเวลาการแยกวิเคราะห์เชลล์ sed ไม่สามารถทำงานได้แตกต่างกันในการเรียกใช้ครั้งแรกทั้งสองเนื่องจากได้รับอาร์กิวเมนต์เดียวกัน
wchargin

44

เมื่ออยู่บน OSX ฉันมักจะติดตั้ง GNU sed version ผ่าน Homebrew เพื่อหลีกเลี่ยงปัญหาในสคริปต์เนื่องจากสคริปต์ส่วนใหญ่ถูกเขียนขึ้นสำหรับ GNU sed version

brew install gnu-sed --with-default-names

จากนั้น BSD ของคุณจะถูกแทนที่ด้วย GNU sed

อีกวิธีหนึ่งคุณสามารถติดตั้งโดยไม่มีชื่อเริ่มต้นได้ แต่:

  • เปลี่ยนของคุณPATHตามคำแนะนำหลังจากการติดตั้งgnu-sed
  • ตรวจสอบสคริปต์ของคุณเพื่อเลือกระหว่างgsedหรือsedขึ้นอยู่กับระบบของคุณ

4
--with-default-namesถูกลบออกจาก homebrew-core , ข้อมูลมากขึ้นในการนี้คำตอบ เมื่อติดตั้งgnu-sedตอนนี้คำแนะนำในการติดตั้งระบุว่าคุณจำเป็นต้องเพิ่มลงgnubinในPATH:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
pgericson

18

อย่างที่Noufal Ibrahimถามทำไมคุณไม่สามารถใช้ Perl ได้? Mac เครื่องใดก็ได้จะมี Perl และมีลีนุกซ์หรือ BSD จำนวนน้อยมากที่ไม่ได้รวม Perl บางเวอร์ชั่นไว้ในระบบพื้นฐาน หนึ่งในสภาพแวดล้อมเดียวที่จริง ๆ แล้วอาจไม่มี Perl ก็คือ BusyBox (ซึ่งใช้งานได้เหมือน GNU / Linux -iยกเว้นว่าจะไม่มีการระบุนามสกุลสำรอง)

ตามที่ismailแนะนำ

เนื่องจาก Perl มีให้บริการทุกที่ที่ฉันทำ perl -pi -e s,foo,bar,g target.file

และนี่ดูเหมือนจะเป็นทางออกที่ดีกว่าในเกือบทุกกรณีกว่าสคริปต์นามแฝงหรือวิธีแก้ไขปัญหาอื่น ๆ เพื่อจัดการกับความไม่ลงรอยกันพื้นฐานsed -iระหว่าง GNU / Linux และ BSD / Mac


ฉันรักทางออกนี้ perlเป็นส่วนหนึ่งของข้อมูลจำเพาะ Linux Standard Base ตั้งแต่วันที่ 1 พฤษภาคม 2009 refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Language/ …
OregonTrail

1
นี่เป็นทางออกที่ดีที่สุดสำหรับการพกพาได้อย่างง่ายดาย
ecnepsnai

17

ไม่มีทางที่จะให้มันทำงานได้

วิธีหนึ่งคือการใช้ไฟล์ชั่วคราวเช่น:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

ใช้ได้ทั้งสองอย่าง


มีเหตุผลหรือไม่ที่ SOME_FILE = "$ (sed -s" s / abc / def / "some / file)"; echo "$ SOME_FILE"> บางส่วน / ไฟล์; จะไม่ทำงานแทน
Jason Gross

ดูเหมือนว่าคุณจะถูก จำกัด ด้วยขนาดสูงสุดของตัวแปร bash ใช่ไหม? ไม่แน่ใจว่ามันจะทำงานกับไฟล์ GB ได้หรือไม่
อะนาล็อก

3
หากคุณกำลังสร้างไฟล์ชั่วคราวทำไมไม่ให้ส่วนขยายเพื่อสร้างไฟล์สำรอง (ซึ่งคุณสามารถลบได้) ตามที่ @kine แนะนำด้านล่าง
Alex Dupuy

13

คำตอบ:ไม่

คำตอบที่ได้รับการยอมรับเดิมจริง ๆ แล้วไม่ได้ทำสิ่งที่ร้องขอ (ดังที่ระบุไว้ในความคิดเห็น) (ฉันพบคำตอบนี้เมื่อค้นหาสาเหตุที่file-eปรากฏ "สุ่ม" ในไดเรกทอรีของฉัน)

เห็นได้ชัดว่าไม่มีทางที่sed -iจะทำงานอย่างต่อเนื่องทั้งบน MacOS และ Linuces

คำแนะนำของฉันสำหรับสิ่งที่คุ้มค่าไม่ใช่เพื่ออัปเดตด้วยsed(ซึ่งมีโหมดความล้มเหลวที่ซับซ้อน) แต่เพื่อสร้างไฟล์ใหม่และเปลี่ยนชื่อพวกเขาในภายหลัง ในคำอื่น ๆ : -iหลีกเลี่ยง


9

-iตัวเลือกที่ไม่ได้เป็นส่วนหนึ่งของPOSIX Sed วิธีการพกพาที่มากกว่านั้นคือใช้ Vim ในโหมด Ex

ex -sc '%s/alfa/bravo/|x' file
  1. % เลือกทุกบรรทัด

  2. s แทนที่

  3. x บันทึกและปิด


นี่คือคำตอบที่ถูกต้อง คุณสมบัติที่สำคัญของ POSIX คือการพกพา
Kajukenbo

9

นี่เป็นอีกเวอร์ชั่นที่ใช้งานได้บน Linux และ macOS โดยไม่ต้องใช้evalและไม่ต้องลบไฟล์สำรอง มันใช้อาร์เรย์ Bash สำหรับการจัดเก็บsedพารามิเตอร์ซึ่งสะอาดกว่าการใช้eval:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

สิ่งนี้ไม่ได้สร้างไฟล์สำรองข้อมูลไม่ใช่ไฟล์ที่มีเครื่องหมายคำพูดต่อท้าย


2
นี่คือคำตอบที่ถูกต้อง ฉันจะทำให้มันง่ายขึ้นเล็กน้อย แต่ความคิดนั้นทำงานได้อย่างสมบูรณ์ sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
Alex Skrypnyk

สิ่งที่ดี! ฉันชอบเวอร์ชันที่ยาวกว่าเพื่อให้อ่านได้ง่ายขึ้น
nwinkler

1
นี่เป็นวิธีที่ดีที่สุดสำหรับฉันที่จะเข้ากันได้กับระบบที่แตกต่างกัน
Victor Choy

6

คำตอบของ Steve Powellนั้นค่อนข้างถูกต้องให้คำปรึกษาหน้า MAN สำหรับ sed บน OSX และ Linux (Ubuntu 12.04) เน้นถึงความเข้ากันได้ภายในการใช้ sed ใน 'in-place' ในทั้งสองระบบปฏิบัติการ

JFYI ไม่ควรมีช่องว่างระหว่าง -i และเครื่องหมายอัญประกาศใด ๆ (ซึ่งแสดงถึงนามสกุลไฟล์ว่างเปล่า) โดยใช้รุ่น Linux ของ sed ดังนั้น

sed Man หน้าลินุกซ์

#Linux
sed -i"" 

และ

sed หน้า OSX Man

#OSX (notice the space after the '-i' argument)
sed -i "" 

ฉันได้รอบนี้ในสคริปต์โดยใช้คำสั่ง alias'd และเอาท์พุทชื่อ OS ของ ' uname ' ภายในทุบตี 'ถ้า' การพยายามจัดเก็บสตริงคำสั่งที่ขึ้นอยู่กับระบบปฏิบัติการในตัวแปรนั้นมีการเข้าชมและพลาดเมื่อตีความคำพูด จำเป็นต้องใช้ ' shopt -s expand_aliases ' เพื่อขยาย / ใช้นามแฝงที่กำหนดไว้ในสคริปต์ของคุณ การใช้งาน shopt จะจัดการกับที่นี่


1

หากคุณจำเป็นต้องทำsedแทนbashสคริปต์และคุณไม่ต้องการให้แทนที่ไฟล์. bkp และคุณมีวิธีการตรวจสอบระบบปฏิบัติการ (พูดโดยใช้ostype.sh ) - แฮ็คต่อไปนี้ที่มีbashเชลล์ในตัวevalควรทำงาน:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`

0

คุณสามารถใช้ฟองน้ำ Sponge เป็นโปรแกรม unix แบบเก่าที่พบในแพ็คเกจ moreutils (ทั้งใน Ubuntu และ debian และใน homebrew ใน mac)

มันจะบัฟเฟอร์เนื้อหาทั้งหมดจากไปป์รอจนกว่าจะปิดไปป์ (อาจหมายถึงว่าไฟล์อินพุตปิดแล้ว) แล้วเขียนทับ:

จากหน้าคน :

สรุป

ไฟล์ '... ' grep '... ' | ไฟล์ฟองน้ำ


3
วิธีการที่ดี - น่าเสียดายที่ไม่ได้ช่วยจริง ๆ ในสถานการณ์ดั้งเดิมเนื่องจากเหตุผลในการหันไปใช้สิ่งที่มีอยู่เพื่อใช้ในสคริปต์ผู้ช่วยเหลือข้ามแพลตฟอร์มที่ใช้กับเครื่องไคลเอนต์ซึ่งคุณไม่สามารถพึ่งพาสิ่งอื่นได้นอกจากระบบ เครื่องมือที่กำลังติดตั้ง แม้ว่าจะมีประโยชน์กับคนอื่นก็ตาม
dnadlinger

0

งานต่อไปนี้สำหรับฉันบน Linux และ OS X:

sed -i' ' <expr> <file>

เช่นสำหรับไฟล์fที่มีaaabbaaba

sed -i' ' 's/b/c/g' f

ให้ผลตอบแทนaaaccaacaทั้ง Linux และ Mac หมายเหตุมีสตริงที่ยกมามีช่องว่างโดยไม่มีช่องว่างระหว่าง-iและสตริง คำพูดเดียวหรือสองครั้งทั้งสองทำงาน

บน Linux ฉันใช้bashเวอร์ชัน 4.3.11 ภายใต้ Ubuntu 14.04.4 และบน Mac เวอร์ชัน 3.2.57 ภายใต้ OS X 10.11.4 El Capitan (Darwin 15.4.0)


1
ว้าวฉันดีใจที่ฉันไม่ได้ฟังทุกคนข้างต้นบอกว่ามันเป็นไปไม่ได้และอ่านต่อไป! มันใช้งานได้จริง ขอบคุณ!
คนงาน

4
ใช้งานไม่ได้กับฉันใน Mac OS ... ไฟล์ใหม่ที่มีช่องว่างต่อท้ายชื่อไฟล์ถูกสร้างขึ้น
Frédéric

ใช้งานไม่ได้กับฉันบน Mac (/ usr / bin / sed) - ตอนนี้ฉันมีไฟล์สำรองพิเศษที่มีช่องว่างต่อท้ายชื่อไฟล์ :-(
paul_h

ลองดึงพื้นที่ออกจากคำพูดเดียวมันใช้งานได้สำหรับฉัน
dps

0

ฉันพบปัญหานี้ วิธีแก้ปัญหาอย่างรวดเร็วเพียงอย่างเดียวคือการแทนที่ sed ใน mac เป็นรุ่น gnu:

brew install gnu-sed

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