ฉันจะแปลงช่องว่างเป็นแท็บใน Vim หรือ Linux ได้อย่างไร


192

ฉันได้ดูคำถามหลายข้อเกี่ยวกับ Stack Overflow สำหรับวิธีการแปลงช่องว่างเป็นแท็บโดยไม่ต้องค้นหาสิ่งที่ฉันต้องการ ดูเหมือนจะมีคำถามเพิ่มเติมเกี่ยวกับวิธีแปลงแท็บเป็นช่องว่าง แต่ฉันพยายามทำตรงกันข้าม

ในVimฉันได้ลอง:retabและ:retab!ไม่มีโชค แต่ฉันเชื่อว่าสิ่งเหล่านี้จริง ๆ แล้วจะเปลี่ยนจากแท็บไปยังช่องว่างอย่างไรก็ตาม

ฉันลองทั้งสองexpandและunexpandที่พรอมต์คำสั่งโดยไม่มีโชค

นี่คือไฟล์ที่มีปัญหา:

http://gdata-python-client.googlecode.com/hg-history/a9ed9edefd61a0ba0e18c43e448472051821003a/samples/docs/docs_v3_example.py

ฉันจะแปลงช่องว่างนำไปสู่แท็บที่ใช้อย่างใดอย่างหนึ่งVimหรือเปลือก?


ในความคิดเห็นของ @ Matt ที่ถูกลบตอนนี้ตัวอย่างแรก ( sed "s/ +/`echo -e '\t'`/g" < input.py > output.py) จะปรากฏขึ้นเพื่อแปลงช่องว่างทั้งหมดไม่ใช่เฉพาะช่องว่างนำหน้า ในตัวอย่างที่สอง ( sed "s/^ +/`echo -e '\t'`/g" < input.py > output.py) มันจะแทนที่ช่องว่างแรกในแต่ละบรรทัดด้วยแท็บและปล่อยให้ส่วนที่เหลือของพวกเขา
cwd

ฝั่งตรงข้ามที่เกี่ยวข้อง: วิธีแทนที่แท็บด้วยช่องว่างได้อย่างไร ที่ Vim SE
kenorb

ฉันไม่พบคำตอบสำหรับไฟล์ทั้งหมด / จำนวนมากดังนั้นฉันได้เขียนอย่างใดอย่างหนึ่งของตัวเอง: stackoverflow.com/a/44581474/1115187 ด้วยfind, awkและกระบอง (นานเกินไปที่จะปล่อยให้มันเป็นในความคิดเห็นแม้ว่า)
maxkoryukov

คำตอบ:


316

ใช้ Vim เพื่อขยายช่องว่างนำหน้า (กว้างกว่า'tabstop') คุณมีสิทธิ์ใช้งานretabแต่ก่อนอื่นต้องแน่ใจว่า'expandtab'ได้รีเซ็ต ( :verbose set ts? et?เป็นเพื่อนของคุณ) retabใช้ช่วงดังนั้นฉันมักจะระบุ%ว่าหมายถึง "ไฟล์ทั้งหมด"

:set tabstop=2      " To match the sample file
:set noexpandtab    " Use tabs, not spaces
:%retab!            " Retabulate the whole file

ก่อนที่จะทำอะไรเช่นนี้ (โดยเฉพาะกับไฟล์ Python!) ฉันมักจะตั้งค่าไว้'list'เพื่อที่ฉันจะได้เห็นช่องว่างและการเปลี่ยนแปลง

ฉันมีแผนที่ในต่อไปนี้สำหรับฉัน.vimrcนี้:

nnoremap    <F2> :<C-U>setlocal lcs=tab:>-,trail:-,eol:$ list! list? <CR>

2
นี่เป็นวิธีที่ฉันได้ไปทำงานไม่แน่ใจว่าเป็นสิ่งที่จำเป็นและสิ่งที่ไม่ได้และครับผมไม่ทราบว่าสิ่งที่ "%" ก่อนที่จะทำ reatab: :set noet, :set tabstop=2, :retab!, :%retab!, :set tabstop=1, :retab!,:%retab!
CWD

2
%ผมได้ปรับปรุงคำตอบของฉันมีคำอธิบายว่าทำไมผมใช้ การทำแผนที่ F2 เป็นสิ่งที่มันพิมพ์
Johnsyweb

7
สำหรับไฟล์นั้นฉันจะทำ::set noet ts=2 |%retab!
Johnsyweb

1
@ Johnsyweb เป็นธรรมฉันผิด: :%retab!ยังคงทำงานได้ ฉันสับสนกับ==ฯลฯ ซึ่งไม่เคารพการตั้งค่าอนุรักษ์
Unk

1
ในการแปลงไฟล์ coffeescript จากช่องว่างเป็นแท็บฉันได้ปฏิบัติตามคำตอบของ @ Johnsyweb (รวมถึงการเพิ่มไฟล์. vimrc) แต่แทนที่ด้วย:set tabstop=4
Mikeumus

38

1 - หากคุณมีช่องว่างและต้องการแท็บ

ก่อนอื่นคุณต้องตัดสินใจว่าจะมีช่องว่างจำนวนเท่าใดในแท็บเดียว ที่กล่าวว่าสมมติว่าคุณมีเส้นที่มีช่องว่างนำหน้า 4 หรือ 8 ... กว่าที่คุณรู้ว่าคุณอาจต้องการแท็บเป็นช่องว่าง 4 ขณะนี้มีข้อมูลที่คุณทำ:

:set ts=4
:set noet
:%retab!

มีปัญหาอยู่ที่นี่! ลำดับของคำสั่งนี้จะค้นหาข้อความทั้งหมดของคุณไม่เพียงเว้นวรรคในตอนต้นของบรรทัด นั่นหมายความว่าสตริงเช่น: "Hey,␣this␣␣␣␣is␣4␣spaces"จะกลายเป็น"Hey,␣this⇥is␣4␣spaces"แต่ไม่ใช่! แท็บ!

ที่จะชำระปัญหาเล็ก ๆ น้อย ๆ นี้ฉันขอแนะนำแทนsearchretab

:%s/^\(^I*\)␣␣␣␣/\1^I/g

การค้นหานี้จะค้นหาไฟล์ทั้งหมดสำหรับบรรทัดใด ๆ ที่ขึ้นต้นด้วยจำนวนแท็บใดก็ตามตามด้วยช่องว่าง 4 อันและแทนที่ด้วยแท็บใดก็ได้ที่พบจำนวนหนึ่งบวกหนึ่งแท็บ

น่าเสียดายที่นี่จะไม่ทำงานทันที!

ตอนแรกไฟล์จะมีบรรทัดที่ขึ้นต้นด้วยช่องว่าง การค้นหาจะแปลงเฉพาะ 4 ช่องว่างแรกไปเป็นแท็บและปล่อยต่อไปนี้ ...

คุณต้องทำซ้ำคำสั่ง กี่ครั้ง? pattern not foundจนกว่าคุณจะได้รับ ฉันไม่สามารถคิดวิธีที่จะทำให้กระบวนการอัตโนมัติ แต่ถ้าคุณทำ:

`10@:`

คุณทำเสร็จแล้ว คำสั่งนี้ทำการค้นหา / แทนที่ครั้งสุดท้ายซ้ำ 10 ครั้ง ไม่น่าเป็นไปได้ที่โปรแกรมของคุณจะมีการเยื้องจำนวนมาก @@หากมีเพียงแค่ทำซ้ำอีกครั้ง

ตอนนี้เพียงเพื่อตอบคำถาม ฉันรู้ว่าคุณถามถึงสิ่งที่ตรงกันข้าม แต่คุณไม่มีทางรู้ว่าจะต้องเลิกทำเมื่อไหร่

2 - คุณมีแท็บและต้องการเว้นวรรค

ก่อนอื่นให้ตัดสินใจว่าคุณต้องการให้มีการแปลงแท็บจำนวนเท่าใด สมมติว่าคุณต้องการให้แต่ละแท็บเป็น 2 ช่องว่าง จากนั้นคุณ:

:set ts=2
:set et
:%retab!

นี่จะมีปัญหาเดียวกันกับสตริง แต่เนื่องจากรูปแบบการเขียนโปรแกรมที่ดีกว่าที่จะไม่ใช้แท็บฮาร์ดภายในสตริงคุณจึงกำลังทำสิ่งที่ดีที่นี่ \tหากคุณต้องการแท็บจริงๆภายในสตริงใช้


ดูเหมือนว่าสภาพการแข่งขันที่หายากมาก อย่างไรก็ตามสร้างฟังก์ชั่นที่ยอมรับการเลือกช่วงภาพและฟังก์ชั่นการค้นหาซ้ำจนกว่าจะไม่พบที่ตรงกันฉันเดาว่าจะเป็นคำตอบที่ฉลาดและมีประโยชน์มากขึ้น
albfan

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

โปรดอธิบายว่าทำไม / g จึงไม่เพียงพอที่จะแปลงกลุ่มทั้งหมดของ 4 ในแต่ละช่องว่างต่อเนื่องกันเป็นแท็บแทนที่จะต้องรันการค้นหาหลายครั้ง
Bjartur Thorlacius

สวัสดี @BjarturThorlacius ปัญหาคือถ้าคุณลบ^สัญลักษณ์เพื่อเริ่มการค้นหาจากจุดเริ่มต้นของบรรทัดคุณเสี่ยงต่อการเปลี่ยนช่องว่างภายในสตริง เมื่อ^คุณรับประกันว่าคุณจะเปลี่ยนเฉพาะที่ว่างและแท็บตั้งแต่เริ่มต้นบรรทัดดังนั้นการเยื้อง นอกจากนั้นหากคุณมั่นใจว่าจะทำทุกอย่างพร้อมกันให้ลบ^และเรียกใช้เพียงครั้งเดียวด้วย::%s/\(^I*\)␣␣␣␣/\1^I/g
ดร.

14
:%s/\(^\s*\)\@<=    /\t/g

การแปล: ค้นหาทุกอินสแตนซ์ของช่องว่างที่ต่อเนื่องกัน 4 ตัว (หลังอักขระ =) แต่เฉพาะในกรณีที่บรรทัดทั้งหมดจนถึงจุดนั้นเป็นช่องว่าง (สิ่งนี้ใช้การยืนยันที่มีความกว้างเป็นศูนย์ด้านหลัง\@<=) แทนที่แต่ละอินสแตนซ์ที่พบด้วยอักขระแท็บ


1
ทางออกเดียวที่ทำงานเพื่อขยายพื้นที่ 1ไปยัง1 แท็บแต่ระวัง Unicode ในคำตอบฉันใช้:%s/\(^\s*\)\@<= /\t/g- ใส่จำนวนช่องว่างที่เหมาะสม (เพื่อแปลง 4 ช่องว่างเป็น 1 แท็บใส่ใน 4 ช่องว่าง) หลังจาก<=
Orwellophile

5

เปลี่ยนช่องว่างเป็นแท็บทั้งหมด:% s / \ s / \ t / g


นี่คือสิ่งที่ฉันต้องการ ขอบคุณ การเปลี่ยนแปลงเดียวที่ฉันต้องทำคือแทนที่ทุก 4 ช่องว่างด้วยแท็บแทนที่จะทำทุกช่องว่าง
บุคคลนิรนาม

3

Linux: ด้วยunexpand(และexpand)

นี่เป็นทางออกที่ดีมาก: https://stackoverflow.com/a/11094620/1115187ซึ่งส่วนใหญ่เป็นเพราะใช้ * nix-utilities:

  1. unexpand - ช่องว่าง -> แท็บ
  2. ขยาย - แท็บ -> ช่องว่าง

Linux: สคริปต์ที่กำหนดเอง

คำตอบเดิมของฉัน

Bash ส่วนย่อยเพื่อแทนที่การเยื้อง4 -space (มีสองแบบ{4}ในสคริปต์) ด้วยแท็บใน.pyไฟล์ทั้งหมดใน./appโฟลเดอร์ (เรียกซ้ำ):

find ./app -iname '*.py' -type f \
    -exec awk -i inplace \
    '{ match($0, /^(( {4})*)(.*?)$/, arr); gsub(/ {4}/, "\t", arr[1]) }; { print arr[1] arr[3] }' {} \; 

มันไม่ได้ปรับเปลี่ยนช่องว่าง 4 ช่องตรงกลางหรือท้ายที่สุด

ได้รับการทดสอบภายใต้ Ubuntu 16.0x และ Linux Mint 18


0

ในกรณีของฉันฉันมีช่องว่างหลายแห่ง (เขตข้อมูลถูกคั่นด้วยช่องว่างอย่างน้อยหนึ่งช่อง) ที่ฉันต้องการแทนที่ด้วยแท็บ ต่อไปนี้มัน:

:% s/\s\+/\t/g

0

ถ้าคุณได้ติดตั้ง coreutils GNU พิจารณา%!unexpand --first-onlyหรือแท็บ 4 พื้นที่พิจารณา%!unexpand -t 4 --first-only( --first-onlyปัจจุบันคือในกรณีที่คุณบังเอิญถูกกล่าวอ้างunexpandด้วย--all)

โปรดทราบว่านี่จะแทนที่ช่องว่างก่อนหน้าแท็บที่กำหนดจะหยุดไม่ใช่ช่องว่างที่ตามมา คุณจะไม่เห็นความแตกต่างในภาพเป็นกลุ่มเว้นแต่คุณจะแสดงแท็บอย่างแท้จริงยิ่งขึ้น ตัวอย่างเช่นฉัน~/.vimrcมีset list listchars=tab:▸┈(ฉันสงสัยว่านี่คือเหตุผลที่คุณคิดว่าunexpandไม่ทำงาน)


0

หากต้องการใช้ Vim เพื่อติดตั้งชุดไฟล์ใหม่ (เช่นไฟล์ * .ts ทั้งหมดในลำดับชั้นไดเรกทอรี) จากพื้นที่ 2 ถึง 4 ช่องคุณสามารถลองใช้สิ่งนี้ได้จากบรรทัดคำสั่ง:

find . -name '*.ts' -print0 | xargs -0 -n1 vim -e '+set ts=2 noet | retab! | set ts=4 et | retab | wq'

สิ่งนี้กำลังทำคือการใช้findเพื่อส่งไฟล์ที่ตรงกันทั้งหมดไปที่xargs(ตัวเลือก -print0 ในการค้นหาทำงานได้กับตัวเลือก -0 เพื่อ xargs เพื่อจัดการไฟล์ w / ช่องว่างในชื่อ)

xargs รัน vim ในโหมด ex ( -e) ในแต่ละไฟล์ที่เรียกใช้คำสั่ง ex ที่กำหนดซึ่งจริงๆแล้วมีหลายคำสั่งเพื่อเปลี่ยนช่องว่างนำหน้าไปเป็นแท็บรีเซ็ตการหยุดแท็บและเปลี่ยนแท็บกลับเป็นช่องว่างและสุดท้ายเป็นการบันทึกและออก

การรันในโหมด ex ป้องกันสิ่งนี้: Vim: Warning: Input is not from a terminalสำหรับแต่ละไฟล์


-1

สคริปต์ Python แบบง่าย:

import os

SOURCE_ROOT = "ROOT DIRECTORY - WILL CONVERT ALL UNDERNEATH"

for root, dirs, files in os.walk(SOURCE_ROOT):
    for f in files:
        fpath = os.path.join(root,f)
        assert os.path.exists(fpath)
        data = open(fpath, "r").read()
        data = data.replace("    ", "\t")
        outfile = open(fpath, "w")
        outfile.write(data)
        outfile.close()

หนึ่งในปัญหาใหญ่: snippet นี้แทนทั้งหมด 4 ช่องว่างเยื้องไม่เพียง แต่ (ที่จุดเริ่มต้นของบรรทัด)
maxkoryukov

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