Bagaimana saya bisa mengonversi tab menjadi spasi di setiap file direktori (mungkin secara rekursif)?
Juga, apakah ada cara mengatur jumlah spasi per tab?
pradalah utilitas luar biasa untuk ini. Lihat jawaban ini .
Bagaimana saya bisa mengonversi tab menjadi spasi di setiap file direktori (mungkin secara rekursif)?
Juga, apakah ada cara mengatur jumlah spasi per tab?
pradalah utilitas luar biasa untuk ini. Lihat jawaban ini .
Jawaban:
Peringatan: Ini akan merusak repo Anda.
Ini akan merusak file biner , termasuk di bawah mereka
svn,.git! Baca komentar sebelum menggunakan!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
File asli disimpan sebagai [filename].orig.
Ganti '* .java' dengan akhiran file dari jenis file yang Anda cari. Dengan cara ini Anda dapat mencegah kerusakan file biner secara tidak sengaja.
Kerugian:
expand.
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;. Tapi saya tidak menyadari perintah memperluas - sangat berguna!
Penggantian sederhana dengan sedtidak apa-apa tapi bukan solusi terbaik. Jika ada ruang "ekstra" di antara tab, mereka akan tetap ada setelah penggantian, sehingga margin akan menjadi acak-acakan. Tab yang diperluas di tengah garis juga tidak akan berfungsi dengan benar. Di bash, kita bisa mengatakan sebaliknya
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
untuk diterapkan expandke setiap file Java di pohon direktori saat ini. Hapus / ganti -nameargumen jika Anda menargetkan beberapa jenis file lainnya. Seperti yang disebutkan dalam salah satu komentar, berhati-hatilah saat mengeluarkan -nameatau menggunakan kartu pengganti yang lemah. Anda dapat dengan mudah menerima repositori dan file tersembunyi lainnya tanpa sengaja. Inilah sebabnya mengapa jawaban asli termasuk ini:
Anda harus selalu membuat salinan cadangan pohon sebelum mencoba sesuatu seperti ini jika terjadi kesalahan.
{}. Sepertinya dia tidak tahu $0kapan -cdigunakan. Kemudian dimo414 berubah dari penggunaan temp saya di direktori konversi menjadi /tmp, yang akan jauh lebih lambat jika /tmpberada pada titik mount yang berbeda. Sayangnya saya tidak memiliki kotak Linux yang tersedia untuk menguji $0proposal Anda . Tapi saya pikir Anda benar.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
spongedari joeyh.name/code/moreutils , Anda dapat menulisfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*', saya baru saja menghancurkan git repo lokal saya
Coba alat baris perintah expand.
expand -i -t 4 input | sponge output
dimana
-i digunakan untuk memperluas hanya tab utama di setiap baris;-t 4 berarti bahwa setiap tab akan dikonversi menjadi 4 spasi spasi (8 secara default).spongeberasal dari moreutilspaket, dan menghindari membersihkan file input .Akhirnya, Anda dapat menggunakan gexpandOSX, setelah menginstal coreutilsdengan Homebrew ( brew install coreutils).
-ike expandhanya mengganti tab terkemuka di setiap baris. Ini membantu menghindari penggantian tab yang mungkin menjadi bagian dari kode.
inputadalah file yang sama outputdengan bash clobbers konten bahkan sebelum memulai expand. Beginilah cara >kerjanya.
Mengumpulkan komentar terbaik dari jawaban Gene , solusi terbaik sejauh ini, adalah dengan menggunakan spongedari moreutils .
sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;
Penjelasan:
./ secara rekursif mencari dari direktori saat ini-inameadalah pencocokan huruf besar-kecil (untuk keduanya *.javadan *.JAVAsuka)type -f hanya menemukan file biasa (tidak ada direktori, binari atau symlink)-exec bash -c menjalankan perintah berikut dalam subkulit untuk setiap nama file, {}expand -t 4 perluas semua TAB menjadi 4 ruangspongemenyerap input standar (dari expand) dan menulis ke file (yang sama) *.CATATAN : * Pengalihan file sederhana ( > "$0") tidak akan berfungsi di sini karena akan menimpa file terlalu cepat .
Keuntungan : Semua izin file asli dipertahankan dan tidak ada tmpfile perantara yang digunakan.
Gunakan garis miring terbalik sed.
Di linux:
Ganti semua tab dengan 1 tanda hubung di tempat, di semua file * .txt:
sed -i $'s/\t/-/g' *.txtGanti semua tab dengan 1 spasi di tempat, di semua file * .txt:
sed -i $'s/\t/ /g' *.txtGanti semua tab dengan 4 spasi di tempat, di semua file * .txt:
sed -i $'s/\t/ /g' *.txtDi mac:
Ganti semua tab dengan 4 spasi di tempat, di semua file * .txt:
sed -i '' $'s/\t/ /g' *.txtsed -i '' $'s/\t/ /g' $(find . -name "*.txt")
Anda dapat menggunakan prperintah yang tersedia secara umum (halaman manual di sini ). Misalnya, untuk mengonversi tab menjadi empat spasi, lakukan ini:
pr -t -e=4 file > file.expanded
-t menekan tajuk-e=numperluas tab ke numspasiUntuk mengonversi semua file di pohon direktori secara rekursif, sambil melewatkan file biner:
#!/bin/bash
num=4
shopt -s globstar nullglob
for f in **/*; do
[[ -f "$f" ]] || continue # skip if not a regular file
! grep -qI "$f" && continue # skip binary files
pr -t -e=$num "$f" > "$f.expanded.$$" && mv "$f.expanded.$$" "$f"
done
Logika untuk melewatkan file biner adalah dari posting ini .
CATATAN:
expandmengingat bahwa keduanya POSIX? Misalnya apakah ada opsi perubahan sebaris? Git safety at: stackoverflow.com/a/52136507/895245
Bagaimana saya bisa mengonversi tab menjadi spasi di setiap file direktori (mungkin secara rekursif)?
Ini biasanya bukan yang Anda inginkan.
Apakah Anda ingin melakukan ini untuk gambar png? File PDF? Direktori .git? Anda
Makefile(yang membutuhkan tab)? Dump SQL 5GB?
Secara teori, Anda bisa meneruskan banyak opsi pengecualian ke findatau apa pun yang Anda gunakan; tetapi ini rapuh, dan akan pecah segera setelah Anda menambahkan file biner lainnya.
Yang Anda inginkan, setidaknya:
expandapakah ini, sed
tidak).Sejauh yang saya tahu, tidak ada utilitas Unix "standar" yang dapat melakukan ini, dan itu tidak mudah dilakukan dengan shell one-liner, jadi diperlukan skrip.
Beberapa waktu yang lalu saya membuat skrip kecil bernama
sanitize_files yang melakukan hal itu. Juga perbaikan beberapa hal umum lainnya seperti mengganti \r\ndengan \n, menambahkan Trailing \n, dll
Anda dapat menemukan skrip yang disederhanakan tanpa fitur tambahan dan argumen baris perintah di bawah ini, tetapi saya sarankan Anda menggunakan skrip di atas karena lebih mungkin untuk menerima perbaikan bug dan pembaruan lainnya daripada posting ini.
Saya juga ingin menunjukkan, dalam menanggapi beberapa jawaban lain di sini, bahwa menggunakan shell globbing bukanlah cara yang kuat untuk melakukan ini, karena cepat atau lambat Anda akan berakhir dengan lebih banyak file daripada yang akan cocok ARG_MAX(pada modern Sistem Linux 128k, yang mungkin tampak banyak, tetapi cepat atau lambat itu tidak
cukup).
#!/usr/bin/env python
#
# http://code.arp242.net/sanitize_files
#
import os, re, sys
def is_binary(data):
return data.find(b'\000') >= 0
def should_ignore(path):
keep = [
# VCS systems
'.git/', '.hg/' '.svn/' 'CVS/',
# These files have significant whitespace/tabs, and cannot be edited
# safely
# TODO: there are probably more of these files..
'Makefile', 'BSDmakefile', 'GNUmakefile', 'Gemfile.lock'
]
for k in keep:
if '/%s' % k in path:
return True
return False
def run(files):
indent_find = b'\t'
indent_replace = b' ' * indent_width
for f in files:
if should_ignore(f):
print('Ignoring %s' % f)
continue
try:
size = os.stat(f).st_size
# Unresolvable symlink, just ignore those
except FileNotFoundError as exc:
print('%s is unresolvable, skipping (%s)' % (f, exc))
continue
if size == 0: continue
if size > 1024 ** 2:
print("Skipping `%s' because it's over 1MiB" % f)
continue
try:
data = open(f, 'rb').read()
except (OSError, PermissionError) as exc:
print("Error: Unable to read `%s': %s" % (f, exc))
continue
if is_binary(data):
print("Skipping `%s' because it looks binary" % f)
continue
data = data.split(b'\n')
fixed_indent = False
for i, line in enumerate(data):
# Fix indentation
repl_count = 0
while line.startswith(indent_find):
fixed_indent = True
repl_count += 1
line = line.replace(indent_find, b'', 1)
if repl_count > 0:
line = indent_replace * repl_count + line
data = list(filter(lambda x: x is not None, data))
try:
open(f, 'wb').write(b'\n'.join(data))
except (OSError, PermissionError) as exc:
print("Error: Unable to write to `%s': %s" % (f, exc))
if __name__ == '__main__':
allfiles = []
for root, dirs, files in os.walk(os.getcwd()):
for f in files:
p = '%s/%s' % (root, f)
if do_add:
allfiles.append(p)
run(allfiles)
Saya suka contoh "temukan" di atas untuk aplikasi rekursif. Untuk mengadaptasinya menjadi non-rekursif, hanya mengubah file di direktori saat ini yang cocok dengan wildcard, ekspansi glob shell bisa mencukupi untuk sejumlah kecil file:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Jika Anda ingin itu diam setelah Anda percaya bahwa itu berfungsi, cukup letakkan -vpada shperintah di akhir.
Tentu saja Anda dapat memilih set file dalam perintah pertama. Misalnya, daftar hanya subdirektori tertentu (atau direktori) dengan cara yang terkontrol seperti ini:
ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
Atau pada gilirannya jalankan temukan (1) dengan beberapa kombinasi parameter kedalaman dll:
find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
ARG_MAXpanjang. Ini 128k pada sistem Linux, tetapi saya telah menemui batas ini cukup kali untuk tidak bergantung pada shell globbing.
finddapat dikatakan -maxdepth 1, dan hanya memproses entri direktori yang sedang dimodifikasi, bukan seluruh pohon.
Saya digunakan astyleuntuk indentasi ulang semua kode C / C ++ saya setelah menemukan tab dan spasi campuran. Ia juga memiliki opsi untuk memaksa gaya penyangga tertentu jika Anda mau.
Satu dapat digunakan vimuntuk itu:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Seperti yang dinyatakan oleh Carpetsmoker, itu akan melakukan retab sesuai dengan vimpengaturan Anda . Dan modelines dalam file, jika ada. Juga, itu akan mengganti tab tidak hanya di awal baris. Yang bukan apa yang biasanya Anda inginkan. Misalnya, Anda mungkin memiliki literal, yang berisi tab.
:retabakan mengubah semua tab dalam file, bukan yang di awal. itu juga tergantung pada apa :tabstopdan :expandtabpengaturan Anda di vimrc atau modeline, jadi ini mungkin tidak berfungsi sama sekali.
tabstopdan expandtab, itu akan berhasil jika Anda menggunakan vim. Kecuali jika Anda memiliki garis mode di file.
Rekomendasi saya adalah menggunakan:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Komentar:
sedadalah editor aliran. Gunakan exuntuk mengedit di tempat. Ini menghindari membuat file temp tambahan dan spawning shells untuk setiap penggantian seperti pada jawaban teratas .find|xargssebagai ganti find -exec. Seperti yang ditunjukkan oleh @ gniourf-gniourf ini menyebabkan masalah dengan spasi, tanda kutip dan karakter kontrol dalam nama file cf. Wheeler .exmungkin tidak tersedia di setiap sistem Unix. Mengganti dengan vi -emungkin bekerja pada lebih banyak mesin. Juga, regex Anda menggantikan sejumlah karakter tab awal dengan dua spasi. Ganti regex dengan +%s/\t/ /gtidak merusak lekukan multi level. Namun ini juga memengaruhi karakter tab yang tidak digunakan untuk indentasi.
/\t/ /varian pada file saya, tetapi memilih untuk /\t\+//tidak merusak tab yang tidak membuat indentasi. Merindukan masalah dengan lekukan multi! Memperbarui Jawaban. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargscara ini tidak berguna, tidak efisien dan rusak (pikirkan nama file yang mengandung spasi atau kutipan). Mengapa Anda tidak menggunakan find's -execberalih bukan?
-print0opsi untuk menemukan / xargs. Saya suka xargs -execsejak: a) Pemisahan masalah b) dapat ditukar dengan GNU paralel lebih mudah.
Untuk mengonversi semua file Java secara rekursif dalam direktori menggunakan 4 spasi alih-alih tab:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Anda dapat menggunakan finddengan tabs-to-spacespaket untuk ini.
Pertama, instal tabs-to-spaces
npm install -g tabs-to-spaces
kemudian, jalankan perintah ini dari direktori root proyek Anda;
find . -name '*' -exec t2s --spaces 2 {} \;
Ini akan mengganti setiap tabkarakter dengan 2 spacesdi setiap file.
Tidak ada badan yang disebutkan rpl? Menggunakan rpl Anda dapat mengganti string apa pun. Untuk mengonversi tab menjadi spasi,
rpl -R -e "\t" " " .
sangat sederhana.
Penggunaan expandseperti yang disarankan dalam jawaban lain tampaknya merupakan pendekatan yang paling logis untuk tugas ini saja.
Yang mengatakan, itu juga dapat dilakukan dengan Bash dan Awk jika Anda mungkin ingin melakukan beberapa modifikasi lain bersamanya.
Jika menggunakan Bash 4.0 atau lebih besar, shopt builtin globstar dapat digunakan untuk mencari secara rekursif **.
Dengan GNU Awk versi 4.1 atau lebih tinggi, modifikasi file seperti "inplace" dapat dibuat:
shopt -s globstar
gawk -i inplace '{gsub("\t"," ")}1' **/*.ext
Jika Anda ingin mengatur jumlah spasi per tab:
gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
Unduh dan jalankan skrip berikut untuk secara rekursif mengonversi tab keras menjadi tab lunak dalam file teks biasa.
Jalankan skrip dari dalam folder yang berisi file teks biasa.
#!/bin/bash
find . -type f -and -not -path './.git/*' -exec grep -Iq . {} \; -and -print | while read -r file; do {
echo "Converting... "$file"";
data=$(expand --initial -t 4 "$file");
rm "$file";
echo "$data" > "$file";
}; done;
Metode ramah repositori
git-tab-to-space() (
d="$(mktemp -d)"
git grep --cached -Il '' | grep -E "${1:-.}" | \
xargs -I'{}' bash -c '\
f="${1}/f" \
&& expand -t 4 "$0" > "$f" && \
chmod --reference="$0" "$f" && \
mv "$f" "$0"' \
'{}' "$d" \
;
rmdir "$d"
)
Bertindak pada semua file di bawah direktori saat ini:
git-tab-to-space
Bertindak hanya pada file C atau C ++:
git-tab-to-space '\.(c|h)(|pp)$'
Anda mungkin menginginkan ini terutama karena Makefile yang mengganggu yang memerlukan tab.
Perintah git grep --cached -Il '' :
.gitseperti yang dijelaskan di: Bagaimana cara mendaftar semua file teks (non-biner) dalam repositori git?
chmod --referencemembuat izin file tidak berubah: /unix/20645/clone-ownership-and-permissions-from-another-file Sayangnya saya tidak dapat menemukan alternatif POSIX yang ringkas .
Jika basis kode Anda memiliki ide gila untuk mengizinkan tab mentah fungsional dalam string, gunakan:
expand -i
dan kemudian bersenang-senang memeriksa semua tab awal yang tidak dimulai satu per satu, yang dapat Anda daftarkan: Apakah mungkin untuk mendapatkan grep untuk tab?
Diuji pada Ubuntu 18.04.
Mengonversi tab menjadi spasi hanya dalam file ".lua" [tab -> 2 spasi]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output)
expand -t 4akan memperluas tab a\tbke 3 spasi dan tab aa\tbke 2 spasi, seperti yang seharusnya. expandmempertimbangkan konteks tab, sedtidak dan akan mengganti tab dengan jumlah spasi yang Anda tentukan, terlepas dari konteksnya.
Gunakan vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar( **) untuk rekursi, aktifkan oleh shopt -s globstar.**/*.c.Untuk memodifikasi tabstop, tambahkan +'set ts=2'.
Namun sisi buruknya adalah ia dapat mengganti tab di dalam string .
Jadi untuk solusi yang sedikit lebih baik (dengan menggunakan substitusi), coba:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
Atau dengan menggunakan exeditor + expandutility:
$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*
Untuk spasi tambahan, lihat: Bagaimana menghapus spasi spasi tambahan untuk banyak file?
Anda dapat menambahkan fungsi berikut ke .bash_profile:
# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
ex +'set ts=2' +'bufdo retab' -cxa $*
}
:retabmungkin tidak bekerja sama sekali , shell globbing adalah solusi yang buruk untuk hal semacam ini , Anda :sperintah akan menggantikan setiap jumlah tab dengan 2 spasi (yang Anda hampir tidak pernah mau), memulai ex hanya untuk menjalankan suatu :!expandproses itu konyol ...